diff --git a/config/action.d/badips.py b/config/action.d/badips.py index 025289ca..4bc879a1 100644 --- a/config/action.d/badips.py +++ b/config/action.d/badips.py @@ -80,14 +80,17 @@ class BadIPsAction(ActionBase): If invalid `category`, `score`, `banaction` or `updateperiod`. """ + TIMEOUT = 10 _badips = "http://www.badips.com" def _Request(self, url, **argv): return Request(url, headers={'User-Agent': self.agent}, **argv) def __init__(self, jail, name, category, score=3, age="24h", key=None, - banaction=None, bancategory=None, bankey=None, updateperiod=900, agent="Fail2Ban"): + banaction=None, bancategory=None, bankey=None, updateperiod=900, agent="Fail2Ban", + timeout=TIMEOUT): super(BadIPsAction, self).__init__(jail, name) + self.timeout = timeout self.agent = agent self.category = category self.score = score @@ -119,7 +122,7 @@ class BadIPsAction(ActionBase): """ try: response = urlopen( - self._Request("/".join([self._badips, "get", "categories"])), None, 3) + self._Request("/".join([self._badips, "get", "categories"])), timeout=self.timeout) except HTTPError as response: messages = json.loads(response.read().decode('utf-8')) self._logSys.error( @@ -173,7 +176,7 @@ class BadIPsAction(ActionBase): urlencode({'age': age})]) if key: url = "&".join([url, urlencode({'key': key})]) - response = urlopen(self._Request(url)) + response = urlopen(self._Request(url), timeout=self.timeout) except HTTPError as response: messages = json.loads(response.read().decode('utf-8')) self._logSys.error( @@ -358,7 +361,7 @@ class BadIPsAction(ActionBase): url = "/".join([self._badips, "add", self.category, aInfo['ip']]) if self.key: url = "?".join([url, urlencode({'key': self.key})]) - response = urlopen(self._Request(url)) + response = urlopen(self._Request(url), timeout=self.timeout) except HTTPError as response: messages = json.loads(response.read().decode('utf-8')) self._logSys.error( diff --git a/fail2ban/server/failregex.py b/fail2ban/server/failregex.py index d343dbe0..12b8986a 100644 --- a/fail2ban/server/failregex.py +++ b/fail2ban/server/failregex.py @@ -274,7 +274,7 @@ class FailRegex(Regex): for grp in groups: try: fid = self._matchCache.group(grp) - except IndexError: + except (IndexError, KeyError): continue if fid is not None: break diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index 8f5dda21..53bf3bb7 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -537,21 +537,17 @@ class Filter(JailThread): # failure-id: fid = fail.get('fid') # ip-address or host: - host = fail.get('ip4') + host = fail.get('ip4') or fail.get('ip6') if host is not None: raw = True else: - host = fail.get('ip6') - if host is not None: - raw = True - else: - host = fail.get('dns') - if host is None: - # if no failure-id also (obscure case, wrong regex), throw error inside getFailID: - if fid is None: - fid = failRegex.getFailID() - host = fid - cidr = IPAddr.CIDR_RAW + host = fail.get('dns') + if host is None: + # if no failure-id also (obscure case, wrong regex), throw error inside getFailID: + if fid is None: + fid = failRegex.getFailID() + host = fid + cidr = IPAddr.CIDR_RAW # if raw - add single ip or failure-id, # otherwise expand host to multiple ips using dns (or ignore it if not valid): if raw: diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index 61c1ba68..cca63535 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -85,10 +85,7 @@ class DNSUtils: return v # retrieve name try: - if not isinstance(ip, IPAddr): - v = socket.gethostbyaddr(ip)[0] - else: - v = socket.gethostbyaddr(ip.ntoa)[0] + v = socket.gethostbyaddr(ip)[0] except socket.error, e: logSys.debug("Unable to find a name for the IP %s: %s", ip, e) v = None @@ -359,7 +356,7 @@ class IPAddr(object): if not suffix: suffix = "in-addr.arpa." elif self.isIPv6: - exploded_ip = self.hexdump() + exploded_ip = self.hexdump if not suffix: suffix = "ip6.arpa." else: diff --git a/fail2ban/tests/action_d/test_badips.py b/fail2ban/tests/action_d/test_badips.py index 74594420..97dabada 100644 --- a/fail2ban/tests/action_d/test_badips.py +++ b/fail2ban/tests/action_d/test_badips.py @@ -39,6 +39,7 @@ if sys.version_info >= (2,7): self.jail.actions.add("badips", pythonModule, initOpts={ 'category': "ssh", 'banaction': "test", + 'timeout': (3 if unittest.F2B.fast else 30), }) self.action = self.jail.actions["badips"] diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 4f5dbb55..dd3468ed 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -1353,6 +1353,42 @@ class DNSUtilsNetworkTests(unittest.TestCase): """Call before every test case.""" unittest.F2B.SkipIfNoNetwork() + def test_IPAddr(self): + self.assertTrue(IPAddr('192.0.2.1').isIPv4) + self.assertTrue(IPAddr('2001:DB8::').isIPv6) + + def test_IPAddr_Raw(self): + # raw string: + r = IPAddr('xxx', IPAddr.CIDR_RAW) + self.assertFalse(r.isIPv4) + self.assertFalse(r.isIPv6) + self.assertTrue(r.isValid) + self.assertEqual(r, 'xxx') + self.assertEqual('xxx', str(r)) + self.assertNotEqual(r, IPAddr('xxx')) + # raw (not IP, for example host:port as string): + r = IPAddr('1:2', IPAddr.CIDR_RAW) + self.assertFalse(r.isIPv4) + self.assertFalse(r.isIPv6) + self.assertTrue(r.isValid) + self.assertEqual(r, '1:2') + self.assertEqual('1:2', str(r)) + self.assertNotEqual(r, IPAddr('1:2')) + # raw vs ip4 (raw is not an ip): + r = IPAddr('93.184.0.1', IPAddr.CIDR_RAW) + ip4 = IPAddr('93.184.0.1') + self.assertNotEqual(ip4, r) + self.assertNotEqual(r, ip4) + self.assertTrue(r < ip4) + self.assertTrue(r < ip4) + # raw vs ip6 (raw is not an ip): + r = IPAddr('1::2', IPAddr.CIDR_RAW) + ip6 = IPAddr('1::2') + self.assertNotEqual(ip6, r) + self.assertNotEqual(r, ip6) + self.assertTrue(r < ip6) + self.assertTrue(r < ip6) + def testUseDns(self): res = DNSUtils.textToIp('www.example.com', 'no') self.assertEqual(res, []) @@ -1377,14 +1413,25 @@ class DNSUtilsNetworkTests(unittest.TestCase): self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) else: self.assertEqual(res, []) + # pure ips: + for s in ('93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'): + ips = DNSUtils.textToIp(s, 'yes') + self.assertEqual(ips, [s]) + self.assertTrue(isinstance(ips[0], IPAddr)) def testIpToName(self): unittest.F2B.SkipIfNoNetwork() res = DNSUtils.ipToName('8.8.4.4') self.assertEqual(res, 'google-public-dns-b.google.com') + # same as above, but with IPAddr: + res = DNSUtils.ipToName(IPAddr('8.8.4.4')) + self.assertEqual(res, 'google-public-dns-b.google.com') # invalid ip (TEST-NET-1 according to RFC 5737) res = DNSUtils.ipToName('192.0.2.0') self.assertEqual(res, None) + # invalid ip: + res = DNSUtils.ipToName('192.0.2.888') + self.assertEqual(res, None) def testAddr2bin(self): res = IPAddr('10.0.0.0') @@ -1398,11 +1445,44 @@ class DNSUtilsNetworkTests(unittest.TestCase): res = IPAddr('10.0.0.1', cidr=31L) self.assertEqual(res.addr, 167772160L) + self.assertEqual(IPAddr('10.0.0.0').hexdump, '0a000000') + self.assertEqual(IPAddr('1::2').hexdump, '00010000000000000000000000000002') + self.assertEqual(IPAddr('xxx').hexdump, '') + + self.assertEqual(IPAddr('192.0.2.0').getPTR(), '0.2.0.192.in-addr.arpa.') + self.assertEqual(IPAddr('192.0.2.1').getPTR(), '1.2.0.192.in-addr.arpa.') + self.assertEqual(IPAddr('2606:2800:220:1:248:1893:25c8:1946').getPTR(), + '6.4.9.1.8.c.5.2.3.9.8.1.8.4.2.0.1.0.0.0.0.2.2.0.0.0.8.2.6.0.6.2.ip6.arpa.') + def testIPAddr_Equal6(self): self.assertEqual( IPAddr('2606:2800:220:1:248:1893::'), IPAddr('2606:2800:220:1:248:1893:0:0') ) + # special case IPv6 in brackets: + self.assertEqual( + IPAddr('[2606:2800:220:1:248:1893::]'), + IPAddr('2606:2800:220:1:248:1893:0:0') + ) + + def testIPAddr_InInet(self): + ip4net = IPAddr('93.184.0.1/24') + ip6net = IPAddr('2606:2800:220:1:248:1893:25c8:0/120') + # ip4: + self.assertTrue(IPAddr('93.184.0.1').isInNet(ip4net)) + self.assertTrue(IPAddr('93.184.0.255').isInNet(ip4net)) + self.assertFalse(IPAddr('93.184.1.0').isInNet(ip4net)) + self.assertFalse(IPAddr('93.184.0.1').isInNet(ip6net)) + # ip6: + self.assertTrue(IPAddr('2606:2800:220:1:248:1893:25c8:1').isInNet(ip6net)) + self.assertTrue(IPAddr('2606:2800:220:1:248:1893:25c8:ff').isInNet(ip6net)) + self.assertFalse(IPAddr('2606:2800:220:1:248:1893:25c8:100').isInNet(ip6net)) + self.assertFalse(IPAddr('2606:2800:220:1:248:1893:25c8:100').isInNet(ip4net)) + # raw not in net: + self.assertFalse(IPAddr('93.184.0.1', IPAddr.CIDR_RAW).isInNet(ip4net)) + self.assertFalse(IPAddr('2606:2800:220:1:248:1893:25c8:1', IPAddr.CIDR_RAW).isInNet(ip6net)) + # invalid not in net: + self.assertFalse(IPAddr('xxx').isInNet(ip4net)) def testIPAddr_Compare(self): ip4 = [ @@ -1473,6 +1553,10 @@ class DNSUtilsNetworkTests(unittest.TestCase): self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')), '2606:28ff:220:1:248:1893:25c8:0') + def testIPAddr_CIDR_Wrong(self): + # too many plen representations: + self.assertRaises(ValueError, IPAddr, '2606:28ff:220:1:248:1893:25c8::/ffff::/::1') + def testIPAddr_CIDR_Repr(self): self.assertEqual(["127.0.0.0/8", "::/32", "2001:db8::/32"], [IPAddr("127.0.0.0", 8), IPAddr("::1", 32), IPAddr("2001:db8::", 32)] diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index e5f42ff9..508b4ba7 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1047,28 +1047,6 @@ class ServerConfigReaderTests(LogCaptureTestCase): logSys.debug(l) return True - def test_IPAddr(self): - self.assertTrue(IPAddr('192.0.2.1').isIPv4) - self.assertTrue(IPAddr('2001:DB8::').isIPv6) - - def test_IPAddr_Raw(self): - # raw string: - r = IPAddr('xxx', IPAddr.CIDR_RAW) - self.assertFalse(r.isIPv4) - self.assertFalse(r.isIPv6) - self.assertTrue(r.isValid) - self.assertEqual(r, 'xxx') - self.assertEqual('xxx', str(r)) - self.assertNotEqual(r, IPAddr('xxx')) - # raw (not IP, for example host:port as string): - r = IPAddr('1:2', IPAddr.CIDR_RAW) - self.assertFalse(r.isIPv4) - self.assertFalse(r.isIPv6) - self.assertTrue(r.isValid) - self.assertEqual(r, '1:2') - self.assertEqual('1:2', str(r)) - self.assertNotEqual(r, IPAddr('1:2')) - def _testExecActions(self, server): jails = server._Server__jails for jail in jails: