# emacs: -*- mode: python; coding: utf-8; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # Fail2Ban is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import re, time from abc import abstractmethod from .strptime import reGroupDictStrptime, timeRE, getTimePatternRE from ..helpers import getLogger logSys = getLogger(__name__) # check already grouped contains "(", but ignores char "\(" and conditional "(?(id)...)": RE_GROUPED = re.compile(r'(?(?<=^\[))|(?P(?<=\baudit\()))%s)(?:(?(selinux)(?=:\d+\)))|(?(square)(?=\])))" % epochRE self.setRegex(regex, wordBegin=False) ;# already line begin resp. word begin anchored else: regex = r"((?P(?<=^\[))?%s)(?(square)(?=\]))" % epochRE self.setRegex(regex, wordBegin='start', wordEnd=True) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, Unix timestamps are time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. """ if not dateMatch: dateMatch = self.matchDate(line) if dateMatch: v = dateMatch.group(self._grpIdx) # extract part of format which represents seconds since epoch if self._longFrm and len(v) >= 13: if len(v) >= 16 and '.' not in v: v = float(v) / 1000000 else: v = float(v) / 1000 return (float(v), dateMatch) class DatePatternRegex(DateTemplate): """Date template, with regex/pattern Parameters ---------- pattern : str Sets the date templates pattern. Attributes ---------- name regex pattern """ _patternRE, _patternName = getTimePatternRE() _patternRE = re.compile(_patternRE) def __init__(self, pattern=None, **kwargs): super(DatePatternRegex, self).__init__() self._pattern = None if pattern is not None: self.setRegex(pattern, **kwargs) @property def pattern(self): """The pattern used for regex with strptime "%" time fields. This should be a valid regular expression, of which matching string will be extracted from the log line. strptime style "%" fields will be replaced by appropriate regular expressions, or custom regex groups with names as per the strptime fields can also be used instead. """ return self._pattern @pattern.setter def pattern(self, pattern): self.setRegex(pattern) def setRegex(self, pattern, wordBegin=True, wordEnd=True): # original pattern: self._pattern = pattern # if unbound signalled - reset boundaries left and right: if RE_EXLINE_NO_BOUNDS.search(pattern): pattern = RE_EXLINE_NO_BOUNDS.sub('', pattern) wordBegin = wordEnd = False # if explicit given {^LN-BEG} - remove it from pattern and set 'start' in wordBegin: if wordBegin and RE_EXLINE_BOUND_BEG.search(pattern): pattern = RE_EXLINE_BOUND_BEG.sub('', pattern) wordBegin = 'start' try: # wrap to regex: fmt = self._patternRE.sub(r'%(\1)s', pattern) self.name = fmt % self._patternName regex = fmt % timeRE # if expected add (?iu) for "ignore case" and "unicode": if RE_ALPHA_PATTERN.search(pattern): regex = r'(?iu)' + regex super(DatePatternRegex, self).setRegex(regex, wordBegin, wordEnd) except Exception as e: raise TypeError("Failed to set datepattern '%s' (may be an invalid format or unescaped percent char): %s" % (pattern, e)) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. This uses a custom version of strptime, using the named groups from the instances `pattern` property. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: optionally used to correct timezone Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. """ if not dateMatch: dateMatch = self.matchDate(line) if dateMatch: return (reGroupDictStrptime(dateMatch.groupdict(), default_tz=default_tz), dateMatch) class DateTai64n(DateTemplate): """A date template which matches TAI64N formate timestamps. Attributes ---------- name regex """ def __init__(self, wordBegin=False): DateTemplate.__init__(self) self.name = "TAI64N" # We already know the format for TAI64N self.setRegex("@[0-9a-f]{24}", wordBegin=wordBegin) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, since TAI is time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. """ if not dateMatch: dateMatch = self.matchDate(line) if dateMatch: # extract part of format which represents seconds since epoch value = dateMatch.group(1) seconds_since_epoch = value[2:17] # convert seconds from HEX into local time stamp return (int(seconds_since_epoch, 16), dateMatch)