diff --git a/CHANGES b/CHANGES index 40326a9..62d7bd4 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ - Add support for SigningTable, KeyTable, and KeyTableEd25519 (LP: #1797397) - Add support for specifying MinimumKeyBits for RSA signatures - Add support for SignHeaders feature, thanks to Ralph Seichter for the patch + - Add support for specifying DNSTimeout (bumps required dkimpy version to 1.0) - Add information on message content conversion to README - Add new expand option to setup.py so various file system locations can be specified at build/install time rather than being hard coded diff --git a/TODO b/TODO index 9ae93ec..2144d20 100644 --- a/TODO +++ b/TODO @@ -50,7 +50,7 @@ Subdomain support implemented verified Test suite implemented verified 1.2.0 -DNSTimeout (dkimpy 1.0) +DNSTimeout (dkimpy 1.0) implemented verified by inspection KeyTable implemented verified KeytableEd25519 implemented verified MinimumKeyBits implemented verified diff --git a/dkimpy_milter/__init__.py b/dkimpy_milter/__init__.py index 7975732..b912660 100644 --- a/dkimpy_milter/__init__.py +++ b/dkimpy_milter/__init__.py @@ -359,7 +359,7 @@ class dkimMilter(Milter.Base): res = False self.header_a = None for y in range(self.has_dkim): # Verify _ALL_ the signatures - d = dkim.DKIM(txt, minkey=self.conf.get('MinimumKeyBits')) + d = dkim.DKIM(txt, minkey=self.conf.get('MinimumKeyBits'), timeout=self.conf.get('DNSTimeout')) try: dnsoverride = self.conf.get('DNSOverride') if isinstance(dnsoverride, str): diff --git a/dkimpy_milter/config.py b/dkimpy_milter/config.py index de62947..cbbfaea 100644 --- a/dkimpy_milter/config.py +++ b/dkimpy_milter/config.py @@ -50,6 +50,7 @@ defaultConfigData = { 'MacroList': '', 'MacroListVerify': '', 'DNSOverride': None, + 'DNSTimeout': 5, 'SubDomains': False, 'SigningTable': None, 'debugLevel': 0 # Undocumented config item for developer use @@ -158,7 +159,7 @@ class HostsDataset(object): '''Get validated PTR name of IP address''' results = [] s = Session() - ptrnames = s.dns(source.reverse_pointer, 'PTR') + ptrnames = s.dns(source.reverse_pointer, 'PTR', timeout=self.conf.get('DNSTimeout')) for name in ptrnames: if isinstance(source, ipaddress.IPv4Address): ips = s.dns(name, 'A') @@ -357,6 +358,7 @@ def _readConfigFile(path, configData=None, configGlobal={}): 'MacroList': 'dataset', 'MacroListVerify': 'dataset', 'DNSOverride': 'str', + 'DNSTimeout': 'int', 'debugLevel': 'int', 'SignHeaders': 'dataset' } diff --git a/dkimpy_milter/dnsplug.py b/dkimpy_milter/dnsplug.py index 33067e9..9e7aa58 100644 --- a/dkimpy_milter/dnsplug.py +++ b/dkimpy_milter/dnsplug.py @@ -88,7 +88,7 @@ class Session(object): result = self.dns(cname, qtype, cnames=cnames) return result -def DNSLookup_pydns(name, qtype, tcpfallback=True, timeout=30): +def DNSLookup_pydns(name, qtype, tcpfallback=True, timeout=5): try: # FIXME: To be thread safe, we create a fresh DnsRequest with # each call. It would be more efficient to reuse @@ -114,11 +114,11 @@ def DNSLookup_pydns(name, qtype, tcpfallback=True, timeout=30): except IOError as x: raise DNS.DNSError('DNS: ' + str(x)) -def DNSLookup_dnspython(name,qtype,tcpfallback=True,timeout=30): +def DNSLookup_dnspython(name,qtype,tcpfallback=True,timeout=5): retVal = [] try: # FIXME: how to disable TCP fallback in dnspython if not tcpfallback? - answers = dns.resolver.query(name, qtype) + answers = dns.resolver.query(name, qtype, raise_on_no_answer=False, lifetime=timeout) for rdata in answers: if qtype == 'A' or qtype == 'AAAA': retVal.append(((name, qtype), rdata.address)) diff --git a/man/dkimpy-milter.conf.5 b/man/dkimpy-milter.conf.5 index 2506865..a363598 100644 --- a/man/dkimpy-milter.conf.5 +++ b/man/dkimpy-milter.conf.5 @@ -358,6 +358,12 @@ consulting the DNS on each message. This is useful primarily for testing purposes in environments where it is awkward to modify the system DNS resolution. It should not be used in production. +.TP +.I DNSTimeout (integer) +Sets the DNS timeout in seconds. A value of 0 causes no wait (this is +different than opendkim). The default is 5. See also the NOTES section +below. + .TP .I PeerList (dataset) Identifies a set of "peers" that identifies clients whose connections @@ -511,6 +517,27 @@ unless an alternate .I group is specified. +.SH NOTES +When using DNS timeouts (see the +.I DNSTimeout +option above), be sure not to use a timeout that is larger than the timeout +being used for interaction between +.I sendmail +and the filter. Otherwise, the MTA could abort a message while waiting for +a reply from the filter, which in turn is still waiting for a DNS reply. This +must take into accout that the timeout is per DNS lookup so the total DNS wait +time may be subustantially loner than the value specified in +.I DNSTimeout +\. There is a DNS lookup for each connection if the +.I InternalHosts +option is in use and one for DKIM public key record lookup for each algorithm +per signature per message (i.e. potentially two lookups per signature). + +.SH FILES +.TP +.I /usr/local/etc/dkimpy-milter/dkimpy-milter.conf +Default location of this file. + .SH "AUTHORS" \ddkimpy-milter\fR was written by Scott Kitterman . It is based on dkim-milter.py Copyright (c) 2001-2013 Business Management Systems, Inc. diff --git a/man/dkimpy-milter.conf.5.in b/man/dkimpy-milter.conf.5.in index 30eaecd..9a776af 100644 --- a/man/dkimpy-milter.conf.5.in +++ b/man/dkimpy-milter.conf.5.in @@ -358,6 +358,12 @@ consulting the DNS on each message. This is useful primarily for testing purposes in environments where it is awkward to modify the system DNS resolution. It should not be used in production. +.TP +.I DNSTimeout (integer) +Sets the DNS timeout in seconds. A value of 0 causes no wait (this is +different than opendkim). The default is 5. See also the NOTES section +below. + .TP .I PeerList (dataset) Identifies a set of "peers" that identifies clients whose connections @@ -511,6 +517,27 @@ unless an alternate .I group is specified. +.SH NOTES +When using DNS timeouts (see the +.I DNSTimeout +option above), be sure not to use a timeout that is larger than the timeout +being used for interaction between +.I sendmail +and the filter. Otherwise, the MTA could abort a message while waiting for +a reply from the filter, which in turn is still waiting for a DNS reply. This +must take into accout that the timeout is per DNS lookup so the total DNS wait +time may be subustantially loner than the value specified in +.I DNSTimeout +\. There is a DNS lookup for each connection if the +.I InternalHosts +option is in use and one for DKIM public key record lookup for each algorithm +per signature per message (i.e. potentially two lookups per signature). + +.SH FILES +.TP +.I @CONFDIR@/dkimpy-milter.conf +Default location of this file. + .SH "AUTHORS" \ddkimpy-milter\fR was written by Scott Kitterman . It is based on dkim-milter.py Copyright (c) 2001-2013 Business Management Systems, Inc. diff --git a/setup.py b/setup.py index dae4be2..913cf56 100644 --- a/setup.py +++ b/setup.py @@ -80,9 +80,9 @@ class FileMacroExpand(distutils.cmd.Command): kw = {} # Work-around for lack of 'or' requires in setuptools. try: import dns - kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'dnspython'] + kw['install_requires'] = ['dkimpy>=1.0', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'dnspython>=1.16.0'] except ImportError: # If PyDNS is not installed, prefer dnspython - kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'Py3DNS'] + kw['install_requires'] = ['dkimpy>=1.0', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'Py3DNS'] setup( name='dkimpy-milter',