diff -Nru pyspf-2.0.10/CHANGELOG pyspf-2.0.12t/CHANGELOG --- pyspf-2.0.10/CHANGELOG 2014-09-02 19:17:08.000000000 +0000 +++ pyspf-2.0.12t/CHANGELOG 2015-08-05 13:44:45.000000000 +0000 @@ -1,4 +1,19 @@ -Version 2.0.10 - September 2. 2014 +Version 2.0.12 - August 5, 2015 + * Reset void_lookups at top of check() + * Ignore permerror for best_guess() + * Don't crash on null DNS TXT record (ignore): test case null-text + * Trailing spaces are allowed by 4.5/2: test case trailing-space + * Make CNAME loop result in unknown host: test case ptr-cname-loop + * Test case and fix for mixed case CNAME loop, test case ptr-cname-loop + +Version 2.0.11 - December 5, 2014 + * Fix another bug in SPF record parsing that caused records with terms + separated by multple spaces as invalid, but they are fine per the ABNF + * Downcase names in additional answers returned by DNS before adding + to cache, since case inconsistency can cause PTR match failures (initial + patch thanks to Joni Fieggen) and other problems. + +Version 2.0.10 - September 2, 2014 * Fix bug in SPF record parsing that caused all 'whitespace' characters to be considered valid term separators and not just spaces * Fixed multiple bugs in temperror processing that would lead to tracebacks diff -Nru pyspf-2.0.10/debian/changelog pyspf-2.0.12t/debian/changelog --- pyspf-2.0.10/debian/changelog 2016-03-11 18:22:18.000000000 +0000 +++ pyspf-2.0.12t/debian/changelog 2016-03-11 18:18:31.000000000 +0000 @@ -1,8 +1,44 @@ -pyspf (2.0.10-1~ubuntu14.04.1) trusty-backports; urgency=medium +pyspf (2.0.12t-1~ubuntu14.04.1) trusty-backports; urgency=medium - * No-change backport to trusty (LP: #1364883) + * No-change backport to trusty (LP: #1556209) - -- Scott Kitterman Wed, 03 Sep 2014 06:25:32 -0400 + -- Scott Kitterman Fri, 11 Mar 2016 13:18:31 -0500 + +pyspf (2.0.12t-1) unstable; urgency=medium + + * New upstream release + * Switch spf-tools-python to use python3 instead of python2.7 + - Update debian/rules and control + - Patch spf.py to use /usr/bin/python3 when run as a script + * Promote python{3}-authres recommends to depends since it is used in the + doctests + * Add dh-python to build-depends + * Updated debian/watch to point at pypi.debian.net redirector + * Update debian/copyright + * Update Homepage: field in debian/control to point at pypi instead of + cheeseshop + * Bump standards version to 3.9.6 without further change. + + -- Scott Kitterman Wed, 05 Aug 2015 19:42:25 -0400 + +pyspf (2.0.11-1) unstable; urgency=medium + + * New upstream release + - Fix problem with incorrect SPF results due to DNS cache case sensitivity + problems (Closes: #773491) + - Remove debian/patches since the only patch was a cherry pick from + upstream that is in the new release + + -- Scott Kitterman Thu, 18 Dec 2014 17:55:40 -0500 + +pyspf (2.0.10-2) unstable; urgency=medium + + * Backport upstream fix for multiple spaces between SPF record terms + issue (Closes: #771547) + * Switch to source format 3.0(quilt) as the lowest impact way to add a + patch system. + + -- Scott Kitterman Sun, 30 Nov 2014 11:19:09 -0500 pyspf (2.0.10-1) unstable; urgency=medium diff -Nru pyspf-2.0.10/debian/control pyspf-2.0.12t/debian/control --- pyspf-2.0.10/debian/control 2016-03-11 18:22:18.000000000 +0000 +++ pyspf-2.0.12t/debian/control 2015-08-13 05:19:15.000000000 +0000 @@ -3,18 +3,18 @@ Priority: optional Maintainer: Scott Kitterman Uploaders: Debian Python Modules Team , Gustavo Franco -Build-Depends: debhelper (>= 9), python-all (>= 2.6.6-6), python3-all +Build-Depends: debhelper (>= 9), dh-python, python-all (>= 2.6.6-6), python3-all X-Python-Version: >= 2.6 X-Python3-Version: >= 3.3 -Homepage: http://cheeseshop.python.org/pypi/pyspf/ +Homepage: https://pypi.python.org/pypi/pyspf/ Vcs-Svn: svn://anonscm.debian.org/python-modules/packages/pyspf/trunk/ Vcs-Browser: http://anonscm.debian.org/viewvc/python-modules/packages/pyspf/trunk/ -Standards-Version: 3.9.5 +Standards-Version: 3.9.6 Package: python-spf Architecture: all -Depends: ${python:Depends}, ${misc:Depends}, python-dns, python-ipaddr (>= 2.1.10) -Recommends: python-authres +Depends: ${python:Depends}, ${misc:Depends}, python-dns, python-ipaddr (>= 2.1.10), + python-authres Suggests: python-yaml Breaks: postfix-policyd-spf-python (<< 1.1~), tumgreyspf (<< 1.36-4.1~) Description: sender policy framework (SPF) module for Python @@ -25,8 +25,8 @@ Package: python3-spf Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-dns (>= 3.0.2-1+deb7u1) -Recommends: python3-authres +Depends: ${python3:Depends}, ${misc:Depends}, python3-dns (>= 3.0.2-1+deb7u1), + python3-authres Suggests: python3-yaml Breaks: postfix-policyd-spf-python (<< 1.2~) Description: sender policy framework (SPF) module for Python 3 @@ -37,7 +37,7 @@ Package: spf-tools-python Architecture: all -Depends: ${python:Depends}, ${misc:Depends}, python-spf +Depends: ${python3:Depends}, ${misc:Depends}, python3-spf Replaces: python-spf (<< 2.0.5-3) Description: sender policy framework (SPF) tools for Python SPF (Sender Policy Framework) related scripts and tools in Python. This diff -Nru pyspf-2.0.10/debian/copyright pyspf-2.0.12t/debian/copyright --- pyspf-2.0.10/debian/copyright 2016-03-11 18:22:18.000000000 +0000 +++ pyspf-2.0.12t/debian/copyright 2015-08-05 23:37:41.000000000 +0000 @@ -8,9 +8,9 @@ Copyright Holder: (c) 2003 Terence Way Portions Copyright(c) 2004,2005,2006,2007,2008,2011,2012 Stuart Gathman -Portions Copyright(c) 2005,2006,2007,2008,2011,2012,2013 Scott Kitterman +Portions Copyright(c) 2005,2006,2007,2008,2011,2012,2013,2014 Scott Kitterman -Portions Copyright(c) 2013 Stuart Gathman +Portions Copyright(c) 2013,2014 Stuart Gathman This software is licensed under the Python Software Foundation License. @@ -75,6 +75,34 @@ All rights reserved. Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The RFC 7208 test-suite (rfc7208-tests.yml) is +(C) 2006-2008 Stuart D Gathman + 2007-2008 Julian Mehnle + 2014 Scott Kitterman +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, diff -Nru pyspf-2.0.10/debian/patches/series pyspf-2.0.12t/debian/patches/series --- pyspf-2.0.10/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ pyspf-2.0.12t/debian/patches/series 2015-08-13 04:46:59.000000000 +0000 @@ -0,0 +1 @@ +use_python3.diff diff -Nru pyspf-2.0.10/debian/patches/use_python3.diff pyspf-2.0.12t/debian/patches/use_python3.diff --- pyspf-2.0.10/debian/patches/use_python3.diff 1970-01-01 00:00:00.000000000 +0000 +++ pyspf-2.0.12t/debian/patches/use_python3.diff 2015-08-13 04:48:18.000000000 +0000 @@ -0,0 +1,14 @@ +Description: Patch spf.py to use /usr/bin/python3 when run as a script +Author: Scott Kitterman +Origin: vendor +Forwarded: not-needed +Last-Update: 2015-08-13 + +--- pyspf-2.0.12t.orig/spf.py ++++ pyspf-2.0.12t/spf.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/python ++#!/usr/bin/python3 + from __future__ import print_function + """SPF (Sender Policy Framework) implementation. + diff -Nru pyspf-2.0.10/debian/rules pyspf-2.0.12t/debian/rules --- pyspf-2.0.10/debian/rules 2016-03-11 18:22:18.000000000 +0000 +++ pyspf-2.0.12t/debian/rules 2015-08-13 04:41:22.000000000 +0000 @@ -1,5 +1,6 @@ #!/usr/bin/make -f +export DH_VERBOSE=1 %: dh $@ --with python2,python3 @@ -19,13 +20,11 @@ mkdir -p debian/spf-tools-python/usr/bin/ # Rename the `spfquery` tool for the alternatives system # (omitting the ".py" language extension): - mv $(CURDIR)/debian/python-spf/usr/bin/spfquery.py debian/spf-tools-python/usr/bin/spfquery.pyspf + mv $(CURDIR)/debian/python3-spf/usr/bin/spfquery.py debian/spf-tools-python/usr/bin/spfquery.pyspf # Give the `type99` tool a more specific name # (omitting the ".py" language extension): - mv $(CURDIR)/debian/python-spf/usr/bin/type99.py debian/spf-tools-python/usr/bin/pyspf-type99 - rm -rf $(CURDIR)/debian/python-spf/usr/bin - # Don't ship python3 specific variants of spf-tools (current scripts work for both) - rm -rf $(CURDIR)/debian/python3-spf/usr/bin + mv $(CURDIR)/debian/python3-spf/usr/bin/type99.py debian/spf-tools-python/usr/bin/pyspf-type99 + rm -rf $(CURDIR)/debian/python*-spf/usr/bin override_dh_installdocs: @@ -46,7 +45,11 @@ override_dh_fixperms: dh_fixperms - chmod +x $(CURDIR)/debian/python-spf/usr/lib/python2.?/*-packages/spf.py + chmod +x $(CURDIR)/debian/python3-spf/usr/lib/python3/dist-packages/spf.py + +override_dh_python2: + dh_python2 -N spf-tools-python override_dh_python3: - dh_python3 -N spf-tools-python + dh_python3 --shebang=/usr/bin/python3 + diff -Nru pyspf-2.0.10/debian/source/format pyspf-2.0.12t/debian/source/format --- pyspf-2.0.10/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ pyspf-2.0.12t/debian/source/format 2014-11-30 16:23:24.000000000 +0000 @@ -0,0 +1 @@ +3.0 (quilt) diff -Nru pyspf-2.0.10/debian/spf-tools-python.links pyspf-2.0.12t/debian/spf-tools-python.links --- pyspf-2.0.10/debian/spf-tools-python.links 2016-03-11 18:22:18.000000000 +0000 +++ pyspf-2.0.12t/debian/spf-tools-python.links 2015-08-05 23:31:19.000000000 +0000 @@ -1 +1 @@ -usr/lib/python2.7/dist-packages/spf.py usr/bin/pyspf +usr/lib/python3/dist-packages/spf.py usr/bin/pyspf diff -Nru pyspf-2.0.10/PKG-INFO pyspf-2.0.12t/PKG-INFO --- pyspf-2.0.10/PKG-INFO 2014-09-02 19:18:04.000000000 +0000 +++ pyspf-2.0.12t/PKG-INFO 2015-08-05 13:50:09.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyspf -Version: 2.0.10 +Version: 2.0.12 Summary: SPF (Sender Policy Framework) implemented in Python. Home-page: http://pymilter.sourceforge.net/ Author: Stuart D. Gathman diff -Nru pyspf-2.0.10/pyspf.spec pyspf-2.0.12t/pyspf.spec --- pyspf-2.0.10/pyspf.spec 2014-09-02 19:17:08.000000000 +0000 +++ pyspf-2.0.12t/pyspf.spec 2015-08-05 13:44:45.000000000 +0000 @@ -7,7 +7,7 @@ %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: %{pythonbase}-pyspf -Version: 2.0.10 +Version: 2.0.12 Release: 1 Summary: Python module and programs for SPF (Sender Policy Framework). @@ -56,6 +56,21 @@ /usr/lib/python2.6/site-packages/pyspf-%{version}-py2.6.egg-info %changelog +* Wed Aug 5 2015 Stuart Gathman 2.0.12-1 +- Reset void_lookups at top of check() to fix bogus permerror on best_guess() +- Ignore permerror for best_guess() +- Don't crash on null DNS TXT record (ignore): test case null-text +- Trailing spaces are allowed by 4.5/2: test case trailing-space +- Make CNAME loop result in unknown host: test case ptr-cname-loop +- Test case and fix for mixed case CNAME loop, test case ptr-cname-loop + +* Fri Dec 5 2014 Stuart Gathman 2.0.11-1 +- Fix another bug in SPF record parsing that caused records with terms + separated by multple spaces as invalid, but they are fine per the ABNF +- Downcase names in additional answers returned by DNS before adding + to cache, since case inconsistency can cause PTR match failures (initial + patch thanks to Joni Fieggen) and other problems. + * Tue Sep 2 2014 Stuart Gathman 2.0.10-1 - Fix AAAA not flagged as bytes when strict=2 - Split mechanisms by space only, not by whitespace diff -Nru pyspf-2.0.10/python-pyspf.spec pyspf-2.0.12t/python-pyspf.spec --- pyspf-2.0.10/python-pyspf.spec 2014-09-02 19:17:08.000000000 +0000 +++ pyspf-2.0.12t/python-pyspf.spec 2015-08-05 13:44:45.000000000 +0000 @@ -1,7 +1,7 @@ %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: python-pyspf -Version: 2.0.10 +Version: 2.0.12 Release: 1%{?dist} Summary: Python module and programs for SPF (Sender Policy Framework). @@ -55,6 +55,21 @@ /usr/lib/python2.6/site-packages/pyspf-%{version}-py2.6.egg-info %changelog +* Wed Aug 5 2015 Stuart Gathman 2.0.12-1 +- Reset void_lookups at top of check() to fix bogus permerror on best_guess() +- Ignore permerror for best_guess() +- Don't crash on null DNS TXT record (ignore): test case null-text +- Trailing spaces are allowed by 4.5/2: test case trailing-space +- Make CNAME loop result in unknown host: test case ptr-cname-loop +- Test case and fix for mixed case CNAME loop, test case ptr-cname-loop + +* Fri Dec 5 2014 Stuart Gathman 2.0.11-1 +- Fix another bug in SPF record parsing that caused records with terms + separated by multple spaces as invalid, but they are fine per the ABNF +- Downcase names in additional answers returned by DNS before adding + to cache, since case inconsistency can cause PTR match failures (initial + patch thanks to Joni Fieggen) and other problems. + * Tue Sep 2 2014 Stuart Gathman 2.0.10-1 - Fix AAAA not flagged as bytes when strict=2 - Split mechanisms by space only, not by whitespace diff -Nru pyspf-2.0.10/setup.py pyspf-2.0.12t/setup.py --- pyspf-2.0.10/setup.py 2014-09-02 19:17:08.000000000 +0000 +++ pyspf-2.0.12t/setup.py 2015-01-02 00:19:52.000000000 +0000 @@ -6,7 +6,7 @@ DESC = """SPF (Sender Policy Framework) implemented in Python.""" setup(name='pyspf', - version='2.0.10', + version='2.0.12', description=DESC, author='Terence Way', author_email='terry@wayforward.net', diff -Nru pyspf-2.0.10/spf.py pyspf-2.0.12t/spf.py --- pyspf-2.0.10/spf.py 2014-09-02 19:17:08.000000000 +0000 +++ pyspf-2.0.12t/spf.py 2015-08-05 13:49:45.000000000 +0000 @@ -1,10 +1,11 @@ #!/usr/bin/python +from __future__ import print_function """SPF (Sender Policy Framework) implementation. Copyright (c) 2003 Terence Way Portions Copyright(c) 2004,2005,2006,2007,2008,2011,2012 Stuart Gathman -Portions Copyright(c) 2005,2006,2007,2008,2011,2012,2013 Scott Kitterman -Portions Copyright(c) 2013 Stuart Gathman +Portions Copyright(c) 2005,2006,2007,2008,2011,2012,2013,2014 Scott Kitterman +Portions Copyright(c) 2013,2014 Stuart Gathman This module is free software, and you may redistribute it and/or modify it under the same terms as Python itself, so long as this copyright message @@ -30,33 +31,55 @@ http://www.wayforward.net/spf/ """ -# CVS Commits since last release (2.0.9): +# CVS Commits since last release (2.0.11): # $Log: spf.py,v $ -# Revision 1.108.2.128 2014/09/02 17:31:53 customdesigned -# Release 2.0.10 +# Revision 1.108.2.150 2015/08/05 13:49:45 customdesigned +# Forgot tabnanny # -# Revision 1.108.2.127 2014/09/01 21:17:13 kitterma -# Fix TempError handling of errors from the DNS module. +# Revision 1.108.2.149 2015/08/05 13:07:09 customdesigned +# Release 2.0.12 # -# Revision 1.108.2.126 2014/08/02 18:35:50 customdesigned -# '~' is also an unreserved char in rfc7208. +# Revision 1.108.2.148 2015/08/05 04:49:48 customdesigned +# Reset void_lookups at top of check() # -# Revision 1.108.2.125 2014/08/02 04:36:48 kitterma -# * Fix bug in SPF record parsing that caused all 'whitespace' characters to -# be considered valid term separators and not just spaces +# Revision 1.108.2.147 2015/08/05 03:36:59 customdesigned +# Ignore permerror for best_guess # -# Revision 1.108.2.124 2014/08/02 04:32:36 kitterma -# Archive previous commit messages for spf.py in pyspf_changelog.txt and bumpi -# version to 2.0.10 for start of follow on work. +# Revision 1.108.2.146 2015/06/05 15:58:18 customdesigned +# Don't crash on null TXT record. # -# Revision 1.108.2.123 2014/07/30 18:41:18 customdesigned -# Fix flagging AAAA records in dns_a. Add --strict option to CLI +# Revision 1.108.2.145 2015/01/14 20:27:42 customdesigned +# Fix list feature +# +# Revision 1.108.2.144 2015/01/13 04:40:07 customdesigned +# Trailing spaces *are* allowed by 4.5/2 +# +# Revision 1.108.2.143 2015/01/12 22:51:56 customdesigned +# Trailing space is PermError, but strip for extended result in lax mode. +# +# Revision 1.108.2.142 2015/01/06 14:13:50 customdesigned +# Make CNAME loop result in unknown host. +# +# Revision 1.108.2.141 2015/01/02 01:08:18 customdesigned +# Test case and fix for mixed case CNAME loop. +# +# Revision 1.108.2.140 2015/01/02 00:26:08 customdesigned +# Make CNAME loop check case insensitive. +# +# Revision 1.108.2.139 2014/12/19 00:16:12 kitterma +# Missed a spot bumping to 2.0.12. +# +# Revision 1.108.2.138 2014/12/19 00:15:12 kitterma +# Bump versions, etc. to start 2.0.12 development. +# +# Revision 1.108.2.137 2014/12/13 15:39:27 customdesigned +# Require ipaddress/ipaddr backport with Bytes for python2. # # See pyspf_changelog.txt for earlier CVS commits. __author__ = "Terence Way, Stuart Gathman, Scott Kitterman" __email__ = "pyspf@openspf.org" -__version__ = "2.0.10: Sep 2, 2014" +__version__ = "2.0.12: Aug 5, 2015" MODULE = 'spf' USAGE = """To check an incoming mail request: @@ -93,9 +116,12 @@ try: # Python standard libarary as of python3.3 import ipaddress + if bytes is str: + from ipaddress import Bytes except ImportError: try: import ipaddr as ipaddress + from ipaddr import Bytes except ImportError: print('ipaddr module required: http://code.google.com/p/ipaddr-py/') @@ -366,6 +392,7 @@ if querytime > 0: self.timeout = querytime self.timer = 0 + self.ipaddr = None if i: self.set_ip(i) # Document bits of the object model not set up here: @@ -410,12 +437,14 @@ if ip6: self.A = 'AAAA' self.v = 'ip6' - self.i = '.'.join(list(self.ipaddr.exploded.replace(':','').upper())) + if self.ipaddr: + self.i = '.'.join(list(self.ipaddr.exploded.replace(':','').upper())) self.cidrmax = 128 else: self.A = 'A' self.v = 'in-addr' - self.i = self.ipaddr.exploded + if self.ipaddr: + self.i = self.ipaddr.exploded self.cidrmax = 32 def set_default_explanation(self, exp): @@ -456,7 +485,15 @@ """ if RE_TOPLAB.split(self.d)[-1]: return ('none', 250, '') - return self.check(spf) + pe = self.perm_error + r,c,e = self.check(spf) + if r == 'permerror': # permerror not useful for bestguess + if self.perm_error and self.perm_error.ext: + r,c,e = self.perm_error.ext + else: + r,c = 'neutral',250 + self.perm_error = pe + return r,c,e def check(self, spf=None): """ @@ -526,6 +563,7 @@ # that strict processing would raise is saved here self.perm_error = None self.mechanism = None + self.void_lookups = 0 self.options = {} try: @@ -776,7 +814,7 @@ # Split string by space, drop the 'v=spf1'. Split by all whitespace # casuses things like carriage returns being treated as valid space - # separators, so split() is not sufficient. + # separators, so split() is not sufficient. spf = spf.split(' ') # Catch case where SPF record has no spaces. # Can never happen with conforming dns_spf(), however @@ -787,7 +825,10 @@ if self.strict > 1: raise AmbiguityWarning('Invalid SPF record in', self.d) return ('none', 250, EXPLANATIONS['none']) - spf = spf[1:] + # Just to make it even more fun, the relevant piece of the ABNF for + # term separations is *( 1*SP ( directive / modifier ) ), so it's one + # or more spaces, not just one. So strip empty mechanisms. + spf = [mech for mech in spf[1:] if mech] # copy of explanations to be modified by exp= exps = self.exps @@ -1149,7 +1190,7 @@ dns_list = self.dns(domainname, rr,ignore_void=ignore_void) if dns_list: # a[0][:0] is '' for py3dns-3.0.2, otherwise b'' - a = [a[0][:0].join(a) for a in dns_list] + a = [a[0][:0].join(a) for a in dns_list if a] # FIXME: workaround for error in py3dns-3.0.2 if isinstance(a[0],bytes): return a @@ -1192,7 +1233,7 @@ 'No %s records found for'%A, domainname) if A == 'AAAA' and bytes is str: # work around pydns inconsistency plus python2 bytes/str ambiguity - return [ipaddress.Bytes(ip) for ip in r] + return [Bytes(ip) for ip in r] return r def validated_ptrs(self): @@ -1257,6 +1298,7 @@ if name.endswith('.'): name = name[:-1] if not reduce(lambda x,y:x and 0 < len(y) < 64, name.split('.'),True): return [] # invalid DNS name (too long or empty) + name = name.lower() result = self.cache.get( (name, qtype), []) if result: return result cnamek = (name,'CNAME') @@ -1274,6 +1316,9 @@ timeout = self.timeout timethen = time.time() for k, v in DNSLookup(name, qtype, self.strict, timeout): + # Force case insensitivity in cache, DNS servers often + # return random case in domain part of answers. + k = (k[0].lower(), k[1]) if k == cnamek: cname = v if k[1] == 'CNAME' or (qtype,k[1]) in safe2cache: @@ -1290,11 +1335,12 @@ #return result # if too many == NX_DOMAIN raise PermError('Length of CNAME chain exceeds %d' % MAX_CNAME) cnames[name] = cname - if cname in cnames: - raise PermError('CNAME loop') - result = self.dns(cname, qtype, cnames=cnames) - if result: - self.cache[(name,qtype)] = result + if cname.lower().rstrip('.') in cnames: + if self.strict > 1: raise AmbiguityWarning('CNAME loop', cname) + else: + result = self.dns(cname, qtype, cnames=cnames) + if result: + self.cache[(name,qtype)] = result if not result and not ignore_void: self.void_lookups += 1 if self.void_lookups > MAX_VOID_LOOKUPS: @@ -1895,9 +1941,12 @@ i, s, h = argv q = query(i=i, s=s, h=h,receiver=socket.gethostname(),verbose=verbose, strict=strict) - print(q.check(),q.mechanism) + r = q.check() + print('result:',r,q.mechanism) + if r[0] == 'none': + print('guessed:',q.best_guess(),q.mechanism) if q.perm_error and q.perm_error.ext: - print(q.perm_error.ext) + print('lax:',q.perm_error.ext) if q.iplist: for ip in q.iplist: print(ip) @@ -1905,8 +1954,11 @@ i, s, h = argv[1:] q = query(i=i, s=s, h=h, receiver=socket.gethostname(), strict=False, verbose=verbose) - print(q.check(argv[0]),q.mechanism) + r = q.check(argv[0]) + print('result:',r,q.mechanism) + if r[0] == 'none': + print('guessed:',q.best_guess(),q.mechanism) if q.perm_error and q.perm_error.ext: - print(q.perm_error.ext) + print('lax:',q.perm_error.ext) else: print(USAGE) diff -Nru pyspf-2.0.10/test/rfc4408-tests.yml pyspf-2.0.12t/test/rfc4408-tests.yml --- pyspf-2.0.10/test/rfc4408-tests.yml 2014-08-02 05:44:54.000000000 +0000 +++ pyspf-2.0.12t/test/rfc4408-tests.yml 2014-09-22 00:24:02.000000000 +0000 @@ -1,7 +1,7 @@ # This is the openspf.org test suite (release 2009.10) based on RFC 4408. # http://www.openspf.org/Test_Suite # -# $Id: rfc4408-tests.yml,v 1.1.2.29 2014/08/02 05:44:54 customdesigned Exp $ +# $Id: rfc4408-tests.yml,v 1.1.2.30 2014/09/21 20:44:03 kitterma Exp $ # vim:sw=2 sts=2 et # # See rfc4408-tests.CHANGES for a changelog. @@ -127,6 +127,14 @@ mailfrom: "foobar@nothosed.example.com" result: fail explanation: DEFAULT + two-spaces: + description: >- + ABNF for term separation is one or more spaces, not just one. + spec: 4.6.1 + helo: hosed + host: 1.2.3.4 + mailfrom: "actually@fine.example.com" + result: fail zonedata: example.com: - TIMEOUT @@ -147,6 +155,8 @@ nothosed.example.com: - SPF: "v=spf1 a:example.net -all" - SPF: "\x96" + fine.example.com: + - TXT: "v=spf1 a -all" --- description: Record lookup tests: diff -Nru pyspf-2.0.10/test/rfc7208-tests.yml pyspf-2.0.12t/test/rfc7208-tests.yml --- pyspf-2.0.10/test/rfc7208-tests.yml 2014-08-02 18:35:01.000000000 +0000 +++ pyspf-2.0.12t/test/rfc7208-tests.yml 2015-08-05 13:44:45.000000000 +0000 @@ -1,7 +1,7 @@ # This is the openspf.org test suite (release 2014.04) based on RFC 7208. # http://www.openspf.org/Test_Suite # -# $Id: rfc7208-tests.yml,v 1.1.2.8 2014/08/02 18:35:01 customdesigned Exp $ +# $Id: rfc7208-tests.yml,v 1.1.2.19 2015/06/05 15:58:18 customdesigned Exp $ # vim:sw=2 sts=2 et # # See rfc7208-tests.CHANGES for a changelog. @@ -128,6 +128,34 @@ host: 192.0.2.3 mailfrom: "foobar@ctrl.example.com" result: permerror + two-spaces: + description: >- + ABNF for term separation is one or more spaces, not just one. + spec: 4.6.1 + helo: hosed + host: 1.2.3.4 + mailfrom: "actually@fine.example.com" + result: fail + trailing-space: + description: >- + ABNF for record does allow trailing spaces. + comment: >- + record = version terms *SP + spec: 4.5/2 + helo: hosed + host: 192.0.2.5 + mailfrom: "silly@trail.example.com" + result: fail + null-text: + description: >- + Multiple strings are glued together with no separator. + comment: >- + Note that null text (no strings) is illegal, but SPF should not crash. + spec: 3.3 + helo: hosed + host: 192.0.2.5 + mailfrom: "silly@null.example.com" + result: pass zonedata: example.com: - TIMEOUT @@ -151,6 +179,13 @@ ctrl.example.com: - SPF: "v=spf1 a:ctrl.example.com\x0dptr -all" - A: 192.0.2.3 + fine.example.com: + - SPF: "v=spf1 a -all" + trail.example.com: + - SPF: "v=spf1 a -all " + null.example.com: + - SPF: [ "v=spf1 ip4:", "192.0.2.5 -all" ] + - SPF: [ ] --- description: Record lookup tests: @@ -627,9 +662,37 @@ host: 1.2.3.4 mailfrom: foo@e5.example.com result: permerror + ptr-case-change: + description: >- + arpa domain is case insensitive. + comment: >- + Some DNS servers have random case in the domain part of returned + answers, especially for PTR records. For example, a query for + 1.2.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.F.0.0.4.F.1.1.1.0.1.0.A.2.ip6.arpa + may return + 1.2.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.F.0.0.4.F.1.1.1.0.1.0.a.2.ip6.arpa + spec: 5.5/2 + helo: mail.example.com + host: 2001:db8::1 + mailfrom: bar@e6.example.com + result: pass + ptr-cname-loop: + description: >- + a PTR with CNAME loop and inconsistent case in domain. + comment: >- + RFC 1034 3.6.2/11 says, CNAME chains should be followed and CNAME loops + signalled as an error. RFC 7208 5.5/7 says, If a DNS error occurs while + doing an A RR lookup, then that domain name is skipped and the search + continues. + spec: 5.5/7 + helo: loop.example.com + host: 192.0.2.4 + mailfrom: postmaster@loop.example.com + result: neutral zonedata: mail.example.com: - A: 1.2.3.4 + - AAAA: 2001:db8::1 e1.example.com: - SPF: v=spf1 ptr/0 -all e2.example.com: @@ -640,6 +703,8 @@ - PTR: mail.example.com 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.B.A.B.E.F.A.C.ip6.arpa: - PTR: e3.example.com + 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.D.0.1.0.0.2.ip6.arpa: + - PTR: mail.Example.com e3.example.com: - SPF: v=spf1 ptr -all - A: 1.2.3.4 @@ -648,6 +713,17 @@ - SPF: v=spf1 ptr -all e5.example.com: - SPF: "v=spf1 ptr:" + e6.example.com: + - SPF: "v=spf1 ptr:example.Com -all" + loop.example.com: + - SPF: "v=spf1 ptr" + 4.2.0.192.in-addr.arpa: + - PTR: "loop4.example.com." + loop4.example.com: + - CNAME: "CNAME.example.com." + cname.example.com: + - CNAME: "CNAME.example.com." + --- description: A mechanism syntax tests: @@ -1607,7 +1683,7 @@ e4.example.com: - SPF: v=spf1 ip6:::1.1.1.1//33 e5.example.com: - - SPF: v=spf1 ip6:CAFE:BABE:8000::/33 + - SPF: v=spf1 ip6:Cafe:Babe:8000::/33 e6.example.com: - SPF: v=spf1 ip6::CAFE::BABE --- diff -Nru pyspf-2.0.10/test/testspf.py pyspf-2.0.12t/test/testspf.py --- pyspf-2.0.10/test/testspf.py 2014-08-02 05:01:14.000000000 +0000 +++ pyspf-2.0.12t/test/testspf.py 2015-01-12 22:47:56.000000000 +0000 @@ -42,7 +42,7 @@ if timeout: raise spf.TempError('DNS timeout') return - t,v = i + t,v,n = i if t == qtype: timeout = False if v == 'TIMEOUT': @@ -54,7 +54,7 @@ v = bytes(socket.inet_pton(socket.AF_INET6,v)) elif t in ('TXT','SPF'): v = tuple([s.encode('utf-8') for s in v]) - yield ((name,t),v) + yield ((n,t),v) except KeyError: if name.startswith('error.'): raise spf.TempError('DNS timeout') @@ -66,6 +66,7 @@ self.id = testid self.scenario = scenario self.explanation = None + self.bestguess = None self.spec = None self.header = None self.strict = True @@ -78,7 +79,7 @@ if type(self.comment) is str: self.comment = self.comment.splitlines() -def getrdata(r): +def getrdata(r,name): "Unpack rdata given as list of maps to list of tuples." txt = [] # generated TXT records gen = True @@ -89,12 +90,12 @@ if t == 'TXT': gen = False # no generated TXT records elif t == 'SPF' and gen: - txt.append(('TXT',v)) + txt.append(('TXT',v,name)) if v != 'NONE': if t in ('TXT','SPF') and type(v) == str: - yield (t,(v,)) + yield (t,(v,),name) else: - yield i + yield (t,v,name) except: yield m if gen: @@ -103,7 +104,7 @@ def loadZone(data): return dict([ - (d.lower(), list(getrdata(r))) for d,r in list(data['zonedata'].items()) + (d.lower(), list(getrdata(r,d))) for d,r in list(data['zonedata'].items()) ]) class SPFScenario(object): @@ -152,7 +153,7 @@ unittest.TestCase.__init__(self) self._spftest = t self._testMethodName = 'runTest' - self._testMethodDoc = t.spec + self._testMethodDoc = str(t.spec) def id(self): t = self._spftest @@ -193,6 +194,9 @@ ok = False if t.header: self.assertEqual(t.header,q.get_header(res,receiver=t.receiver)) + if q.perm_error and t.bestguess is not None \ + and q.perm_error.ext[0] != t.bestguess: + ok = False if not ok: if verbose and not t.explanation: msg += exp+'\n' if verbose > 1: msg += t.scenario.zonedata diff -Nru pyspf-2.0.10/test/test.yml pyspf-2.0.12t/test/test.yml --- pyspf-2.0.10/test/test.yml 2013-07-25 14:18:52.000000000 +0000 +++ pyspf-2.0.12t/test/test.yml 2015-01-13 04:34:27.000000000 +0000 @@ -195,4 +195,3 @@ - CNAME: '' e3.example.com: - SPF: v=spf1 a:mx1.example.com mx:mx1.example.com ~all -