aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/BasicDOSProtector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/BasicDOSProtector.cs')
-rw-r--r--OpenSim/Framework/BasicDOSProtector.cs209
1 files changed, 209 insertions, 0 deletions
diff --git a/OpenSim/Framework/BasicDOSProtector.cs b/OpenSim/Framework/BasicDOSProtector.cs
new file mode 100644
index 0000000..b470161
--- /dev/null
+++ b/OpenSim/Framework/BasicDOSProtector.cs
@@ -0,0 +1,209 @@
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
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
95 /// <summary>
96 /// Given a string Key, Returns if that context is blocked
97 /// </summary>
98 /// <param name="key">A Key identifying the context</param>
99 /// <returns>bool Yes or No, True or False for blocked</returns>
100 public bool IsBlocked(string key)
101 {
102 bool ret = false;
103 _lockSlim.EnterReadLock();
104 ret = _tempBlocked.ContainsKey(key);
105 _lockSlim.ExitReadLock();
106 return ret;
107 }
108
109 /// <summary>
110 /// Process the velocity of this context
111 /// </summary>
112 /// <param name="key"></param>
113 /// <param name="endpoint"></param>
114 /// <returns></returns>
115 public bool Process(string key, string endpoint)
116 {
117 if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1)
118 return true;
119
120 string clientstring = key;
121
122 _lockSlim.EnterReadLock();
123 if (_tempBlocked.ContainsKey(clientstring))
124 {
125 _lockSlim.ExitReadLock();
126
127 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
128 return false;
129 else
130 throw new System.Security.SecurityException("Throttled");
131 }
132 _lockSlim.ExitReadLock();
133
134 _generalRequestTimes.Put(Util.EnvironmentTickCount());
135
136 if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
137 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
138 _options.RequestTimeSpan.TotalMilliseconds))
139 {
140 //Trigger deeper inspection
141 if (DeeperInspection(key, endpoint))
142 return true;
143 if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
144 return false;
145 else
146 throw new System.Security.SecurityException("Throttled");
147 }
148 return true;
149 }
150
151 /// <summary>
152 /// At this point, the rate limiting code needs to track 'per user' velocity.
153 /// </summary>
154 /// <param name="key">Context Key, string representing a rate limiting context</param>
155 /// <param name="endpoint"></param>
156 /// <returns></returns>
157 private bool DeeperInspection(string key, string endpoint)
158 {
159 lock (_deeperInspection)
160 {
161 string clientstring = key;
162
163
164 if (_deeperInspection.ContainsKey(clientstring))
165 {
166 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
167 if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
168 (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
169 _options.RequestTimeSpan.TotalMilliseconds))
170 {
171 //Looks like we're over the limit
172 _lockSlim.EnterWriteLock();
173 if (!_tempBlocked.ContainsKey(clientstring))
174 _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds);
175 else
176 _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds;
177 _lockSlim.ExitWriteLock();
178
179 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);
180
181 return false;
182 }
183 //else
184 // return true;
185 }
186 else
187 {
188 _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
189 _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
190 _forgetTimer.Enabled = true;
191 }
192
193 }
194 return true;
195 }
196
197 }
198
199
200 public class BasicDosProtectorOptions
201 {
202 public int MaxRequestsInTimeframe;
203 public TimeSpan RequestTimeSpan;
204 public TimeSpan ForgetTimeSpan;
205 public bool AllowXForwardedFor;
206 public string ReportingName = "BASICDOSPROTECTOR";
207 public BasicDOSProtector.ThrottleAction ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod;
208 }
209}