aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/BasicDOSProtector.cs
diff options
context:
space:
mode:
authorteravus2013-10-07 23:48:24 -0500
committerteravus2013-10-07 23:48:24 -0500
commit1df58d04b16601fb3afa408ef48e59aa68dc335b (patch)
treec19027893d939d6315d16a43b1158644359cdb89 /OpenSim/Framework/BasicDOSProtector.cs
parent* Refactor (diff)
downloadopensim-SC_OLD-1df58d04b16601fb3afa408ef48e59aa68dc335b.zip
opensim-SC_OLD-1df58d04b16601fb3afa408ef48e59aa68dc335b.tar.gz
opensim-SC_OLD-1df58d04b16601fb3afa408ef48e59aa68dc335b.tar.bz2
opensim-SC_OLD-1df58d04b16601fb3afa408ef48e59aa68dc335b.tar.xz
* Move the BasicDOSProtector.cs to OpenSim.Framework (all useful classes belong there.....)
* Add an IsBlocked(string Key) method so it can be used more generically. (think.. if we want to rate limit login failures, we could have a call in the Login Service to IsBlocked(uuid.ToString()) and ignore the connection if it returns true, if IsBlocked returns false, we could run the login information and if the login fails we could run the Process method to count the login failures.
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}