From 75f63ecfcd885ae71bca130ee0db1ecc997b86cd Mon Sep 17 00:00:00 2001 From: teravus Date: Wed, 9 Oct 2013 22:21:25 -0500 Subject: * Add a session concurrency option per key. Allows developer/config to specify number of concurrent requests on a service. --- OpenSim/Framework/BasicDOSProtector.cs | 96 ++++++++++++++++++---- .../BaseStreamHandlerBasicDOSProtector.cs | 8 +- .../HttpServer/GenericHTTPBasicDOSProtector.cs | 14 +++- .../Servers/HttpServer/XmlRpcBasicDOSProtector.cs | 7 +- 4 files changed, 102 insertions(+), 23 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/BasicDOSProtector.cs b/OpenSim/Framework/BasicDOSProtector.cs index b470161..89bfa94 100644 --- a/OpenSim/Framework/BasicDOSProtector.cs +++ b/OpenSim/Framework/BasicDOSProtector.cs @@ -43,9 +43,11 @@ namespace OpenSim.Framework private readonly BasicDosProtectorOptions _options; private readonly Dictionary> _deeperInspection; // per client request checker private readonly Dictionary _tempBlocked; // blocked list + private readonly Dictionary _sessions; private readonly System.Timers.Timer _forgetTimer; // Cleanup timer private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim(); + private readonly System.Threading.ReaderWriterLockSlim _blockLockSlim = new System.Threading.ReaderWriterLockSlim(); + private readonly System.Threading.ReaderWriterLockSlim _sessionLockSlim = new System.Threading.ReaderWriterLockSlim(); public BasicDOSProtector(BasicDosProtectorOptions options) { _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1, true); @@ -53,13 +55,14 @@ namespace OpenSim.Framework _options = options; _deeperInspection = new Dictionary>(); _tempBlocked = new Dictionary(); + _sessions = new Dictionary(); _forgetTimer = new System.Timers.Timer(); _forgetTimer.Elapsed += delegate { _forgetTimer.Enabled = false; List removes = new List(); - _lockSlim.EnterReadLock(); + _blockLockSlim.EnterReadLock(); foreach (string str in _tempBlocked.Keys) { if ( @@ -67,26 +70,27 @@ namespace OpenSim.Framework _tempBlocked[str]) > 0) removes.Add(str); } - _lockSlim.ExitReadLock(); + _blockLockSlim.ExitReadLock(); lock (_deeperInspection) { - _lockSlim.EnterWriteLock(); + _blockLockSlim.EnterWriteLock(); for (int i = 0; i < removes.Count; i++) { _tempBlocked.Remove(removes[i]); _deeperInspection.Remove(removes[i]); + _sessions.Remove(removes[i]); } - _lockSlim.ExitWriteLock(); + _blockLockSlim.ExitWriteLock(); } foreach (string str in removes) { m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", _options.ReportingName, str); } - _lockSlim.EnterReadLock(); + _blockLockSlim.EnterReadLock(); if (_tempBlocked.Count > 0) _forgetTimer.Enabled = true; - _lockSlim.ExitReadLock(); + _blockLockSlim.ExitReadLock(); }; _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; @@ -100,9 +104,9 @@ namespace OpenSim.Framework public bool IsBlocked(string key) { bool ret = false; - _lockSlim.EnterReadLock(); + _blockLockSlim.EnterReadLock(); ret = _tempBlocked.ContainsKey(key); - _lockSlim.ExitReadLock(); + _blockLockSlim.ExitReadLock(); return ret; } @@ -119,20 +123,58 @@ namespace OpenSim.Framework string clientstring = key; - _lockSlim.EnterReadLock(); + _blockLockSlim.EnterReadLock(); if (_tempBlocked.ContainsKey(clientstring)) { - _lockSlim.ExitReadLock(); + _blockLockSlim.ExitReadLock(); if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) return false; else throw new System.Security.SecurityException("Throttled"); } - _lockSlim.ExitReadLock(); + + _blockLockSlim.ExitReadLock(); - _generalRequestTimes.Put(Util.EnvironmentTickCount()); + lock (_generalRequestTimes) + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + if (_options.MaxConcurrentSessions > 0) + { + int sessionscount = 0; + + _sessionLockSlim.EnterReadLock(); + if (_sessions.ContainsKey(key)) + sessionscount = _sessions[key]; + _sessionLockSlim.ExitReadLock(); + + if (sessionscount > _options.MaxConcurrentSessions) + { + // Add to blocking and cleanup methods + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + { + _tempBlocked.Add(clientstring, + Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds); + _forgetTimer.Enabled = true; + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds based on concurrency, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + } + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + } + + + } + else + ProcessConcurrency(key, endpoint); + } if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < _options.RequestTimeSpan.TotalMilliseconds)) @@ -147,6 +189,29 @@ namespace OpenSim.Framework } return true; } + private void ProcessConcurrency(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + _sessions[key] = _sessions[key] + 1; + else + _sessions.Add(key,1); + _sessionLockSlim.ExitWriteLock(); + } + public void ProcessEnd(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + { + _sessions[key]--; + if (_sessions[key] <= 0) + _sessions.Remove(key); + } + else + _sessions.Add(key, 1); + + _sessionLockSlim.ExitWriteLock(); + } /// /// At this point, the rate limiting code needs to track 'per user' velocity. @@ -169,12 +234,12 @@ namespace OpenSim.Framework _options.RequestTimeSpan.TotalMilliseconds)) { //Looks like we're over the limit - _lockSlim.EnterWriteLock(); + _blockLockSlim.EnterWriteLock(); if (!_tempBlocked.ContainsKey(clientstring)) _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); else _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; - _lockSlim.ExitWriteLock(); + _blockLockSlim.ExitWriteLock(); 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); @@ -205,5 +270,6 @@ namespace OpenSim.Framework public bool AllowXForwardedFor; public string ReportingName = "BASICDOSPROTECTOR"; public BasicDOSProtector.ThrottleAction ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod; + public int MaxConcurrentSessions; } } diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs index 9b8b8c2..1b88545 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs @@ -55,12 +55,14 @@ namespace OpenSim.Framework.Servers.HttpServer { byte[] result; RequestsReceived++; - - if (_dosProtector.Process(GetClientString(httpRequest), GetRemoteAddr(httpRequest))) + string clientstring = GetClientString(httpRequest); + string endpoint = GetRemoteAddr(httpRequest); + if (_dosProtector.Process(clientstring, endpoint)) result = ProcessRequest(path, request, httpRequest, httpResponse); else result = ThrottledRequest(path, request, httpRequest, httpResponse); - + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); RequestsHandled++; diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs index 39c98b4..cd4b8ff 100644 --- a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs @@ -47,10 +47,18 @@ namespace OpenSim.Framework.Servers.HttpServer } public Hashtable Process(Hashtable request) { - if (_dosProtector.Process(GetClientString(request), GetRemoteAddr(request))) - return _normalMethod(request); + Hashtable process = null; + string clientstring= GetClientString(request); + string endpoint = GetRemoteAddr(request); + if (_dosProtector.Process(clientstring, endpoint)) + process = _normalMethod(request); else - return _throttledMethod(request); + process = _throttledMethod(request); + + if (_options.MaxConcurrentSessions>0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + return process; } private string GetRemoteAddr(Hashtable request) diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs index bc7efb6..f212208 100644 --- a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs @@ -53,11 +53,14 @@ namespace OpenSim.Framework.Servers.HttpServer { XmlRpcResponse resp = null; - if (_dosProtector.Process(GetClientString(request, client), GetEndPoint(request, client))) + string clientstring = GetClientString(request, client); + string endpoint = GetEndPoint(request, client); + if (_dosProtector.Process(clientstring, endpoint)) resp = _normalMethod(request, client); else resp = _throttledMethod(request, client); - + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); return resp; } -- cgit v1.1