aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
authorteravus2013-10-07 23:19:50 -0500
committerteravus2013-10-07 23:19:50 -0500
commit75fdd6054d6605877acb511b1bd794de963a15f3 (patch)
tree4ef82542f1ad743f30ab963572805b12f185260a /OpenSim/Framework
parent* Added a Basic DOS protection container/base object for the most common HTTP... (diff)
downloadopensim-SC-75fdd6054d6605877acb511b1bd794de963a15f3.zip
opensim-SC-75fdd6054d6605877acb511b1bd794de963a15f3.tar.gz
opensim-SC-75fdd6054d6605877acb511b1bd794de963a15f3.tar.bz2
opensim-SC-75fdd6054d6605877acb511b1bd794de963a15f3.tar.xz
* Refactor
* Break out common BasicDOSProtector code into separate class.
Diffstat (limited to 'OpenSim/Framework')
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs144
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BasicDOSProtector.cs181
-rw-r--r--OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs143
-rw-r--r--OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs155
4 files changed, 213 insertions, 410 deletions
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
index 8fc9a8a..9b8b8c2 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
@@ -25,10 +25,7 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27using OpenSim.Framework; 27using OpenSim.Framework;
28using System.Collections.Generic;
29using System.IO; 28using System.IO;
30using System.Reflection;
31using log4net;
32 29
33namespace OpenSim.Framework.Servers.HttpServer 30namespace OpenSim.Framework.Servers.HttpServer
34{ 31{
@@ -40,61 +37,17 @@ namespace OpenSim.Framework.Servers.HttpServer
40 /// </remarks> 37 /// </remarks>
41 public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler 38 public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler
42 { 39 {
43 private readonly CircularBuffer<int> _generalRequestTimes; 40
44 private readonly BasicDosProtectorOptions _options; 41 private readonly BasicDosProtectorOptions _options;
45 private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection; 42 private readonly BasicDOSProtector _dosProtector;
46 private readonly Dictionary<string, int> _tempBlocked;
47 private readonly System.Timers.Timer _forgetTimer;
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49 private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim();
50 43
51 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {} 44 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {}
52 45
53 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options) 46 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options)
54 : base(httpMethod, path, name, description) 47 : base(httpMethod, path, name, description)
55 { 48 {
56 _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1, true);
57 _generalRequestTimes.Put(0);
58 _options = options; 49 _options = options;
59 _deeperInspection = new Dictionary<string, CircularBuffer<int>>(); 50 _dosProtector = new BasicDOSProtector(_options);
60 _tempBlocked = new Dictionary<string, int>();
61 _forgetTimer = new System.Timers.Timer();
62 _forgetTimer.Elapsed += delegate
63 {
64 _forgetTimer.Enabled = false;
65
66 List<string> removes = new List<string>();
67 _lockSlim.EnterReadLock();
68 foreach (string str in _tempBlocked.Keys)
69 {
70 if (
71 Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
72 _tempBlocked[str]) > 0)
73 removes.Add(str);
74 }
75 _lockSlim.ExitReadLock();
76 lock (_deeperInspection)
77 {
78 _lockSlim.EnterWriteLock();
79 for (int i = 0; i < removes.Count; i++)
80 {
81 _tempBlocked.Remove(removes[i]);
82 _deeperInspection.Remove(removes[i]);
83 }
84 _lockSlim.ExitWriteLock();
85 }
86 foreach (string str in removes)
87 {
88 m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
89 _options.ReportingName, str);
90 }
91 _lockSlim.EnterReadLock();
92 if (_tempBlocked.Count > 0)
93 _forgetTimer.Enabled = true;
94 _lockSlim.ExitReadLock();
95 };
96
97 _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
98 } 51 }
99 52
100 public virtual byte[] Handle( 53 public virtual byte[] Handle(
@@ -102,58 +55,13 @@ namespace OpenSim.Framework.Servers.HttpServer
102 { 55 {
103 byte[] result; 56 byte[] result;
104 RequestsReceived++; 57 RequestsReceived++;
105 //httpRequest.Headers
106 58
107 if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1) 59 if (_dosProtector.Process(GetClientString(httpRequest), GetRemoteAddr(httpRequest)))
108 {
109 result = ProcessRequest(path, request, httpRequest, httpResponse); 60 result = ProcessRequest(path, request, httpRequest, httpResponse);
110 RequestsHandled++; 61 else
111 return result; 62 result = ThrottledRequest(path, request, httpRequest, httpResponse);
112
113 }
114
115 string clientstring = GetClientString(httpRequest);
116
117 _lockSlim.EnterReadLock();
118 if (_tempBlocked.ContainsKey(clientstring))
119 {
120 _lockSlim.ExitReadLock();
121
122 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
123 {
124 result = ThrottledRequest(path, request, httpRequest, httpResponse);
125 RequestsHandled++;
126 return result;
127 }
128 else
129 throw new System.Security.SecurityException("Throttled");
130 }
131 _lockSlim.ExitReadLock();
132
133 _generalRequestTimes.Put(Util.EnvironmentTickCount());
134 63
135 if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && 64
136 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
137 _options.RequestTimeSpan.TotalMilliseconds))
138 {
139 //Trigger deeper inspection
140 if (DeeperInspection(httpRequest))
141 {
142 result = ProcessRequest(path, request, httpRequest, httpResponse);
143 RequestsHandled++;
144 return result;
145 }
146 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
147 {
148 result = ThrottledRequest(path, request, httpRequest, httpResponse);
149 RequestsHandled++;
150 return result;
151 }
152 else
153 throw new System.Security.SecurityException("Throttled");
154 }
155
156 result =ProcessRequest(path, request, httpRequest, httpResponse);
157 RequestsHandled++; 65 RequestsHandled++;
158 66
159 return result; 67 return result;
@@ -171,43 +79,7 @@ namespace OpenSim.Framework.Servers.HttpServer
171 return new byte[0]; 79 return new byte[0];
172 } 80 }
173 81
174 private bool DeeperInspection(IOSHttpRequest httpRequest) 82
175 {
176 lock (_deeperInspection)
177 {
178 string clientstring = GetClientString(httpRequest);
179
180
181 if (_deeperInspection.ContainsKey(clientstring))
182 {
183 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
184 if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
185 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
186 _options.RequestTimeSpan.TotalMilliseconds))
187 {
188 _lockSlim.EnterWriteLock();
189 if (!_tempBlocked.ContainsKey(clientstring))
190 _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds);
191 else
192 _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds;
193 _lockSlim.ExitWriteLock();
194
195 m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, GetRemoteAddr(httpRequest));
196 return false;
197 }
198 //else
199 // return true;
200 }
201 else
202 {
203 _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
204 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
205 _forgetTimer.Enabled = true;
206 }
207
208 }
209 return true;
210 }
211 private string GetRemoteAddr(IOSHttpRequest httpRequest) 83 private string GetRemoteAddr(IOSHttpRequest httpRequest)
212 { 84 {
213 string remoteaddr = string.Empty; 85 string remoteaddr = string.Empty;
diff --git a/OpenSim/Framework/Servers/HttpServer/BasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BasicDOSProtector.cs
new file mode 100644
index 0000000..50a4ea2
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BasicDOSProtector.cs
@@ -0,0 +1,181 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31
32namespace OpenSim.Framework.Servers.HttpServer
33{
34
35 public class BasicDOSProtector
36 {
37 public enum ThrottleAction
38 {
39 DoThrottledMethod,
40 DoThrow
41 }
42 private readonly CircularBuffer<int> _generalRequestTimes; // General request checker
43 private readonly BasicDosProtectorOptions _options;
44 private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection; // per client request checker
45 private readonly Dictionary<string, int> _tempBlocked; // blocked list
46 private readonly System.Timers.Timer _forgetTimer; // Cleanup timer
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim();
49 public BasicDOSProtector(BasicDosProtectorOptions options)
50 {
51 _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1, true);
52 _generalRequestTimes.Put(0);
53 _options = options;
54 _deeperInspection = new Dictionary<string, CircularBuffer<int>>();
55 _tempBlocked = new Dictionary<string, int>();
56 _forgetTimer = new System.Timers.Timer();
57 _forgetTimer.Elapsed += delegate
58 {
59 _forgetTimer.Enabled = false;
60
61 List<string> removes = new List<string>();
62 _lockSlim.EnterReadLock();
63 foreach (string str in _tempBlocked.Keys)
64 {
65 if (
66 Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
67 _tempBlocked[str]) > 0)
68 removes.Add(str);
69 }
70 _lockSlim.ExitReadLock();
71 lock (_deeperInspection)
72 {
73 _lockSlim.EnterWriteLock();
74 for (int i = 0; i < removes.Count; i++)
75 {
76 _tempBlocked.Remove(removes[i]);
77 _deeperInspection.Remove(removes[i]);
78 }
79 _lockSlim.ExitWriteLock();
80 }
81 foreach (string str in removes)
82 {
83 m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
84 _options.ReportingName, str);
85 }
86 _lockSlim.EnterReadLock();
87 if (_tempBlocked.Count > 0)
88 _forgetTimer.Enabled = true;
89 _lockSlim.ExitReadLock();
90 };
91
92 _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
93 }
94 public bool Process(string key, string endpoint)
95 {
96 if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1)
97 return true;
98
99 string clientstring = key;
100
101 _lockSlim.EnterReadLock();
102 if (_tempBlocked.ContainsKey(clientstring))
103 {
104 _lockSlim.ExitReadLock();
105
106 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
107 return false;
108 else
109 throw new System.Security.SecurityException("Throttled");
110 }
111 _lockSlim.ExitReadLock();
112
113 _generalRequestTimes.Put(Util.EnvironmentTickCount());
114
115 if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
116 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
117 _options.RequestTimeSpan.TotalMilliseconds))
118 {
119 //Trigger deeper inspection
120 if (DeeperInspection(key, endpoint))
121 return true;
122 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
123 return false;
124 else
125 throw new System.Security.SecurityException("Throttled");
126 }
127 return true;
128 }
129 private bool DeeperInspection(string key, string endpoint)
130 {
131 lock (_deeperInspection)
132 {
133 string clientstring = key;
134
135
136 if (_deeperInspection.ContainsKey(clientstring))
137 {
138 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
139 if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
140 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
141 _options.RequestTimeSpan.TotalMilliseconds))
142 {
143 //Looks like we're over the limit
144 _lockSlim.EnterWriteLock();
145 if (!_tempBlocked.ContainsKey(clientstring))
146 _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds);
147 else
148 _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds;
149 _lockSlim.ExitWriteLock();
150
151 m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint);
152
153 return false;
154 }
155 //else
156 // return true;
157 }
158 else
159 {
160 _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
161 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
162 _forgetTimer.Enabled = true;
163 }
164
165 }
166 return true;
167 }
168
169 }
170
171
172 public class BasicDosProtectorOptions
173 {
174 public int MaxRequestsInTimeframe;
175 public TimeSpan RequestTimeSpan;
176 public TimeSpan ForgetTimeSpan;
177 public bool AllowXForwardedFor;
178 public string ReportingName = "BASICDOSPROTECTOR";
179 public BasicDOSProtector.ThrottleAction ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod;
180 }
181}
diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
index 5fc999a..39c98b4 100644
--- a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
+++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
@@ -25,13 +25,7 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections; 28using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Net;
33using OpenSim.Framework;
34using log4net;
35 29
36namespace OpenSim.Framework.Servers.HttpServer 30namespace OpenSim.Framework.Servers.HttpServer
37{ 31{
@@ -39,147 +33,26 @@ namespace OpenSim.Framework.Servers.HttpServer
39 { 33 {
40 private readonly GenericHTTPMethod _normalMethod; 34 private readonly GenericHTTPMethod _normalMethod;
41 private readonly GenericHTTPMethod _throttledMethod; 35 private readonly GenericHTTPMethod _throttledMethod;
42 private readonly CircularBuffer<int> _generalRequestTimes; 36
43 private readonly BasicDosProtectorOptions _options; 37 private readonly BasicDosProtectorOptions _options;
44 private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection; 38 private readonly BasicDOSProtector _dosProtector;
45 private readonly Dictionary<string, int> _tempBlocked;
46 private readonly System.Timers.Timer _forgetTimer;
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim();
49 39
50 public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options) 40 public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options)
51 { 41 {
52 _normalMethod = normalMethod; 42 _normalMethod = normalMethod;
53 _throttledMethod = throttledMethod; 43 _throttledMethod = throttledMethod;
54 _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1, true); 44
55 _generalRequestTimes.Put(0);
56 _options = options; 45 _options = options;
57 _deeperInspection = new Dictionary<string, CircularBuffer<int>>(); 46 _dosProtector = new BasicDOSProtector(_options);
58 _tempBlocked = new Dictionary<string, int>();
59 _forgetTimer = new System.Timers.Timer();
60 _forgetTimer.Elapsed += delegate
61 {
62 _forgetTimer.Enabled = false;
63
64 List<string> removes = new List<string>();
65 _lockSlim.EnterReadLock();
66 foreach (string str in _tempBlocked.Keys)
67 {
68 if (
69 Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
70 _tempBlocked[str]) > 0)
71 removes.Add(str);
72 }
73 _lockSlim.ExitReadLock();
74 lock (_deeperInspection)
75 {
76 _lockSlim.EnterWriteLock();
77 for (int i = 0; i < removes.Count; i++)
78 {
79 _tempBlocked.Remove(removes[i]);
80 _deeperInspection.Remove(removes[i]);
81 }
82 _lockSlim.ExitWriteLock();
83 }
84 foreach (string str in removes)
85 {
86 m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
87 _options.ReportingName, str);
88 }
89 _lockSlim.EnterReadLock();
90 if (_tempBlocked.Count > 0)
91 _forgetTimer.Enabled = true;
92 _lockSlim.ExitReadLock();
93 };
94
95 _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
96 } 47 }
97 public Hashtable Process(Hashtable request) 48 public Hashtable Process(Hashtable request)
98 { 49 {
99 if (_options.MaxRequestsInTimeframe < 1) 50 if (_dosProtector.Process(GetClientString(request), GetRemoteAddr(request)))
100 return _normalMethod(request); 51 return _normalMethod(request);
101 if (_options.RequestTimeSpan.TotalMilliseconds < 1) 52 else
102 return _normalMethod(request); 53 return _throttledMethod(request);
103
104 string clientstring = GetClientString(request);
105
106 _lockSlim.EnterReadLock();
107 if (_tempBlocked.ContainsKey(clientstring))
108 {
109 _lockSlim.ExitReadLock();
110
111 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
112 return _throttledMethod(request);
113 else
114 throw new System.Security.SecurityException("Throttled");
115 }
116 _lockSlim.ExitReadLock();
117
118 _generalRequestTimes.Put(Util.EnvironmentTickCount());
119
120 if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
121 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
122 _options.RequestTimeSpan.TotalMilliseconds))
123 {
124 //Trigger deeper inspection
125 if (DeeperInspection(request))
126 return _normalMethod(request);
127 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
128 return _throttledMethod(request);
129 else
130 throw new System.Security.SecurityException("Throttled");
131 }
132 Hashtable resp = null;
133 try
134 {
135 resp = _normalMethod(request);
136 }
137 catch (Exception)
138 {
139
140 throw;
141 }
142
143 return resp;
144 }
145 private bool DeeperInspection(Hashtable request)
146 {
147 lock (_deeperInspection)
148 {
149 string clientstring = GetClientString(request);
150
151
152 if (_deeperInspection.ContainsKey(clientstring))
153 {
154 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
155 if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
156 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
157 _options.RequestTimeSpan.TotalMilliseconds))
158 {
159 _lockSlim.EnterWriteLock();
160 if (!_tempBlocked.ContainsKey(clientstring))
161 _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds);
162 else
163 _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds;
164 _lockSlim.ExitWriteLock();
165
166 m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, GetRemoteAddr(request));
167 return false;
168 }
169 //else
170 // return true;
171 }
172 else
173 {
174 _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
175 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
176 _forgetTimer.Enabled = true;
177 }
178
179 }
180 return true;
181 } 54 }
182 55
183 private string GetRemoteAddr(Hashtable request) 56 private string GetRemoteAddr(Hashtable request)
184 { 57 {
185 string remoteaddr = ""; 58 string remoteaddr = "";
diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs
index ae59c95..bc7efb6 100644
--- a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs
+++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs
@@ -25,162 +25,42 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Net; 28using System.Net;
32using Nwc.XmlRpc; 29using Nwc.XmlRpc;
33using OpenSim.Framework; 30using OpenSim.Framework;
34using log4net; 31
35 32
36namespace OpenSim.Framework.Servers.HttpServer 33namespace OpenSim.Framework.Servers.HttpServer
37{ 34{
38 public enum ThrottleAction
39 {
40 DoThrottledMethod,
41 DoThrow
42 }
43
44 public class XmlRpcBasicDOSProtector 35 public class XmlRpcBasicDOSProtector
45 { 36 {
46 private readonly XmlRpcMethod _normalMethod; 37 private readonly XmlRpcMethod _normalMethod;
47 private readonly XmlRpcMethod _throttledMethod; 38 private readonly XmlRpcMethod _throttledMethod;
48 private readonly CircularBuffer<int> _generalRequestTimes; // General request checker 39
49 private readonly BasicDosProtectorOptions _options; 40 private readonly BasicDosProtectorOptions _options;
50 private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection; // per client request checker 41 private readonly BasicDOSProtector _dosProtector;
51 private readonly Dictionary<string, int> _tempBlocked; // blocked list
52 private readonly System.Timers.Timer _forgetTimer; // Cleanup timer
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim();
55 42
56 public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options) 43 public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options)
57 { 44 {
58 _normalMethod = normalMethod; 45 _normalMethod = normalMethod;
59 _throttledMethod = throttledMethod; 46 _throttledMethod = throttledMethod;
60 _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1,true); 47
61 _generalRequestTimes.Put(0);
62 _options = options; 48 _options = options;
63 _deeperInspection = new Dictionary<string, CircularBuffer<int>>(); 49 _dosProtector = new BasicDOSProtector(_options);
64 _tempBlocked = new Dictionary<string, int>();
65 _forgetTimer = new System.Timers.Timer();
66 _forgetTimer.Elapsed += delegate
67 {
68 _forgetTimer.Enabled = false;
69 50
70 List<string> removes = new List<string>();
71 _lockSlim.EnterReadLock();
72 foreach (string str in _tempBlocked.Keys)
73 {
74 if (
75 Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
76 _tempBlocked[str]) > 0)
77 removes.Add(str);
78 }
79 _lockSlim.ExitReadLock();
80 lock (_deeperInspection)
81 {
82 _lockSlim.EnterWriteLock();
83 for (int i = 0; i < removes.Count; i++)
84 {
85 _tempBlocked.Remove(removes[i]);
86 _deeperInspection.Remove(removes[i]);
87 }
88 _lockSlim.ExitWriteLock();
89 }
90 foreach (string str in removes)
91 {
92 m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
93 _options.ReportingName, str);
94 }
95 _lockSlim.EnterReadLock();
96 if (_tempBlocked.Count > 0)
97 _forgetTimer.Enabled = true;
98 _lockSlim.ExitReadLock();
99 };
100
101 _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
102 } 51 }
103 public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client) 52 public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client)
104 { 53 {
105 // If these are set like this, this is disabled
106 if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1)
107 return _normalMethod(request, client);
108
109 string clientstring = GetClientString(request, client);
110
111 _lockSlim.EnterReadLock();
112 if (_tempBlocked.ContainsKey(clientstring))
113 {
114 _lockSlim.ExitReadLock();
115 54
116 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
117 return _throttledMethod(request, client);
118 else
119 throw new System.Security.SecurityException("Throttled");
120 }
121 _lockSlim.ExitReadLock();
122
123 _generalRequestTimes.Put(Util.EnvironmentTickCount());
124
125 if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
126 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
127 _options.RequestTimeSpan.TotalMilliseconds))
128 {
129 //Trigger deeper inspection
130 if (DeeperInspection(request, client))
131 return _normalMethod(request, client);
132 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
133 return _throttledMethod(request, client);
134 else
135 throw new System.Security.SecurityException("Throttled");
136 }
137 XmlRpcResponse resp = null; 55 XmlRpcResponse resp = null;
138 56 if (_dosProtector.Process(GetClientString(request, client), GetEndPoint(request, client)))
139 resp = _normalMethod(request, client); 57 resp = _normalMethod(request, client);
140 58 else
59 resp = _throttledMethod(request, client);
60
141 return resp; 61 return resp;
142 } 62 }
143 63
144 // If the service is getting more hits per expected timeframe then it starts to separate them out by client
145 private bool DeeperInspection(XmlRpcRequest request, IPEndPoint client)
146 {
147 lock (_deeperInspection)
148 {
149 string clientstring = GetClientString(request, client);
150
151
152 if (_deeperInspection.ContainsKey(clientstring))
153 {
154 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
155 if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
156 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
157 _options.RequestTimeSpan.TotalMilliseconds))
158 {
159 //Looks like we're over the limit
160 _lockSlim.EnterWriteLock();
161 if (!_tempBlocked.ContainsKey(clientstring))
162 _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds);
163 else
164 _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds;
165 _lockSlim.ExitWriteLock();
166
167 m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}",_options.ReportingName,clientstring,_options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, client.Address);
168
169 return false;
170 }
171 //else
172 // return true;
173 }
174 else
175 {
176 _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
177 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
178 _forgetTimer.Enabled = true;
179 }
180
181 }
182 return true;
183 }
184 private string GetClientString(XmlRpcRequest request, IPEndPoint client) 64 private string GetClientString(XmlRpcRequest request, IPEndPoint client)
185 { 65 {
186 string clientstring; 66 string clientstring;
@@ -197,15 +77,12 @@ namespace OpenSim.Framework.Servers.HttpServer
197 return clientstring; 77 return clientstring;
198 } 78 }
199 79
200 } 80 private string GetEndPoint(XmlRpcRequest request, IPEndPoint client)
81 {
82 return client.Address.ToString();
83 }
201 84
202 public class BasicDosProtectorOptions
203 {
204 public int MaxRequestsInTimeframe;
205 public TimeSpan RequestTimeSpan;
206 public TimeSpan ForgetTimeSpan;
207 public bool AllowXForwardedFor;
208 public string ReportingName = "BASICDOSPROTECTOR";
209 public ThrottleAction ThrottledAction = ThrottleAction.DoThrottledMethod;
210 } 85 }
86
87
211} 88}