diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index 7256ce4e..aeb5102d 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -42,6 +42,32 @@ def asip(ip): return ip return IPAddr(ip) +def getfqdn(name=''): + """Get fully-qualified hostname of given host, thereby resolve of an external + IPs and name will be preferred before the local domain (or a loopback), see gh-2438 + """ + try: + name = name or socket.gethostname() + names = ( + ai[3] for ai in socket.getaddrinfo( + name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME + ) if ai[3] + ) + if names: + # first try to find a fqdn starting with the host name like www.domain.tld for www: + pref = name+'.' + first = None + for ai in names: + if ai.startswith(pref): + return ai + if not first: first = ai + # not found - simply use first known fqdn: + return first + except socket.error: + pass + # fallback to python's own getfqdn routine: + return socket.getfqdn(name) + ## # Utils class for DNS handling. @@ -132,7 +158,7 @@ class DNSUtils: if name is None: name = '' for hostname in ( - (socket.getfqdn, socket.gethostname) if fqdn else (socket.gethostname, socket.getfqdn) + (getfqdn, socket.gethostname) if fqdn else (socket.gethostname, getfqdn) ): try: name = hostname() diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 478ba34c..2a005502 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -40,7 +40,7 @@ from ..server.jail import Jail from ..server.filterpoll import FilterPoll from ..server.filter import FailTicket, Filter, FileFilter, FileContainer from ..server.failmanager import FailManagerEmpty -from ..server.ipdns import DNSUtils, IPAddr +from ..server.ipdns import getfqdn, DNSUtils, IPAddr from ..server.mytime import MyTime from ..server.utils import Utils, uni_decode from .utils import setUpMyTime, tearDownMyTime, mtimesleep, with_tmpdir, LogCaptureTestCase, \ @@ -2028,6 +2028,20 @@ class DNSUtilsNetworkTests(unittest.TestCase): ip1 = IPAddr('93.184.216.34'); ip2 = IPAddr('93.184.216.34'); self.assertEqual(id(ip1), id(ip2)) ip1 = IPAddr('2606:2800:220:1:248:1893:25c8:1946'); ip2 = IPAddr('2606:2800:220:1:248:1893:25c8:1946'); self.assertEqual(id(ip1), id(ip2)) + def testFQDN(self): + sname = DNSUtils.getHostname(fqdn=False) + lname = DNSUtils.getHostname(fqdn=True) + # FQDN is not localhost if short hostname is not localhost too (or vice versa): + self.assertEqual(lname != 'localhost', + sname != 'localhost') + # FQDN from short name should be long name: + self.assertEqual(getfqdn(sname), lname) + # FQDN from FQDN is the same: + self.assertEqual(getfqdn(lname), lname) + # coverage (targeting all branches): FQDN from loopback and DNS blackhole is always the same: + self.assertIn(getfqdn('localhost.'), ('localhost', 'localhost.')) + self.assertIn(getfqdn('as112.arpa.'), ('as112.arpa.', 'as112.arpa')) + class JailTests(unittest.TestCase):