diff --git a/fail2ban/server/banmanager.py b/fail2ban/server/banmanager.py index 1d14ae84..ee9ecae8 100644 --- a/fail2ban/server/banmanager.py +++ b/fail2ban/server/banmanager.py @@ -256,7 +256,7 @@ class BanManager: # we should always use correct time to calculate correct end time (ban time is variable now, # + possible double banning by restore from database and from log file) # so use as lastTime always time from ticket. - return BanTicket(ticket=ticket) + return BanTicket.wrap(ticket) ## # Add a ban ticket. diff --git a/fail2ban/server/failmanager.py b/fail2ban/server/failmanager.py index 1255cf21..6ce9b74e 100644 --- a/fail2ban/server/failmanager.py +++ b/fail2ban/server/failmanager.py @@ -27,7 +27,7 @@ __license__ = "GPL" from threading import Lock import logging -from .ticket import FailTicket +from .ticket import FailTicket, BanTicket from ..helpers import getLogger, BgService # Gets the instance of the logger. @@ -103,13 +103,13 @@ class FailManager: fData.setMatches(matches[-self.maxEntries:]) except KeyError: # not found - already banned - prevent to add failure if comes from observer: - if observed: + if observed or isinstance(ticket, BanTicket): return # if already FailTicket - add it direct, otherwise create (using copy all ticket data): if isinstance(ticket, FailTicket): fData = ticket; else: - fData = FailTicket(ticket=ticket) + fData = FailTicket.wrap(ticket) if count > ticket.getAttempt(): fData.setRetry(count) self.__failList[fid] = fData diff --git a/fail2ban/server/ticket.py b/fail2ban/server/ticket.py index f154ab10..3dc7aba3 100644 --- a/fail2ban/server/ticket.py +++ b/fail2ban/server/ticket.py @@ -33,6 +33,7 @@ logSys = getLogger(__name__) class Ticket(object): + __slots__ = ('_ip', '_flags', '_banCount', '_banTime', '_time', '_data', '_retry', '_lastReset') MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year @@ -59,36 +60,44 @@ class Ticket(object): self._data[k] = v if ticket: # ticket available - copy whole information from ticket: - self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__) + self.update(ticket) + #self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__) def __str__(self): return "%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%r" % \ - (self.__class__.__name__.split('.')[-1], self.__ip, self._time, - self._banTime, self._banCount, - self._data['failures'], self._data.get('matches', [])) + (self.__class__.__name__.split('.')[-1], self._ip, self._time, + self._banTime, self._banCount, + self._data['failures'], self._data.get('matches', [])) def __repr__(self): return str(self) def __eq__(self, other): try: - return self.__ip == other.__ip and \ + return self._ip == other._ip and \ round(self._time, 2) == round(other._time, 2) and \ self._data == other._data except AttributeError: return False + def update(self, ticket): + for n in ticket.__slots__: + v = getattr(ticket, n, None) + if v is not None: + setattr(self, n, v) + + def setIP(self, value): # guarantee using IPAddr instead of unicode, str for the IP if isinstance(value, basestring): value = IPAddr(value) - self.__ip = value + self._ip = value def getID(self): - return self._data.get('fid', self.__ip) + return self._data.get('fid', self._ip) def getIP(self): - return self.__ip + return self._ip def setTime(self, value): self._time = value @@ -204,21 +213,21 @@ class FailTicket(Ticket): def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None): # this class variables: - self.__retry = 0 - self.__lastReset = None + self._retry = 0 + self._lastReset = None # create/copy using default ticket constructor: Ticket.__init__(self, ip, time, matches, data, ticket) # init: if ticket is None: - self.__lastReset = time if time is not None else self.getTime() - if not self.__retry: - self.__retry = self._data['failures']; + self._lastReset = time if time is not None else self.getTime() + if not self._retry: + self._retry = self._data['failures']; def setRetry(self, value): """ Set artificial retry count, normally equal failures / attempt, used in incremental features (BanTimeIncr) to increase retry count for bad IPs """ - self.__retry = value + self._retry = value if not self._data['failures']: self._data['failures'] = 1 if not value: @@ -229,10 +238,10 @@ class FailTicket(Ticket): """ Returns failures / attempt count or artificial retry count increased for bad IPs """ - return max(self.__retry, self._data['failures']) + return max(self._retry, self._data['failures']) def inc(self, matches=None, attempt=1, count=1): - self.__retry += count + self._retry += count self._data['failures'] += attempt if matches: # we should duplicate "matches", because possibly referenced to multiple tickets: @@ -249,15 +258,24 @@ class FailTicket(Ticket): return self._time def getLastReset(self): - return self.__lastReset + return self._lastReset def setLastReset(self, value): - self.__lastReset = value + self._lastReset = value + + @staticmethod + def wrap(o): + o.__class__ = FailTicket + return o ## # Ban Ticket. # # This class extends the Ticket class. It is mainly used by the BanManager. -class BanTicket(Ticket): - pass +class BanTicket(FailTicket): + + @staticmethod + def wrap(o): + o.__class__ = BanTicket + return o