mirror of https://github.com/fail2ban/fail2ban
code review, test case extended;
parent
697da99f8f
commit
4dbc77dbbb
|
@ -559,7 +559,7 @@ class FileFilter(Filter):
|
||||||
#
|
#
|
||||||
# @param path log file path
|
# @param path log file path
|
||||||
|
|
||||||
def addLogPath(self, path, tail = False):
|
def addLogPath(self, path, tail=False):
|
||||||
if self.containsLogPath(path):
|
if self.containsLogPath(path):
|
||||||
logSys.error(path + " already exists")
|
logSys.error(path + " already exists")
|
||||||
else:
|
else:
|
||||||
|
@ -655,7 +655,7 @@ class FileFilter(Filter):
|
||||||
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
|
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
|
||||||
# is created and is added to the FailManager.
|
# is created and is added to the FailManager.
|
||||||
|
|
||||||
def getFailures(self, filename, startTime = None):
|
def getFailures(self, filename, startTime=None):
|
||||||
container = self.getFileContainer(filename)
|
container = self.getFileContainer(filename)
|
||||||
if container is None:
|
if container is None:
|
||||||
logSys.error("Unable to get failures in " + filename)
|
logSys.error("Unable to get failures in " + filename)
|
||||||
|
@ -673,14 +673,19 @@ class FileFilter(Filter):
|
||||||
logSys.exception(e)
|
logSys.exception(e)
|
||||||
return False
|
return False
|
||||||
except OSError, e: # pragma: no cover - Requires implemention error in FileContainer to generate
|
except OSError, e: # pragma: no cover - Requires implemention error in FileContainer to generate
|
||||||
logSys.error("Internal errror in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issues")
|
logSys.error("Internal error in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issues")
|
||||||
logSys.exception(e)
|
logSys.exception(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# prevent completely read of big files first time (after start of service), initial seek to start time using half-interval search algorithm:
|
# prevent completely read of big files first time (after start of service), initial seek to start time using half-interval search algorithm:
|
||||||
if container.getPos() == 0 and startTime is not None:
|
if container.getPos() == 0 and startTime is not None:
|
||||||
# startTime = MyTime.time() - self.getFindTime()
|
try:
|
||||||
self.seekToTime(container, startTime)
|
# startTime = MyTime.time() - self.getFindTime()
|
||||||
|
self.seekToTime(container, startTime)
|
||||||
|
except Exception, e: # pragma: no cover
|
||||||
|
logSys.error("Error during seek to start time in \"%s\"", filename)
|
||||||
|
logSys.exception(e)
|
||||||
|
return False
|
||||||
|
|
||||||
# yoh: has_content is just a bool, so do not expect it to
|
# yoh: has_content is just a bool, so do not expect it to
|
||||||
# change -- loop is exited upon break, and is not entered at
|
# change -- loop is exited upon break, and is not entered at
|
||||||
|
@ -717,7 +722,7 @@ class FileFilter(Filter):
|
||||||
cntr = 0
|
cntr = 0
|
||||||
unixTime = None
|
unixTime = None
|
||||||
lasti = 0
|
lasti = 0
|
||||||
movecntr = 3
|
movecntr = 1
|
||||||
while maxp > minp:
|
while maxp > minp:
|
||||||
i = int(minp + (maxp - minp) / 2)
|
i = int(minp + (maxp - minp) / 2)
|
||||||
pos = container.seek(i)
|
pos = container.seek(i)
|
||||||
|
@ -726,7 +731,8 @@ class FileFilter(Filter):
|
||||||
lncntr = 5;
|
lncntr = 5;
|
||||||
dateTimeMatch = None
|
dateTimeMatch = None
|
||||||
llen = 0
|
llen = 0
|
||||||
i = pos
|
if lastpos == pos:
|
||||||
|
i = pos
|
||||||
while True:
|
while True:
|
||||||
line = container.readline()
|
line = container.readline()
|
||||||
if not line:
|
if not line:
|
||||||
|
@ -763,6 +769,7 @@ class FileFilter(Filter):
|
||||||
lastpos = container.seek(lastFew, False)
|
lastpos = container.seek(lastFew, False)
|
||||||
else:
|
else:
|
||||||
lastpos = container.seek(lastpos, False)
|
lastpos = container.seek(lastpos, False)
|
||||||
|
container.setPos(lastpos)
|
||||||
if logSys.getEffectiveLevel() <= logging.DEBUG:
|
if logSys.getEffectiveLevel() <= logging.DEBUG:
|
||||||
logSys.debug("Position %s from %s, found time %s (%s) within %s seeks", lastpos, fs, unixTime,
|
logSys.debug("Position %s from %s, found time %s (%s) within %s seeks", lastpos, fs, unixTime,
|
||||||
(datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") if unixTime is not None else ''), cntr)
|
(datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") if unixTime is not None else ''), cntr)
|
||||||
|
@ -943,10 +950,6 @@ class DNSUtils:
|
||||||
logSys.warning("Unable to find a corresponding IP address for %s: %s"
|
logSys.warning("Unable to find a corresponding IP address for %s: %s"
|
||||||
% (dns, e))
|
% (dns, e))
|
||||||
return list()
|
return list()
|
||||||
except socket.error, e:
|
|
||||||
logSys.warning("Socket error raised trying to resolve hostname %s: %s"
|
|
||||||
% (dns, e))
|
|
||||||
return list()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def searchIP(text):
|
def searchIP(text):
|
||||||
|
@ -967,7 +970,7 @@ class DNSUtils:
|
||||||
try:
|
try:
|
||||||
socket.inet_aton(s[0])
|
socket.inet_aton(s[0])
|
||||||
return True
|
return True
|
||||||
except socket.error:
|
except socket.error: # pragma: no cover
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -27,7 +27,7 @@ import unittest
|
||||||
import getpass
|
import getpass
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time, datetime
|
||||||
import tempfile
|
import tempfile
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ except ImportError:
|
||||||
|
|
||||||
from ..server.jail import Jail
|
from ..server.jail import Jail
|
||||||
from ..server.filterpoll import FilterPoll
|
from ..server.filterpoll import FilterPoll
|
||||||
from ..server.filter import Filter, FileFilter, DNSUtils
|
from ..server.filter import Filter, FileFilter, FileContainer, DNSUtils
|
||||||
from ..server.failmanager import FailManagerEmpty
|
from ..server.failmanager import FailManagerEmpty
|
||||||
from ..server.mytime import MyTime
|
from ..server.mytime import MyTime
|
||||||
from .utils import setUpMyTime, tearDownMyTime, mtimesleep, LogCaptureTestCase
|
from .utils import setUpMyTime, tearDownMyTime, mtimesleep, LogCaptureTestCase
|
||||||
|
@ -314,6 +314,60 @@ class LogFileFilterPoll(unittest.TestCase):
|
||||||
self.assertTrue(self.filter.isModified(LogFileFilterPoll.FILENAME))
|
self.assertTrue(self.filter.isModified(LogFileFilterPoll.FILENAME))
|
||||||
self.assertFalse(self.filter.isModified(LogFileFilterPoll.FILENAME))
|
self.assertFalse(self.filter.isModified(LogFileFilterPoll.FILENAME))
|
||||||
|
|
||||||
|
def testSeekToTime(self):
|
||||||
|
fname = tempfile.mktemp(prefix='tmp_fail2ban', suffix='.log')
|
||||||
|
tm = lambda time: datetime.datetime.fromtimestamp(time).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
time = 1417512352
|
||||||
|
f = open(fname, 'w')
|
||||||
|
fc = FileContainer(fname, self.filter.getLogEncoding())
|
||||||
|
fc.open()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
try:
|
||||||
|
f.flush()
|
||||||
|
# empty :
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 0)
|
||||||
|
# one entry with exact time:
|
||||||
|
f.write("%s [sshd] error: PAM: failure len 1\n" % tm(time))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
# one entry with smaller time:
|
||||||
|
f.seek(0)
|
||||||
|
f.write("%s [sshd] error: PAM: failure len 1\n" % tm(time - 10))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 0)
|
||||||
|
f.write("%s [sshd] error: PAM: failure len 3 2 1\n" % tm(time - 9))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 0)
|
||||||
|
# add exact time between:
|
||||||
|
f.write("%s [sshd] error: PAM: failure\n" % tm(time - 1))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 110)
|
||||||
|
# stil one exact line:
|
||||||
|
f.write("%s [sshd] error: PAM: Authentication failure\n" % tm(time))
|
||||||
|
f.write("%s [sshd] error: PAM: failure len 1\n" % tm(time))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 110)
|
||||||
|
# add something hereafter:
|
||||||
|
f.write("%s [sshd] error: PAM: failure len 3 2 1\n" % tm(time + 2))
|
||||||
|
f.write("%s [sshd] error: PAM: Authentication failure\n" % tm(time + 3))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 110)
|
||||||
|
# add something hereafter:
|
||||||
|
f.write("%s [sshd] error: PAM: failure\n" % tm(time + 9))
|
||||||
|
f.write("%s [sshd] error: PAM: failure len 3 2 1\n" % tm(time + 9))
|
||||||
|
f.flush()
|
||||||
|
fc.setPos(0); self.filter.seekToTime(fc, time)
|
||||||
|
self.assertEqual(fc.getPos(), 110)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
fc.close()
|
||||||
|
_killfile(f, fname)
|
||||||
|
|
||||||
class LogFileMonitor(LogCaptureTestCase):
|
class LogFileMonitor(LogCaptureTestCase):
|
||||||
"""Few more tests for FilterPoll API
|
"""Few more tests for FilterPoll API
|
||||||
|
|
Loading…
Reference in New Issue