Selaa lähdekoodia

* Added a Basic DOS protection container/base object for the most common HTTP Server handlers. XMLRPC Handler, GenericHttpHandler and <Various>StreamHandler
* Applied the XmlRpcBasicDOSProtector.cs to the login service as both an example, and good practice.
* Applied the BaseStreamHandlerBasicDOSProtector.cs to the friends service as an example of the DOS Protector on StreamHandlers
* Added CircularBuffer, used for CPU and Memory friendly rate monitoring.
* DosProtector has 2 states, 1. Just Check for blocked users and check general velocity, 2. Track velocity per user, It only jumps to 2 if it's getting a lot of requests, and state 1 is about as resource friendly as if it wasn't even there.

teravus 10 vuotta sitten
vanhempi
commit
f76cc6036e

+ 312 - 0
OpenSim/Framework/CircularBuffer.cs

@@ -0,0 +1,312 @@
+/*
+Copyright (c) 2012, Alex Regueiro 
+All rights reserved.
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
+following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
+in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.
+*/ 
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace OpenSim.Framework
+{
+    public class CircularBuffer<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable
+    {
+        private int capacity;
+        private int size;
+        private int head;
+        private int tail;
+        private T[] buffer;
+
+        [NonSerialized()]
+        private object syncRoot;
+
+        public CircularBuffer(int capacity)
+            : this(capacity, false)
+        {
+        }
+
+        public CircularBuffer(int capacity, bool allowOverflow)
+        {
+            if (capacity < 0)
+                throw new ArgumentException("Needs to have at least 1","capacity");
+
+            this.capacity = capacity;
+            size = 0;
+            head = 0;
+            tail = 0;
+            buffer = new T[capacity];
+            AllowOverflow = allowOverflow;
+        }
+
+        public bool AllowOverflow
+        {
+            get;
+            set;
+        }
+
+        public int Capacity
+        {
+            get { return capacity; }
+            set
+            {
+                if (value == capacity)
+                    return;
+
+                if (value < size)
+                    throw new ArgumentOutOfRangeException("value","Capacity is too small.");
+
+                var dst = new T[value];
+                if (size > 0)
+                    CopyTo(dst);
+                buffer = dst;
+
+                capacity = value;
+            }
+        }
+
+        public int Size
+        {
+            get { return size; }
+        }
+
+        public bool Contains(T item)
+        {
+            int bufferIndex = head;
+            var comparer = EqualityComparer<T>.Default;
+            for (int i = 0; i < size; i++, bufferIndex++)
+            {
+                if (bufferIndex == capacity)
+                    bufferIndex = 0;
+
+                if (item == null && buffer[bufferIndex] == null)
+                    return true;
+                else if ((buffer[bufferIndex] != null) &&
+                    comparer.Equals(buffer[bufferIndex], item))
+                    return true;
+            }
+
+            return false;
+        }
+
+        public void Clear()
+        {
+            size = 0;
+            head = 0;
+            tail = 0;
+        }
+
+        public int Put(T[] src)
+        {
+            return Put(src, 0, src.Length);
+        }
+
+        public int Put(T[] src, int offset, int count)
+        {
+            if (!AllowOverflow && count > capacity - size)
+                throw new InvalidOperationException("Buffer Overflow");
+
+            int srcIndex = offset;
+            for (int i = 0; i < count; i++, tail++, srcIndex++)
+            {
+                if (tail == capacity)
+                    tail = 0;
+                buffer[tail] = src[srcIndex];
+            }
+            size = Math.Min(size + count, capacity);
+            return count;
+        }
+
+        public void Put(T item)
+        {
+            if (!AllowOverflow && size == capacity)
+                throw new InvalidOperationException("Buffer Overflow");
+
+            buffer[tail] = item;
+            if (++tail == capacity)
+                tail = 0;
+            size++;
+        }
+
+        public void Skip(int count)
+        {
+            head += count;
+            if (head >= capacity)
+                head -= capacity;
+        }
+
+        public T[] Get(int count)
+        {
+            var dst = new T[count];
+            Get(dst);
+            return dst;
+        }
+
+        public int Get(T[] dst)
+        {
+            return Get(dst, 0, dst.Length);
+        }
+
+        public int Get(T[] dst, int offset, int count)
+        {
+            int realCount = Math.Min(count, size);
+            int dstIndex = offset;
+            for (int i = 0; i < realCount; i++, head++, dstIndex++)
+            {
+                if (head == capacity)
+                    head = 0;
+                dst[dstIndex] = buffer[head];
+            }
+            size -= realCount;
+            return realCount;
+        }
+
+        public T Get()
+        {
+            if (size == 0)
+                throw new InvalidOperationException("Buffer Empty");
+
+            var item = buffer[head];
+            if (++head == capacity)
+                head = 0;
+            size--;
+            return item;
+        }
+
+        public void CopyTo(T[] array)
+        {
+            CopyTo(array, 0);
+        }
+
+        public void CopyTo(T[] array, int arrayIndex)
+        {
+            CopyTo(0, array, arrayIndex, size);
+        }
+
+        public void CopyTo(int index, T[] array, int arrayIndex, int count)
+        {
+            if (count > size)
+                throw new ArgumentOutOfRangeException("count", "Count Too Large");
+
+            int bufferIndex = head;
+            for (int i = 0; i < count; i++, bufferIndex++, arrayIndex++)
+            {
+                if (bufferIndex == capacity)
+                    bufferIndex = 0;
+                array[arrayIndex] = buffer[bufferIndex];
+            }
+        }
+
+        public IEnumerator<T> GetEnumerator()
+        {
+            int bufferIndex = head;
+            for (int i = 0; i < size; i++, bufferIndex++)
+            {
+                if (bufferIndex == capacity)
+                    bufferIndex = 0;
+
+                yield return buffer[bufferIndex];
+            }
+        }
+
+        public T[] GetBuffer()
+        {
+            return buffer;
+        }
+
+        public T[] ToArray()
+        {
+            var dst = new T[size];
+            CopyTo(dst);
+            return dst;
+        }
+
+        #region ICollection<T> Members
+
+        int ICollection<T>.Count
+        {
+            get { return Size; }
+        }
+
+        bool ICollection<T>.IsReadOnly
+        {
+            get { return false; }
+        }
+
+        void ICollection<T>.Add(T item)
+        {
+            Put(item);
+        }
+
+        bool ICollection<T>.Remove(T item)
+        {
+            if (size == 0)
+                return false;
+
+            Get();
+            return true;
+        }
+
+        #endregion
+
+        #region IEnumerable<T> Members
+
+        IEnumerator<T> IEnumerable<T>.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        #endregion
+
+        #region ICollection Members
+
+        int ICollection.Count
+        {
+            get { return Size; }
+        }
+
+        bool ICollection.IsSynchronized
+        {
+            get { return false; }
+        }
+
+        object ICollection.SyncRoot
+        {
+            get
+            {
+                if (syncRoot == null)
+                    Interlocked.CompareExchange(ref syncRoot, new object(), null);
+                return syncRoot;
+            }
+        }
+
+        void ICollection.CopyTo(Array array, int arrayIndex)
+        {
+            CopyTo((T[])array, arrayIndex);
+        }
+
+        #endregion
+
+        #region IEnumerable Members
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return (IEnumerator)GetEnumerator();
+        }
+
+        #endregion
+    }
+}

+ 233 - 0
OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs

@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+using OpenSim.Framework;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using log4net;
+
+namespace OpenSim.Framework.Servers.HttpServer
+{
+    /// <summary>
+    /// BaseStreamHandlerBasicDOSProtector Base streamed request handler.
+    /// </summary>
+    /// <remarks>
+    /// Inheriting classes should override ProcessRequest() rather than Handle()
+    /// </remarks>
+    public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler
+    {
+        private readonly CircularBuffer<int> _generalRequestTimes;
+        private readonly BasicDosProtectorOptions _options;
+        private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection;
+        private readonly Dictionary<string, int> _tempBlocked;
+        private readonly System.Timers.Timer _forgetTimer;
+        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+        private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim();
+
+        protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {}
+
+        protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options)
+            : base(httpMethod, path, name, description)
+        {
+            _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1, true);
+            _generalRequestTimes.Put(0);
+            _options = options;
+            _deeperInspection = new Dictionary<string, CircularBuffer<int>>();
+            _tempBlocked = new Dictionary<string, int>();
+            _forgetTimer = new System.Timers.Timer();
+            _forgetTimer.Elapsed += delegate
+            {
+                _forgetTimer.Enabled = false;
+
+                List<string> removes = new List<string>();
+                _lockSlim.EnterReadLock();
+                foreach (string str in _tempBlocked.Keys)
+                {
+                    if (
+                        Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
+                                                          _tempBlocked[str]) > 0)
+                        removes.Add(str);
+                }
+                _lockSlim.ExitReadLock();
+                lock (_deeperInspection)
+                {
+                    _lockSlim.EnterWriteLock();
+                    for (int i = 0; i < removes.Count; i++)
+                    {
+                        _tempBlocked.Remove(removes[i]);
+                        _deeperInspection.Remove(removes[i]);
+                    }
+                    _lockSlim.ExitWriteLock();
+                }
+                foreach (string str in removes)
+                {
+                    m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
+                                     _options.ReportingName, str);
+                }
+                _lockSlim.EnterReadLock();
+                if (_tempBlocked.Count > 0)
+                    _forgetTimer.Enabled = true;
+                _lockSlim.ExitReadLock();
+            };
+
+            _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
+        }
+
+        public virtual byte[] Handle(
+            string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+        {
+            byte[] result;
+            RequestsReceived++;
+            //httpRequest.Headers
+
+            if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1)
+            {
+                result = ProcessRequest(path, request, httpRequest, httpResponse);
+                RequestsHandled++;
+                return result;
+                
+            }
+
+            string clientstring = GetClientString(httpRequest);
+
+            _lockSlim.EnterReadLock();
+            if (_tempBlocked.ContainsKey(clientstring))
+            {
+                _lockSlim.ExitReadLock();
+
+                if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
+                {
+                    result = ThrottledRequest(path, request, httpRequest, httpResponse);
+                    RequestsHandled++;
+                    return result;
+                }
+                else
+                    throw new System.Security.SecurityException("Throttled");
+            }
+            _lockSlim.ExitReadLock();
+
+            _generalRequestTimes.Put(Util.EnvironmentTickCount());
+
+            if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
+                (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
+                 _options.RequestTimeSpan.TotalMilliseconds))
+            {
+                //Trigger deeper inspection
+                if (DeeperInspection(httpRequest))
+                {
+                    result = ProcessRequest(path, request, httpRequest, httpResponse);
+                    RequestsHandled++;
+                    return result;
+                }
+                if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
+                {
+                    result = ThrottledRequest(path, request, httpRequest, httpResponse);
+                    RequestsHandled++;
+                    return result;
+                }
+                else
+                    throw new System.Security.SecurityException("Throttled");
+            }
+           
+            result =ProcessRequest(path, request, httpRequest, httpResponse);
+            RequestsHandled++;
+
+            return result;
+        }
+
+        protected virtual byte[] ProcessRequest(
+            string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+        {
+            return null;
+        }
+
+        protected virtual byte[] ThrottledRequest(
+            string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+        {
+            return new byte[0];
+        }
+
+        private bool DeeperInspection(IOSHttpRequest httpRequest)
+        {
+            lock (_deeperInspection)
+            {
+                string clientstring = GetClientString(httpRequest);
+
+
+                if (_deeperInspection.ContainsKey(clientstring))
+                {
+                    _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
+                    if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
+                        (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
+                         _options.RequestTimeSpan.TotalMilliseconds))
+                    {
+                        _lockSlim.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();
+
+                        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));
+                        return false;
+                    }
+                    //else
+                    //   return true;
+                }
+                else
+                {
+                    _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
+                    _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
+                    _forgetTimer.Enabled = true;
+                }
+
+            }
+            return true;
+        }
+        private string GetRemoteAddr(IOSHttpRequest httpRequest)
+        {
+            string remoteaddr = string.Empty;
+            if (httpRequest.Headers["remote_addr"] != null)
+                remoteaddr = httpRequest.Headers["remote_addr"];
+
+            return remoteaddr;
+        }
+
+        private string GetClientString(IOSHttpRequest httpRequest)
+        {
+            string clientstring = string.Empty;
+
+            if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null)
+                clientstring = httpRequest.Headers["x-forwarded-for"];
+            else
+                clientstring = GetRemoteAddr(httpRequest);
+
+            return clientstring;
+           
+        }
+    }
+}

+ 238 - 0
OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs

@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Net;
+using OpenSim.Framework;
+using log4net;
+
+namespace OpenSim.Framework.Servers.HttpServer
+{
+    public class GenericHTTPDOSProtector
+    {
+        private readonly GenericHTTPMethod _normalMethod;
+        private readonly GenericHTTPMethod _throttledMethod;
+        private readonly CircularBuffer<int> _generalRequestTimes;
+        private readonly BasicDosProtectorOptions _options;
+        private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection;
+        private readonly Dictionary<string, int> _tempBlocked;
+        private readonly System.Timers.Timer _forgetTimer;
+        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+        private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim();
+
+        public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options)
+        {
+            _normalMethod = normalMethod;
+            _throttledMethod = throttledMethod;
+            _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1, true);
+            _generalRequestTimes.Put(0);
+            _options = options;
+            _deeperInspection = new Dictionary<string, CircularBuffer<int>>();
+            _tempBlocked = new Dictionary<string, int>();
+            _forgetTimer = new System.Timers.Timer();
+            _forgetTimer.Elapsed += delegate
+            {
+                _forgetTimer.Enabled = false;
+
+                List<string> removes = new List<string>();
+                _lockSlim.EnterReadLock();
+                foreach (string str in _tempBlocked.Keys)
+                {
+                    if (
+                        Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
+                                                          _tempBlocked[str]) > 0)
+                        removes.Add(str);
+                }
+                _lockSlim.ExitReadLock();
+                lock (_deeperInspection)
+                {
+                    _lockSlim.EnterWriteLock();
+                    for (int i = 0; i < removes.Count; i++)
+                    {
+                        _tempBlocked.Remove(removes[i]);
+                        _deeperInspection.Remove(removes[i]);
+                    }
+                    _lockSlim.ExitWriteLock();
+                }
+                foreach (string str in removes)
+                {
+                    m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
+                                     _options.ReportingName, str);
+                }
+                _lockSlim.EnterReadLock();
+                if (_tempBlocked.Count > 0)
+                    _forgetTimer.Enabled = true;
+                _lockSlim.ExitReadLock();
+            };
+
+            _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
+        }
+        public Hashtable Process(Hashtable request)
+        {
+            if (_options.MaxRequestsInTimeframe < 1)
+                return _normalMethod(request);
+            if (_options.RequestTimeSpan.TotalMilliseconds < 1)
+                return _normalMethod(request);
+
+            string clientstring = GetClientString(request);
+
+            _lockSlim.EnterReadLock();
+            if (_tempBlocked.ContainsKey(clientstring))
+            {
+                _lockSlim.ExitReadLock();
+
+                if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
+                    return _throttledMethod(request);
+                else
+                    throw new System.Security.SecurityException("Throttled");
+            }
+            _lockSlim.ExitReadLock();
+
+            _generalRequestTimes.Put(Util.EnvironmentTickCount());
+
+            if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
+                (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
+                 _options.RequestTimeSpan.TotalMilliseconds))
+            {
+                //Trigger deeper inspection
+                if (DeeperInspection(request))
+                    return _normalMethod(request);
+                if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
+                    return _throttledMethod(request);
+                else
+                    throw new System.Security.SecurityException("Throttled");
+            }
+            Hashtable resp = null;
+            try
+            {
+                resp = _normalMethod(request);
+            }
+            catch (Exception)
+            {
+
+                throw;
+            }
+
+            return resp;
+        }
+        private bool DeeperInspection(Hashtable request)
+        {
+            lock (_deeperInspection)
+            {
+                string clientstring = GetClientString(request);
+
+
+                if (_deeperInspection.ContainsKey(clientstring))
+                {
+                    _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
+                    if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
+                        (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
+                         _options.RequestTimeSpan.TotalMilliseconds))
+                    {
+                        _lockSlim.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();
+                        
+                         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));
+                        return false;
+                    }
+                    //else
+                    //   return true;
+                }
+                else
+                {
+                    _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
+                    _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
+                    _forgetTimer.Enabled = true;
+                }
+
+            }
+            return true;
+        }
+
+        private string GetRemoteAddr(Hashtable request)
+        {
+            string remoteaddr = "";
+            if (!request.ContainsKey("headers"))
+                return remoteaddr;
+            Hashtable requestinfo = (Hashtable)request["headers"];
+            if (!requestinfo.ContainsKey("remote_addr"))
+                return remoteaddr;
+            object remote_addrobj = requestinfo["remote_addr"];
+            if (remote_addrobj != null)
+            {
+                if (!string.IsNullOrEmpty(remote_addrobj.ToString()))
+                {
+                    remoteaddr = remote_addrobj.ToString();
+                }
+
+            }
+            return remoteaddr;
+        }
+
+        private string GetClientString(Hashtable request)
+        {
+            string clientstring = "";
+            if (!request.ContainsKey("headers"))
+                return clientstring;
+
+            Hashtable requestinfo = (Hashtable)request["headers"];
+            if (_options.AllowXForwardedFor && requestinfo.ContainsKey("x-forwarded-for"))
+            {
+                object str = requestinfo["x-forwarded-for"];
+                if (str != null)
+                {
+                    if (!string.IsNullOrEmpty(str.ToString()))
+                    {
+                        return str.ToString();
+                    }
+                }
+            }
+            if (!requestinfo.ContainsKey("remote_addr"))
+                return clientstring;
+
+            object remote_addrobj = requestinfo["remote_addr"];
+            if (remote_addrobj != null)
+            {
+                if (!string.IsNullOrEmpty(remote_addrobj.ToString()))
+                {
+                    clientstring = remote_addrobj.ToString();
+                }
+            }
+
+            return clientstring;
+
+        }
+
+    }
+}

+ 211 - 0
OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs

@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Net;
+using Nwc.XmlRpc;
+using OpenSim.Framework;
+using log4net;
+
+namespace OpenSim.Framework.Servers.HttpServer
+{
+    public enum ThrottleAction
+    {
+        DoThrottledMethod,
+        DoThrow
+    }
+
+    public class XmlRpcBasicDOSProtector
+    {
+        private readonly XmlRpcMethod _normalMethod;
+        private readonly XmlRpcMethod _throttledMethod;
+        private readonly CircularBuffer<int> _generalRequestTimes; // General request checker
+        private readonly BasicDosProtectorOptions _options;
+        private readonly Dictionary<string, CircularBuffer<int>> _deeperInspection;   // per client request checker
+        private readonly Dictionary<string, int> _tempBlocked;  // blocked list
+        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();
+
+        public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options)
+        {
+            _normalMethod = normalMethod;
+            _throttledMethod = throttledMethod;
+            _generalRequestTimes = new CircularBuffer<int>(options.MaxRequestsInTimeframe + 1,true);
+            _generalRequestTimes.Put(0);
+            _options = options;
+            _deeperInspection = new Dictionary<string, CircularBuffer<int>>();
+            _tempBlocked = new Dictionary<string, int>();
+            _forgetTimer = new System.Timers.Timer();
+            _forgetTimer.Elapsed += delegate
+                                        {
+                                            _forgetTimer.Enabled = false;
+
+                                            List<string> removes = new List<string>();
+                                            _lockSlim.EnterReadLock();
+                                            foreach (string str in _tempBlocked.Keys)
+                                            {
+                                                if (
+                                                    Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),
+                                                                                      _tempBlocked[str]) > 0)
+                                                    removes.Add(str);
+                                            }
+                                            _lockSlim.ExitReadLock();
+                                            lock (_deeperInspection)
+                                            {
+                                                _lockSlim.EnterWriteLock();
+                                                for (int i = 0; i < removes.Count; i++)
+                                                {
+                                                    _tempBlocked.Remove(removes[i]);
+                                                    _deeperInspection.Remove(removes[i]);
+                                                }
+                                                _lockSlim.ExitWriteLock();
+                                            }
+                                            foreach (string str in removes)
+                                            {
+                                                m_log.InfoFormat("[{0}] client: {1} is no longer blocked.",
+                                                                 _options.ReportingName, str);
+                                            }
+                                            _lockSlim.EnterReadLock();
+                                            if (_tempBlocked.Count > 0)
+                                                _forgetTimer.Enabled = true;
+                                            _lockSlim.ExitReadLock();
+                                        };
+                                        
+            _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds;
+        }
+        public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client)
+        {
+            // If these are set like this, this is disabled
+            if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1)
+                return _normalMethod(request, client);
+           
+            string clientstring = GetClientString(request, client);
+
+            _lockSlim.EnterReadLock();
+            if (_tempBlocked.ContainsKey(clientstring))
+            {
+                _lockSlim.ExitReadLock();
+
+                if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
+                    return _throttledMethod(request, client);
+                else
+                    throw new System.Security.SecurityException("Throttled");
+            }
+            _lockSlim.ExitReadLock();
+
+            _generalRequestTimes.Put(Util.EnvironmentTickCount());
+            
+            if (_generalRequestTimes.Size == _generalRequestTimes.Capacity &&
+                (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) <
+                 _options.RequestTimeSpan.TotalMilliseconds))
+            {
+                //Trigger deeper inspection
+                if (DeeperInspection(request, client))
+                    return _normalMethod(request, client);
+                if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod)
+                    return _throttledMethod(request, client);
+                else
+                    throw new System.Security.SecurityException("Throttled");
+            }
+            XmlRpcResponse resp = null;
+           
+            resp = _normalMethod(request, client);
+           
+            return resp;
+        }
+
+        // If the service is getting more hits per expected timeframe then it starts to separate them out by client
+        private bool DeeperInspection(XmlRpcRequest request, IPEndPoint client)
+        {
+            lock (_deeperInspection)
+            {
+                string clientstring = GetClientString(request, client);
+                
+
+                if (_deeperInspection.ContainsKey(clientstring))
+                {
+                    _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
+                    if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity &&
+                        (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) <
+                         _options.RequestTimeSpan.TotalMilliseconds))
+                    {
+                        //Looks like we're over the limit
+                        _lockSlim.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();
+
+                        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);
+                        
+                        return false;
+                    }
+                    //else
+                     //   return true;
+                }
+                else
+                {
+                    _deeperInspection.Add(clientstring, new CircularBuffer<int>(_options.MaxRequestsInTimeframe + 1, true));
+                    _deeperInspection[clientstring].Put(Util.EnvironmentTickCount());
+                    _forgetTimer.Enabled = true;
+                }
+
+            }
+            return true;
+        }
+        private string GetClientString(XmlRpcRequest request, IPEndPoint client)
+        {
+            string clientstring;
+            if (_options.AllowXForwardedFor && request.Params.Count > 3)
+            {
+                object headerstr = request.Params[3];
+                if (headerstr != null && !string.IsNullOrEmpty(headerstr.ToString()))
+                    clientstring = request.Params[3].ToString();
+                else
+                    clientstring = client.Address.ToString();
+            }
+            else
+                clientstring = client.Address.ToString();
+            return clientstring;
+        }
+
+    }
+
+    public class BasicDosProtectorOptions
+    {
+        public int MaxRequestsInTimeframe;
+        public TimeSpan RequestTimeSpan;
+        public TimeSpan ForgetTimeSpan;
+        public bool AllowXForwardedFor;
+        public string ReportingName = "BASICDOSPROTECTOR";
+        public ThrottleAction ThrottledAction = ThrottleAction.DoThrottledMethod;
+    }
+}

+ 10 - 2
OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs

@@ -42,14 +42,22 @@ using log4net;
 
 namespace OpenSim.Region.CoreModules.Avatar.Friends
 {
-    public class FriendsRequestHandler : BaseStreamHandler
+    public class FriendsRequestHandler : BaseStreamHandlerBasicDOSProtector
     {
         private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
 
         private FriendsModule m_FriendsModule;
 
         public FriendsRequestHandler(FriendsModule fmodule)
-                : base("POST", "/friends")
+                : base("POST", "/friends", new BasicDosProtectorOptions()
+                                          {
+                                              AllowXForwardedFor = true,
+                                              ForgetTimeSpan = TimeSpan.FromMinutes(2),
+                                              MaxRequestsInTimeframe = 5,
+                                              ReportingName = "FRIENDSDOSPROTECTOR",
+                                              RequestTimeSpan = TimeSpan.FromSeconds(5),
+                                              ThrottledAction = ThrottleAction.DoThrottledMethod
+                                          })
         {
             m_FriendsModule = fmodule;
         }

+ 20 - 1
OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs

@@ -165,7 +165,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
             regionimage = regionimage.Replace("-", "");
             m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage);
 
-            MainServer.Instance.AddHTTPHandler(regionimage, OnHTTPGetMapImage);
+            MainServer.Instance.AddHTTPHandler(regionimage,
+                new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions()
+                    {
+                        AllowXForwardedFor = false,
+                        ForgetTimeSpan = TimeSpan.FromMinutes(2),
+                        MaxRequestsInTimeframe = 4,
+                        ReportingName = "MAPDOSPROTECTOR",
+                        RequestTimeSpan = TimeSpan.FromSeconds(10),
+                        ThrottledAction = ThrottleAction.DoThrottledMethod
+                    }).Process);
             MainServer.Instance.AddLLSDHandler(
                 "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest);
 
@@ -1081,6 +1090,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
             block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
         }
 
+        public Hashtable OnHTTPThrottled(Hashtable keysvals)
+        {
+            Hashtable reply = new Hashtable();
+            int statuscode = 500;
+            reply["str_response_string"] = "I blocked you! HAHAHAHAHAHAHHAHAH";
+            reply["int_response_code"] = statuscode;
+            reply["content_type"] = "text/plain";
+            return reply;
+        }
+
         public Hashtable OnHTTPGetMapImage(Hashtable keysvals)
         {
             m_log.Debug("[WORLD MAP]: Sending map image jpeg");

+ 10 - 2
OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs

@@ -42,14 +42,22 @@ using OpenSim.Framework.Servers.HttpServer;
 
 namespace OpenSim.Server.Handlers.Asset
 {
-    public class AssetServerGetHandler : BaseStreamHandler
+    public class AssetServerGetHandler : BaseStreamHandlerBasicDOSProtector
     {
         // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
 
         private IAssetService m_AssetService;
 
         public AssetServerGetHandler(IAssetService service) :
-                base("GET", "/assets")
+                base("GET", "/assets",new BasicDosProtectorOptions()
+                                          {
+                                              AllowXForwardedFor = true,
+                                              ForgetTimeSpan = TimeSpan.FromSeconds(2),
+                                              MaxRequestsInTimeframe = 5,
+                                              ReportingName = "ASSETGETDOSPROTECTOR",
+                                              RequestTimeSpan = TimeSpan.FromSeconds(5),
+                                              ThrottledAction = ThrottleAction.DoThrottledMethod
+                                          })
         {
             m_AssetService = service;
         }

+ 11 - 0
OpenSim/Server/Handlers/Login/LLLoginHandlers.cs

@@ -145,6 +145,17 @@ namespace OpenSim.Server.Handlers.Login
             return FailedXMLRPCResponse();
 
         }
+        public XmlRpcResponse HandleXMLRPCLoginBlocked(XmlRpcRequest request, IPEndPoint client)
+        {
+            XmlRpcResponse response = new XmlRpcResponse();
+            Hashtable resp = new Hashtable();
+
+            resp["reason"] = "presence";
+            resp["message"] = "Logins are currently restricted. Please try again later.";
+            resp["login"] = "false";
+            response.Value = resp;
+            return response;
+        }
 
         public XmlRpcResponse HandleXMLRPCSetLoginLevel(XmlRpcRequest request, IPEndPoint remoteClient)
         {

+ 14 - 1
OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs

@@ -44,6 +44,7 @@ namespace OpenSim.Server.Handlers.Login
 
         private ILoginService m_LoginService;
         private bool m_Proxy;
+        private BasicDosProtectorOptions m_DosProtectionOptions;
 
         public LLLoginServiceInConnector(IConfigSource config, IHttpServer server, IScene scene) :
                 base(config, server, String.Empty)
@@ -88,6 +89,16 @@ namespace OpenSim.Server.Handlers.Login
                 throw new Exception(String.Format("No LocalServiceModule for LoginService in config file"));
 
             m_Proxy = serverConfig.GetBoolean("HasProxy", false);
+            m_DosProtectionOptions = new BasicDosProtectorOptions();
+            // Dos Protection Options
+            m_DosProtectionOptions.AllowXForwardedFor = serverConfig.GetBoolean("DOSAllowXForwardedForHeader", false);
+            m_DosProtectionOptions.RequestTimeSpan =
+                TimeSpan.FromMilliseconds(serverConfig.GetInt("DOSRequestTimeFrameMS", 10000));
+            m_DosProtectionOptions.MaxRequestsInTimeframe = serverConfig.GetInt("DOSMaxRequestsInTimeFrame", 5);
+            m_DosProtectionOptions.ForgetTimeSpan =
+                TimeSpan.FromMilliseconds(serverConfig.GetInt("DOSForgiveClientAfterMS", 120000));
+            m_DosProtectionOptions.ReportingName = "LOGINDOSPROTECTION";
+            
 
             return loginService;
         }
@@ -95,7 +106,9 @@ namespace OpenSim.Server.Handlers.Login
         private void InitializeHandlers(IHttpServer server)
         {
             LLLoginHandlers loginHandlers = new LLLoginHandlers(m_LoginService, m_Proxy);
-            server.AddXmlRPCHandler("login_to_simulator", loginHandlers.HandleXMLRPCLogin, false);
+            server.AddXmlRPCHandler("login_to_simulator", 
+                new XmlRpcBasicDOSProtector(loginHandlers.HandleXMLRPCLogin,loginHandlers.HandleXMLRPCLoginBlocked,
+                    m_DosProtectionOptions).Process, false);
             server.AddXmlRPCHandler("set_login_level", loginHandlers.HandleXMLRPCSetLoginLevel, false);
             server.SetDefaultLLSDHandler(loginHandlers.HandleLLSDLogin);
             server.AddWebSocketHandler("/WebSocket/GridLogin", loginHandlers.HandleWebSocketLoginEvents);

+ 16 - 0
ThirdPartyLicenses/CircularBuffer.txt

@@ -0,0 +1,16 @@
+Copyright (c) 2012, Alex Regueiro 
+All rights reserved.
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
+following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
+in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.

+ 19 - 0
bin/Robust.ini.example

@@ -356,6 +356,25 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto
     ;;      'America/Los_Angeles' is used on Linux/Mac systems whilst 'Pacific Standard Time' is used on Windows
     DSTZone = "America/Los_Angeles;Pacific Standard Time"
 
+    ;Basic Login Service Dos Protection Tweaks
+    ;;
+    ;; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true
+    ;; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to
+    ;;     get around this basic DOS protection.
+    ;DOSAllowXForwardedForHeader = false
+    ;;
+    ;; The protector adds up requests during this rolling period of time, default 10 seconds
+    ;DOSRequestTimeFrameMS = 10000
+    ;;
+    ;; The amount of requests in the above timeframe from the same endpoint that triggers protection
+    ;DOSMaxRequestsInTimeFrame = 5
+    ;;
+    ;; The amount of time that a specific endpoint is blocked.    Default 2 minutes.
+    ;DOSForgiveClientAfterMS = 120000
+    ;; 
+    ;; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0.
+
+
 [MapImageService]
     LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService"
 	; Set this if you want to change the default

+ 18 - 1
bin/config-include/StandaloneCommon.ini.example

@@ -117,7 +117,7 @@
     SRV_AssetServerURI = "http://127.0.0.1:9000"
     SRV_ProfileServerURI = "http://127.0.0.1:9000"
     SRV_FriendsServerURI = "http://127.0.0.1:9000"
-    SRV_IMServerURI = "http://127.0.0.1:9000"
+    SRV_IMServerURI = "http://127.0.0.1:9000
 
     ;; For Viewer 2
     MapTileURL = "http://127.0.0.1:9000/"
@@ -150,6 +150,23 @@
     ;AllowedClients = ""
     ;DeniedClients = ""
 
+    ; Basic Login Service Dos Protection Tweaks
+    ; ;
+    ; ; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true
+    ; ; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to
+    ; ;     get around this basic DOS protection.
+    ; DOSAllowXForwardedForHeader = false
+    ; ;
+    ; ; The protector adds up requests during this rolling period of time, default 10 seconds
+    ; DOSRequestTimeFrameMS = 10000
+    ; ;
+    ; ; The amount of requests in the above timeframe from the same endpoint that triggers protection
+    ; DOSMaxRequestsInTimeFrame = 5
+    ; ;
+    ; ; The amount of time that a specific endpoint is blocked.    Default 2 minutes.
+    ; DOSForgiveClientAfterMS = 120000
+    ; ; 
+    ; ; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0.
 
 [FreeswitchService]
     ;; If FreeSWITCH is not being used then you don't need to set any of these parameters