diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index 9428e750..9edf0b69 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -559,7 +559,7 @@ class FileFilter(Filter): # # @param path log file path - def addLogPath(self, path, tail = False): + def addLogPath(self, path, tail=False): if self.containsLogPath(path): logSys.error(path + " already exists") else: @@ -655,7 +655,7 @@ class FileFilter(Filter): # MyTime.time()-self.findTime. When a failure is detected, a FailTicket # is created and is added to the FailManager. - def getFailures(self, filename, startTime = None): + def getFailures(self, filename, startTime=None): container = self.getFileContainer(filename) if container is None: logSys.error("Unable to get failures in " + filename) @@ -673,14 +673,19 @@ class FileFilter(Filter): logSys.exception(e) return False 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) return False # 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: - # startTime = MyTime.time() - self.getFindTime() - self.seekToTime(container, startTime) + try: + # 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 # change -- loop is exited upon break, and is not entered at @@ -717,7 +722,7 @@ class FileFilter(Filter): cntr = 0 unixTime = None lasti = 0 - movecntr = 3 + movecntr = 1 while maxp > minp: i = int(minp + (maxp - minp) / 2) pos = container.seek(i) @@ -726,7 +731,8 @@ class FileFilter(Filter): lncntr = 5; dateTimeMatch = None llen = 0 - i = pos + if lastpos == pos: + i = pos while True: line = container.readline() if not line: @@ -763,6 +769,7 @@ class FileFilter(Filter): lastpos = container.seek(lastFew, False) else: lastpos = container.seek(lastpos, False) + container.setPos(lastpos) if logSys.getEffectiveLevel() <= logging.DEBUG: 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) @@ -943,10 +950,6 @@ class DNSUtils: logSys.warning("Unable to find a corresponding IP address for %s: %s" % (dns, e)) return list() - except socket.error, e: - logSys.warning("Socket error raised trying to resolve hostname %s: %s" - % (dns, e)) - return list() @staticmethod def searchIP(text): @@ -967,7 +970,7 @@ class DNSUtils: try: socket.inet_aton(s[0]) return True - except socket.error: + except socket.error: # pragma: no cover return False @staticmethod diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index a462cf80..26f1801a 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -27,7 +27,7 @@ import unittest import getpass import os import sys -import time +import time, datetime import tempfile import uuid @@ -38,7 +38,7 @@ except ImportError: from ..server.jail import Jail 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.mytime import MyTime from .utils import setUpMyTime, tearDownMyTime, mtimesleep, LogCaptureTestCase @@ -314,6 +314,60 @@ class LogFileFilterPoll(unittest.TestCase): self.assertTrue(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): """Few more tests for FilterPoll API