aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs')
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs233
1 files changed, 233 insertions, 0 deletions
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
new file mode 100644
index 0000000..8fc9a8a
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
@@ -0,0 +1,233 @@
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 OpenSim.Framework;
28using System.Collections.Generic;
29using System.IO;
30using System.Reflection;
31using log4net;
32
33namespace OpenSim.Framework.Servers.HttpServer
34{
35 /// <summary>
36 /// BaseStreamHandlerBasicDOSProtector Base streamed request handler.
37 /// </summary>
38 /// <remarks>
39 /// Inheriting classes should override ProcessRequest() rather than Handle()
40 /// </remarks>
41 public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler
42 {
43 private readonly CircularBuffer<int> _generalRequestTimes;
44 private readonly BasicDosProtectorOptions _options;
45 private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection;
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
51 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {}
52
53 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options)
54 : base(httpMethod, path, name, description)
55 {
56 _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1, true);
57 _generalRequestTimes.Put(0);
58 _options = options;
59 _deeperInspection = new Dictionary<string, CircularBuffer<int>>();
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 }
99
100 public virtual byte[] Handle(
101 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
102 {
103 byte[] result;
104 RequestsReceived++;
105 //httpRequest.Headers
106
107 if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1)
108 {
109 result = ProcessRequest(path, request, httpRequest, httpResponse);
110 RequestsHandled++;
111 return result;
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
135 if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
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++;
158
159 return result;
160 }
161
162 protected virtual byte[] ProcessRequest(
163 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
164 {
165 return null;
166 }
167
168 protected virtual byte[] ThrottledRequest(
169 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
170 {
171 return new byte[0];
172 }
173
174 private bool DeeperInspection(IOSHttpRequest httpRequest)
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)
212 {
213 string remoteaddr = string.Empty;
214 if (httpRequest.Headers["remote_addr"] != null)
215 remoteaddr = httpRequest.Headers["remote_addr"];
216
217 return remoteaddr;
218 }
219
220 private string GetClientString(IOSHttpRequest httpRequest)
221 {
222 string clientstring = string.Empty;
223
224 if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null)
225 clientstring = httpRequest.Headers["x-forwarded-for"];
226 else
227 clientstring = GetRemoteAddr(httpRequest);
228
229 return clientstring;
230
231 }
232 }
233}