instructions
This commit is contained in:
392
fail2ban-master/fail2ban/server/strptime.py
Normal file
392
fail2ban-master/fail2ban/server/strptime.py
Normal file
@@ -0,0 +1,392 @@
|
||||
# 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.
|
||||
|
||||
import re
|
||||
import time
|
||||
import calendar
|
||||
import datetime
|
||||
from _strptime import LocaleTime, TimeRE, _calc_julian_from_U_or_W
|
||||
|
||||
from .mytime import MyTime
|
||||
|
||||
locale_time = LocaleTime()
|
||||
|
||||
TZ_ABBR_RE = r"[A-Z](?:[A-Z]{2,4})?"
|
||||
FIXED_OFFSET_TZ_RE = re.compile(r"(%s)?([+-][01]\d(?::?\d{2})?)?$" % (TZ_ABBR_RE,))
|
||||
|
||||
timeRE = TimeRE()
|
||||
|
||||
# %k - one- or two-digit number giving the hour of the day (0-23) on a 24-hour clock,
|
||||
# (corresponds %H, but allows space if not zero-padded).
|
||||
# %l - one- or two-digit number giving the hour of the day (12-11) on a 12-hour clock,
|
||||
# (corresponds %I, but allows space if not zero-padded).
|
||||
timeRE['k'] = r" ?(?P<H>[0-2]?\d)"
|
||||
timeRE['l'] = r" ?(?P<I>1?\d)"
|
||||
|
||||
# TODO: because python currently does not support mixing of case-sensitive with case-insensitive matching,
|
||||
# check how TZ (in uppercase) can be combined with %a/%b etc. (that are currently case-insensitive),
|
||||
# to avoid invalid date-time recognition in strings like '11-Aug-2013 03:36:11.372 error ...'
|
||||
# with wrong TZ "error", which is at least not backwards compatible.
|
||||
# Hence %z currently match literal Z|UTC|GMT only (and offset-based), and %Exz - all zone abbreviations.
|
||||
timeRE['Z'] = r"(?P<Z>Z|[A-Z]{3,5})"
|
||||
timeRE['z'] = r"(?P<z>Z|UTC|GMT|[+-][01]\d(?::?\d{2})?)"
|
||||
|
||||
# Note: this extended tokens supported zone abbreviations, but it can parse 1 or 3-5 char(s) in lowercase,
|
||||
# see todo above. Don't use them in default date-patterns (if not anchored, few precise resp. optional).
|
||||
timeRE['ExZ'] = r"(?P<Z>%s)" % (TZ_ABBR_RE,)
|
||||
timeRE['Exz'] = r"(?P<z>(?:%s)?[+-][01]\d(?::?\d{2})?|%s)" % (TZ_ABBR_RE, TZ_ABBR_RE)
|
||||
|
||||
# overwrite default patterns, since they can be non-optimal:
|
||||
timeRE['d'] = r"(?P<d>[1-2]\d|[0 ]?[1-9]|3[0-1])"
|
||||
timeRE['m'] = r"(?P<m>0?[1-9]|1[0-2])"
|
||||
timeRE['Y'] = r"(?P<Y>\d{4})"
|
||||
timeRE['H'] = r"(?P<H>[0-1]?\d|2[0-3])"
|
||||
timeRE['M'] = r"(?P<M>[0-5]?\d)"
|
||||
timeRE['S'] = r"(?P<S>[0-5]?\d|6[0-1])"
|
||||
|
||||
# Extend built-in TimeRE with some exact patterns
|
||||
# exact two-digit patterns:
|
||||
timeRE['Exd'] = r"(?P<d>[1-2]\d|0[1-9]|3[0-1])"
|
||||
timeRE['Exm'] = r"(?P<m>0[1-9]|1[0-2])"
|
||||
timeRE['ExH'] = r"(?P<H>[0-1]\d|2[0-3])"
|
||||
timeRE['Exk'] = r" ?(?P<H>[0-1]?\d|2[0-3])"
|
||||
timeRE['Exl'] = r" ?(?P<I>1[0-2]|\d)"
|
||||
timeRE['ExM'] = r"(?P<M>[0-5]\d)"
|
||||
timeRE['ExS'] = r"(?P<S>[0-5]\d|6[0-1])"
|
||||
|
||||
def _updateTimeRE():
|
||||
def _getYearCentRE(cent=(0,3), distance=3, now=(MyTime.now(), MyTime.alternateNow)):
|
||||
""" Build century regex for last year and the next years (distance).
|
||||
|
||||
Thereby respect possible run in the test-cases (alternate date used there)
|
||||
"""
|
||||
cent = lambda year, f=cent[0], t=cent[1]: str(year)[f:t]
|
||||
def grp(exprset):
|
||||
c = None
|
||||
if len(exprset) > 1:
|
||||
for i in exprset:
|
||||
if c is None or i[0:-1] == c:
|
||||
c = i[0:-1]
|
||||
else:
|
||||
c = None
|
||||
break
|
||||
if not c:
|
||||
for i in exprset:
|
||||
if c is None or i[0] == c:
|
||||
c = i[0]
|
||||
else:
|
||||
c = None
|
||||
break
|
||||
if c:
|
||||
return "%s%s" % (c, grp([i[len(c):] for i in exprset]))
|
||||
return ("(?:%s)" % "|".join(exprset) if len(exprset[0]) > 1 else "[%s]" % "".join(exprset)) \
|
||||
if len(exprset) > 1 else "".join(exprset)
|
||||
exprset = set( cent(now[0].year + i) for i in (-1, distance) )
|
||||
if len(now) > 1 and now[1]:
|
||||
exprset |= set( cent(now[1].year + i) for i in range(-1, now[0].year-now[1].year+1, distance) )
|
||||
return grp(sorted(list(exprset)))
|
||||
|
||||
# more precise year patterns, within same century of last year and
|
||||
# the next 3 years (for possible long uptime of fail2ban); thereby
|
||||
# consider possible run in the test-cases (alternate date used there),
|
||||
# so accept years: 20xx (from test-date or 2001 up to current century)
|
||||
timeRE['ExY'] = r"(?P<Y>%s\d)" % _getYearCentRE(cent=(0,3), distance=3,
|
||||
now=(datetime.datetime.now(), datetime.datetime.fromtimestamp(
|
||||
min(MyTime.alternateNowTime or 978393600, 978393600))
|
||||
)
|
||||
)
|
||||
timeRE['Exy'] = r"(?P<y>\d{2})"
|
||||
|
||||
_updateTimeRE()
|
||||
|
||||
def getTimePatternRE():
|
||||
keys = list(timeRE.keys())
|
||||
patt = (r"%%(%%|%s|[%s])" % (
|
||||
"|".join([k for k in keys if len(k) > 1]),
|
||||
"".join([k for k in keys if len(k) == 1]),
|
||||
))
|
||||
names = {
|
||||
'a': "DAY", 'A': "DAYNAME", 'b': "MON", 'B': "MONTH", 'd': "Day",
|
||||
'H': "24hour", 'I': "12hour", 'j': "Yearday", 'm': "Month",
|
||||
'M': "Minute", 'p': "AMPM", 'S': "Second", 'U': "Yearweek",
|
||||
'w': "Weekday", 'W': "Yearweek", 'y': 'Year2', 'Y': "Year", '%': "%",
|
||||
'z': "Zone offset", 'f': "Microseconds", 'Z': "Zone name",
|
||||
}
|
||||
for key in set(keys) - set(names): # may not have them all...
|
||||
if key.startswith('Ex'):
|
||||
kn = names.get(key[2:])
|
||||
if kn:
|
||||
names[key] = "Ex" + kn
|
||||
continue
|
||||
names[key] = "%%%s" % key
|
||||
return (patt, names)
|
||||
|
||||
|
||||
def validateTimeZone(tz):
|
||||
"""Validate a timezone and convert it to offset if it can (offset-based TZ).
|
||||
|
||||
For now this accepts the UTC[+-]hhmm format (UTC has aliases GMT/Z and optional).
|
||||
Additionally it accepts all zone abbreviations mentioned below in TZ_STR.
|
||||
Note that currently this zone abbreviations are offset-based and used fixed
|
||||
offset without automatically DST-switch (if CET used then no automatically CEST-switch).
|
||||
|
||||
In the future, it may be extended for named time zones (such as Europe/Paris)
|
||||
present on the system, if a suitable tz library is present (pytz).
|
||||
"""
|
||||
if tz is None:
|
||||
return None
|
||||
m = FIXED_OFFSET_TZ_RE.match(tz)
|
||||
if m is None:
|
||||
raise ValueError("Unknown or unsupported time zone: %r" % tz)
|
||||
tz = m.groups()
|
||||
return zone2offset(tz, 0)
|
||||
|
||||
def zone2offset(tz, dt):
|
||||
"""Return the proper offset, in minutes according to given timezone at a given time.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tz: symbolic timezone or offset (for now only TZA?([+-]hh:?mm?)? is supported,
|
||||
as value are accepted:
|
||||
int offset;
|
||||
string in form like 'CET+0100' or 'UTC' or '-0400';
|
||||
tuple (or list) in form (zone name, zone offset);
|
||||
dt: datetime instance for offset computation (currently unused)
|
||||
"""
|
||||
if isinstance(tz, int):
|
||||
return tz
|
||||
if isinstance(tz, str):
|
||||
return validateTimeZone(tz)
|
||||
tz, tzo = tz
|
||||
if tzo is None or tzo == '': # without offset
|
||||
return TZ_ABBR_OFFS[tz]
|
||||
if len(tzo) <= 3: # short tzo (hh only)
|
||||
# [+-]hh --> [+-]hh*60
|
||||
return TZ_ABBR_OFFS[tz] + int(tzo)*60
|
||||
if tzo[3] != ':':
|
||||
# [+-]hhmm --> [+-]1 * (hh*60 + mm)
|
||||
return TZ_ABBR_OFFS[tz] + (-1 if tzo[0] == '-' else 1) * (int(tzo[1:3])*60 + int(tzo[3:5]))
|
||||
else:
|
||||
# [+-]hh:mm --> [+-]1 * (hh*60 + mm)
|
||||
return TZ_ABBR_OFFS[tz] + (-1 if tzo[0] == '-' else 1) * (int(tzo[1:3])*60 + int(tzo[4:6]))
|
||||
|
||||
def reGroupDictStrptime(found_dict, msec=False, default_tz=None):
|
||||
"""Return time from dictionary of strptime fields
|
||||
|
||||
This is tweaked from python built-in _strptime.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
found_dict : dict
|
||||
Dictionary where keys represent the strptime fields, and values the
|
||||
respective value.
|
||||
default_tz : default timezone to apply if nothing relevant is in found_dict
|
||||
(may be a non-fixed one in the future)
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Unix time stamp.
|
||||
"""
|
||||
|
||||
now = \
|
||||
year = month = day = tzoffset = \
|
||||
weekday = julian = week_of_year = None
|
||||
hour = minute = second = fraction = 0
|
||||
for key, val in found_dict.items():
|
||||
if val is None: continue
|
||||
# Directives not explicitly handled below:
|
||||
# c, x, X
|
||||
# handled by making out of other directives
|
||||
# U, W
|
||||
# worthless without day of the week
|
||||
if key == 'y':
|
||||
year = int(val)
|
||||
# Fail2ban year should be always in the current century (>= 2000)
|
||||
if year <= 2000:
|
||||
year += 2000
|
||||
elif key == 'Y':
|
||||
year = int(val)
|
||||
elif key == 'm':
|
||||
month = int(val)
|
||||
elif key == 'B':
|
||||
month = locale_time.f_month.index(val.lower())
|
||||
elif key == 'b':
|
||||
month = locale_time.a_month.index(val.lower())
|
||||
elif key == 'd':
|
||||
day = int(val)
|
||||
elif key == 'H':
|
||||
hour = int(val)
|
||||
elif key == 'I':
|
||||
hour = int(val)
|
||||
ampm = found_dict.get('p', '').lower()
|
||||
# If there was no AM/PM indicator, we'll treat this like AM
|
||||
if ampm in ('', locale_time.am_pm[0]):
|
||||
# We're in AM so the hour is correct unless we're
|
||||
# looking at 12 midnight.
|
||||
# 12 midnight == 12 AM == hour 0
|
||||
if hour == 12:
|
||||
hour = 0
|
||||
elif ampm == locale_time.am_pm[1]:
|
||||
# We're in PM so we need to add 12 to the hour unless
|
||||
# we're looking at 12 noon.
|
||||
# 12 noon == 12 PM == hour 12
|
||||
if hour != 12:
|
||||
hour += 12
|
||||
elif key == 'M':
|
||||
minute = int(val)
|
||||
elif key == 'S':
|
||||
second = int(val)
|
||||
elif key == 'f':
|
||||
if msec: # pragma: no cover - currently unused
|
||||
s = val
|
||||
# Pad to always return microseconds.
|
||||
s += "0" * (6 - len(s))
|
||||
fraction = int(s)
|
||||
elif key == 'A':
|
||||
weekday = locale_time.f_weekday.index(val.lower())
|
||||
elif key == 'a':
|
||||
weekday = locale_time.a_weekday.index(val.lower())
|
||||
elif key == 'w':
|
||||
weekday = int(val) - 1
|
||||
if weekday < 0: weekday = 6
|
||||
elif key == 'j':
|
||||
julian = int(val)
|
||||
elif key in ('U', 'W'):
|
||||
week_of_year = int(val)
|
||||
# U starts week on Sunday, W - on Monday
|
||||
week_of_year_start = 6 if key == 'U' else 0
|
||||
elif key in ('z', 'Z'):
|
||||
z = val
|
||||
if z in ("Z", "UTC", "GMT"):
|
||||
tzoffset = 0
|
||||
else:
|
||||
tzoffset = zone2offset(z, 0); # currently offset-based only
|
||||
|
||||
# Fail2Ban will assume it's this year
|
||||
assume_year = False
|
||||
if year is None:
|
||||
if not now: now = MyTime.now()
|
||||
year = now.year
|
||||
assume_year = True
|
||||
if month is None or day is None:
|
||||
# If we know the week of the year and what day of that week, we can figure
|
||||
# out the Julian day of the year.
|
||||
if julian is None and week_of_year is not None and weekday is not None:
|
||||
julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
|
||||
(week_of_year_start == 0))
|
||||
# Cannot pre-calculate datetime.datetime() since can change in Julian
|
||||
# calculation and thus could have different value for the day of the week
|
||||
# calculation.
|
||||
if julian is not None:
|
||||
datetime_result = datetime.datetime.fromordinal((julian - 1) + datetime.datetime(year, 1, 1).toordinal())
|
||||
year = datetime_result.year
|
||||
month = datetime_result.month
|
||||
day = datetime_result.day
|
||||
|
||||
# Fail2Ban assume today
|
||||
assume_today = False
|
||||
if month is None and day is None:
|
||||
if not now: now = MyTime.now()
|
||||
month = now.month
|
||||
day = now.day
|
||||
assume_today = True
|
||||
|
||||
# Actually create date
|
||||
date_result = datetime.datetime(
|
||||
year, month, day, hour, minute, second, fraction)
|
||||
# Correct timezone if not supplied in the log linge
|
||||
if tzoffset is None and default_tz is not None:
|
||||
tzoffset = zone2offset(default_tz, date_result)
|
||||
# Add timezone info
|
||||
if tzoffset is not None:
|
||||
date_result -= datetime.timedelta(seconds=tzoffset * 60)
|
||||
|
||||
if assume_today:
|
||||
if not now: now = MyTime.now()
|
||||
if date_result > now:
|
||||
# Rollover at midnight, could mean it's yesterday...
|
||||
date_result -= datetime.timedelta(days=1)
|
||||
if assume_year:
|
||||
if not now: now = MyTime.now()
|
||||
if date_result > now + datetime.timedelta(days=1): # ignore by timezone issues (+24h)
|
||||
# assume last year - also reset month and day as it's not yesterday...
|
||||
date_result = date_result.replace(
|
||||
year=year-1, month=month, day=day)
|
||||
|
||||
# make time:
|
||||
if tzoffset is not None:
|
||||
tm = calendar.timegm(date_result.utctimetuple())
|
||||
else:
|
||||
tm = time.mktime(date_result.timetuple())
|
||||
if msec: # pragma: no cover - currently unused
|
||||
tm += fraction/1000000.0
|
||||
return tm
|
||||
|
||||
|
||||
TZ_ABBR_OFFS = {'':0, None:0}
|
||||
TZ_STR = '''
|
||||
-12 Y
|
||||
-11 X NUT SST
|
||||
-10 W CKT HAST HST TAHT TKT
|
||||
-9 V AKST GAMT GIT HADT HNY
|
||||
-8 U AKDT CIST HAY HNP PST PT
|
||||
-7 T HAP HNR MST PDT
|
||||
-6 S CST EAST GALT HAR HNC MDT
|
||||
-5 R CDT COT EASST ECT EST ET HAC HNE PET
|
||||
-4 Q AST BOT CLT COST EDT FKT GYT HAE HNA PYT
|
||||
-3 P ADT ART BRT CLST FKST GFT HAA PMST PYST SRT UYT WGT
|
||||
-2 O BRST FNT PMDT UYST WGST
|
||||
-1 N AZOT CVT EGT
|
||||
0 Z EGST GMT UTC WET WT
|
||||
1 A CET DFT WAT WEDT WEST
|
||||
2 B CAT CEDT CEST EET SAST WAST
|
||||
3 C EAT EEDT EEST IDT MSK
|
||||
4 D AMT AZT GET GST KUYT MSD MUT RET SAMT SCT
|
||||
5 E AMST AQTT AZST HMT MAWT MVT PKT TFT TJT TMT UZT YEKT
|
||||
6 F ALMT BIOT BTT IOT KGT NOVT OMST YEKST
|
||||
7 G CXT DAVT HOVT ICT KRAT NOVST OMSST THA WIB
|
||||
8 H ACT AWST BDT BNT CAST HKT IRKT KRAST MYT PHT SGT ULAT WITA WST
|
||||
9 I AWDT IRKST JST KST PWT TLT WDT WIT YAKT
|
||||
10 K AEST ChST PGT VLAT YAKST YAPT
|
||||
11 L AEDT LHDT MAGT NCT PONT SBT VLAST VUT
|
||||
12 M ANAST ANAT FJT GILT MAGST MHT NZST PETST PETT TVT WFT
|
||||
13 FJST NZDT
|
||||
11.5 NFT
|
||||
10.5 ACDT LHST
|
||||
9.5 ACST
|
||||
6.5 CCT MMT
|
||||
5.75 NPT
|
||||
5.5 SLT
|
||||
4.5 AFT IRDT
|
||||
3.5 IRST
|
||||
-2.5 HAT NDT
|
||||
-3.5 HNT NST NT
|
||||
-4.5 HLV VET
|
||||
-9.5 MART MIT
|
||||
'''
|
||||
|
||||
def _init_TZ_ABBR():
|
||||
"""Initialized TZ_ABBR_OFFS dictionary (TZ -> offset in minutes)"""
|
||||
for tzline in map(str.split, TZ_STR.split('\n')):
|
||||
if not len(tzline): continue
|
||||
tzoffset = int(float(tzline[0]) * 60)
|
||||
for tz in tzline[1:]:
|
||||
TZ_ABBR_OFFS[tz] = tzoffset
|
||||
|
||||
_init_TZ_ABBR()
|
||||
Reference in New Issue
Block a user