Modified connection DoS limiter.
This commit is contained in:
parent
f41b028d94
commit
4109c86545
1 changed files with 42 additions and 28 deletions
|
|
@ -3,6 +3,8 @@ Sessionhandler for portal sessions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from collections import deque
|
||||||
|
from twisted.internet.task import LoopingCall
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
||||||
|
|
@ -15,9 +17,10 @@ _MAX_CONNECTION_RATE = float(settings.MAX_CONNECTION_RATE)
|
||||||
_MAX_COMMAND_RATE = float(settings.MAX_COMMAND_RATE)
|
_MAX_COMMAND_RATE = float(settings.MAX_COMMAND_RATE)
|
||||||
|
|
||||||
_MIN_TIME_BETWEEN_CONNECTS = 1.0 / float(settings.MAX_CONNECTION_RATE)
|
_MIN_TIME_BETWEEN_CONNECTS = 1.0 / float(settings.MAX_CONNECTION_RATE)
|
||||||
#_MIN_TIME_BETWEEN_COMMANDS = 1.0 / float(settings.MAX_COMMAND_RATE)
|
|
||||||
_ERROR_COMMAND_OVERFLOW = settings.COMMAND_RATE_WARNING
|
_ERROR_COMMAND_OVERFLOW = settings.COMMAND_RATE_WARNING
|
||||||
|
|
||||||
|
_CONNECTION_QUEUE = deque()
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Portal-SessionHandler class
|
# Portal-SessionHandler class
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -44,8 +47,8 @@ class PortalSessionHandler(SessionHandler):
|
||||||
self.uptime = time()
|
self.uptime = time()
|
||||||
self.connection_time = 0
|
self.connection_time = 0
|
||||||
|
|
||||||
self.connection_counter = 0
|
self.connection_last = time()
|
||||||
self.connection_counter_reset = time()
|
self.connection_task = None
|
||||||
self.command_counter = 0
|
self.command_counter = 0
|
||||||
self.command_counter_reset = time()
|
self.command_counter_reset = time()
|
||||||
|
|
||||||
|
|
@ -73,38 +76,43 @@ class PortalSessionHandler(SessionHandler):
|
||||||
tester with a large number of connector dummies.
|
tester with a large number of connector dummies.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
session.server_connected = False
|
global _CONNECTION_QUEUE
|
||||||
|
|
||||||
if not session.sessid:
|
if session:
|
||||||
# only assign if we were not delayed
|
# assign if we are first-connectors
|
||||||
self.latest_sessid += 1
|
self.latest_sessid += 1
|
||||||
session.sessid = self.latest_sessid
|
session.sessid = self.latest_sessid
|
||||||
|
session.server_connected = False
|
||||||
|
_CONNECTION_QUEUE.appendleft(session)
|
||||||
|
if len(_CONNECTION_QUEUE) > 1:
|
||||||
|
session.data_out("%s DoS protection is active. You are queued to connect in %g seconds ..." % (
|
||||||
|
settings.SERVERNAME,
|
||||||
|
len(_CONNECTION_QUEUE)*_MIN_TIME_BETWEEN_CONNECTS))
|
||||||
now = time()
|
now = time()
|
||||||
self.connection_counter += 1
|
if (now - self.connection_last < _MIN_TIME_BETWEEN_CONNECTS) or not self.portal.amp_protocol:
|
||||||
|
if not session or not self.connection_task:
|
||||||
if self.connection_counter > _MAX_CONNECTION_RATE:
|
self.connection_task = reactor.callLater(_MIN_TIME_BETWEEN_CONNECTS, self.connect, None)
|
||||||
if now - self.connection_counter_reset < 1.0:
|
self.connection_last = now
|
||||||
# our rate is higher than MAX_CONNECTION_RATE / second
|
|
||||||
reactor.callLater(_MIN_TIME_BETWEEN_CONNECTS, self.connect, session)
|
|
||||||
self.connection_counter = 0
|
|
||||||
self.last_connetion_counter_reset = now
|
|
||||||
return
|
return
|
||||||
|
elif not session:
|
||||||
|
if _CONNECTION_QUEUE:
|
||||||
|
# keep launching tasks until queue is empty
|
||||||
|
self.connection_task = reactor.callLater(_MIN_TIME_BETWEEN_CONNECTS, self.connect, None)
|
||||||
|
else:
|
||||||
|
self.connection_task = None
|
||||||
|
self.connection_last = now
|
||||||
|
|
||||||
if not self.portal.amp_protocol:
|
if _CONNECTION_QUEUE:
|
||||||
# if amp is not yet ready (usually because the server is
|
# sync with server-side
|
||||||
# booting up), try again a little later
|
session = _CONNECTION_QUEUE.pop()
|
||||||
reactor.callLater(0.5, self.connect, session)
|
sessdata = session.get_sync_data()
|
||||||
return
|
|
||||||
|
|
||||||
# sync with server-side
|
self.sessions[session.sessid] = session
|
||||||
sessdata = session.get_sync_data()
|
session.server_connected = True
|
||||||
self.sessions[session.sessid] = session
|
#print "connecting", session.sessid, " number:", len(self.sessions)
|
||||||
session.server_connected = True
|
self.portal.amp_protocol.call_remote_ServerAdmin(session.sessid,
|
||||||
#print "connecting", session.sessid, " number:", len(self.sessions)
|
operation=PCONN,
|
||||||
self.portal.amp_protocol.call_remote_ServerAdmin(session.sessid,
|
data=sessdata)
|
||||||
operation=PCONN,
|
|
||||||
data=sessdata)
|
|
||||||
|
|
||||||
def sync(self, session):
|
def sync(self, session):
|
||||||
"""
|
"""
|
||||||
|
|
@ -144,6 +152,12 @@ class PortalSessionHandler(SessionHandler):
|
||||||
session (PortalSession): Session to disconnect.
|
session (PortalSession): Session to disconnect.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
global _CONNECTION_QUEUE
|
||||||
|
if session in _CONNECTION_QUEUE:
|
||||||
|
# connection was already dropped before we had time
|
||||||
|
# to forward this to the Server, so now we just remove it.
|
||||||
|
_CONNECTION_QUEUE.remove(session)
|
||||||
|
return
|
||||||
sessid = session.sessid
|
sessid = session.sessid
|
||||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||||
operation=PDISCONN)
|
operation=PDISCONN)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue