Limit CNAME chains independently of DNS lookup limit
This commit is contained in:
+9
-2
@@ -24,7 +24,7 @@ ALT="Viewable With Any Browser" BORDER="0"></A>
|
|||||||
Stuart D. Gathman</a><br>
|
Stuart D. Gathman</a><br>
|
||||||
This web page is written by Stuart D. Gathman<br>and<br>sponsored by
|
This web page is written by Stuart D. Gathman<br>and<br>sponsored by
|
||||||
<a href="http://www.bmsi.com">Business Management Systems, Inc.</a> <br>
|
<a href="http://www.bmsi.com">Business Management Systems, Inc.</a> <br>
|
||||||
Last updated Jun 09, 2005</h4>
|
Last updated Jul 20, 2005</h4>
|
||||||
|
|
||||||
See the <a href="faq.html">FAQ</a> | <a href="http://sourceforge.net/project/showfiles.php?group_id=139894">Download now</a> |
|
See the <a href="faq.html">FAQ</a> | <a href="http://sourceforge.net/project/showfiles.php?group_id=139894">Download now</a> |
|
||||||
<a href="/mailman/listinfo/pymilter">Subscribe to mailing list</a> |
|
<a href="/mailman/listinfo/pymilter">Subscribe to mailing list</a> |
|
||||||
@@ -49,7 +49,14 @@ efficient and secure. I recommend upgrading.
|
|||||||
|
|
||||||
Python milter is being moved to
|
Python milter is being moved to
|
||||||
<a href="http://sourceforge.net/projects/pymilter/">pymilter Sourceforge
|
<a href="http://sourceforge.net/projects/pymilter/">pymilter Sourceforge
|
||||||
project</a> for development.
|
project</a> for development and release downloads.
|
||||||
|
<p>
|
||||||
|
Release 0.8.2 has changes to SPF to bring it in line with the newly
|
||||||
|
official RFC. It adds SES support (the original SES without body hash)
|
||||||
|
for pysrs-0.30.10, and honeypot support for pydspam-1.1.9. There is
|
||||||
|
a new method in the base milter module. milter.set_exception_policy(i)
|
||||||
|
lets you choose a policy of CONTINUE, REJECT, or TEMPFAIL (default) for
|
||||||
|
untrapped exceptions encountered in a milter callback.
|
||||||
<p>
|
<p>
|
||||||
Release 0.8.0 is the first <a href="http://sourceforge.net/">Sourceforge</a>
|
Release 0.8.0 is the first <a href="http://sourceforge.net/">Sourceforge</a>
|
||||||
release. It supports Python-2.4, and provides an option to accept mail
|
release. It supports Python-2.4, and provides an option to accept mail
|
||||||
|
|||||||
+9
-4
@@ -1,6 +1,6 @@
|
|||||||
%define name milter
|
%define name milter
|
||||||
%define version 0.8.2
|
%define version 0.8.2
|
||||||
%define release 2.RH7
|
%define release 4.RH7
|
||||||
# what version of RH are we building for?
|
# what version of RH are we building for?
|
||||||
%define redhat9 0
|
%define redhat9 0
|
||||||
%define redhat7 1
|
%define redhat7 1
|
||||||
@@ -31,7 +31,7 @@ Name: %{name}
|
|||||||
Version: %{version}
|
Version: %{version}
|
||||||
Release: %{release}
|
Release: %{release}
|
||||||
Source: %{name}-%{version}.tar.gz
|
Source: %{name}-%{version}.tar.gz
|
||||||
#Patch: %{name}-%{version}.patch
|
Patch: %{name}-%{version}.patch
|
||||||
Copyright: GPL
|
Copyright: GPL
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
BuildRoot: %{_tmppath}/%{name}-buildroot
|
BuildRoot: %{_tmppath}/%{name}-buildroot
|
||||||
@@ -52,7 +52,7 @@ modules provide for navigating and modifying MIME parts.
|
|||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup
|
%setup
|
||||||
#%patch -p1
|
%patch -p1
|
||||||
|
|
||||||
%build
|
%build
|
||||||
env CFLAGS="$RPM_OPT_FLAGS" %{python} setup.py build
|
env CFLAGS="$RPM_OPT_FLAGS" %{python} setup.py build
|
||||||
@@ -166,6 +166,12 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
/usr/share/sendmail-cf/hack/rhsbl.m4
|
/usr/share/sendmail-cf/hack/rhsbl.m4
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-4
|
||||||
|
- Limit each CNAME chain independently like PTR and MX
|
||||||
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-3
|
||||||
|
- Limit CNAME lookups (regression)
|
||||||
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-2
|
||||||
|
- Handle corrupt ZIP attachments
|
||||||
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-1
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-1
|
||||||
- Strict processing limits per SPF RFC
|
- Strict processing limits per SPF RFC
|
||||||
- Fixed several parsing bugs under RFC
|
- Fixed several parsing bugs under RFC
|
||||||
@@ -174,7 +180,6 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
- Extended SPF processing results beyond strict RFC limits
|
- Extended SPF processing results beyond strict RFC limits
|
||||||
- Support original SES for local bounce protection (requires pysrs-0.30.10)
|
- Support original SES for local bounce protection (requires pysrs-0.30.10)
|
||||||
- Callback exception processing option in milter module
|
- Callback exception processing option in milter module
|
||||||
- Handle corrupt ZIP attachments
|
|
||||||
* Thu Jun 16 2005 Stuart Gathman <stuart@bmsi.com> 0.8.1-1
|
* Thu Jun 16 2005 Stuart Gathman <stuart@bmsi.com> 0.8.1-1
|
||||||
- Fix zip in zip loop in mime.py
|
- Fix zip in zip loop in mime.py
|
||||||
- Fix HeaderParseError in bms.py header callback
|
- Fix HeaderParseError in bms.py header callback
|
||||||
|
|||||||
@@ -47,8 +47,24 @@ For news, bugfixes, etc. visit the home page for this implementation at
|
|||||||
# Terrence is not responding to email.
|
# Terrence is not responding to email.
|
||||||
#
|
#
|
||||||
# $Log$
|
# $Log$
|
||||||
# Revision 1.11 2005/07/20 03:30:04 customdesigned
|
# Revision 1.31 2005/07/22 02:11:50 customdesigned
|
||||||
# Check pydspam version for honeypot, include latest pyspf changes.
|
# Use dictionary to check for CNAME loops. Check limit independently for
|
||||||
|
# each top level name, just like for PTR.
|
||||||
|
#
|
||||||
|
# Revision 1.30 2005/07/21 20:07:31 customdesigned
|
||||||
|
# Translate DNS error in DNSLookup. This completely isolates DNS
|
||||||
|
# dependencies to the DNSLookup method.
|
||||||
|
#
|
||||||
|
# Revision 1.29 2005/07/21 17:49:39 customdesigned
|
||||||
|
# My best guess at what RFC intended for limiting CNAME loops.
|
||||||
|
#
|
||||||
|
# Revision 1.28 2005/07/21 17:37:08 customdesigned
|
||||||
|
# Break out external DNSLookup method so that test suite can
|
||||||
|
# duplicate CNAME loop bug. Test zone data dictionary now
|
||||||
|
# mirrors structure of real DNS.
|
||||||
|
#
|
||||||
|
# Revision 1.27 2005/07/21 15:26:06 customdesigned
|
||||||
|
# First cut at updating docs. Test suite is obsolete.
|
||||||
#
|
#
|
||||||
# Revision 1.26 2005/07/20 03:12:40 customdesigned
|
# Revision 1.26 2005/07/20 03:12:40 customdesigned
|
||||||
# When not in strict mode, don't give PermErr for bad mechanism until
|
# When not in strict mode, don't give PermErr for bad mechanism until
|
||||||
@@ -256,6 +272,16 @@ if not hasattr(DNS.Type,'SPF'):
|
|||||||
DNS.Type.typemap[99] = 'SPF'
|
DNS.Type.typemap[99] = 'SPF'
|
||||||
DNS.Lib.RRunpacker.getSPFdata = DNS.Lib.RRunpacker.getTXTdata
|
DNS.Lib.RRunpacker.getSPFdata = DNS.Lib.RRunpacker.getTXTdata
|
||||||
|
|
||||||
|
def DNSLookup(name,qtype):
|
||||||
|
try:
|
||||||
|
req = DNS.DnsRequest(name, qtype=qtype)
|
||||||
|
resp = req.req()
|
||||||
|
#resp.show()
|
||||||
|
# key k: ('wayforward.net', 'A'), value v
|
||||||
|
return [((a['name'], a['typename']), a['data']) for a in resp.answers]
|
||||||
|
except DNS.DNSError,x:
|
||||||
|
raise TempError,'DNS ' + str(x)
|
||||||
|
|
||||||
# 32-bit IPv4 address mask
|
# 32-bit IPv4 address mask
|
||||||
MASK = 0xFFFFFFFFL
|
MASK = 0xFFFFFFFFL
|
||||||
|
|
||||||
@@ -308,6 +334,7 @@ DEFAULT_SPF = 'v=spf1 a/24 mx/24 ptr'
|
|||||||
MAX_LOOKUP = 10 #draft-schlitt-spf-classic-02 Para 10.1
|
MAX_LOOKUP = 10 #draft-schlitt-spf-classic-02 Para 10.1
|
||||||
MAX_MX = 10 #draft-schlitt-spf-classic-02 Para 10.1
|
MAX_MX = 10 #draft-schlitt-spf-classic-02 Para 10.1
|
||||||
MAX_PTR = 10 #draft-schlitt-spf-classic-02 Para 10.1
|
MAX_PTR = 10 #draft-schlitt-spf-classic-02 Para 10.1
|
||||||
|
MAX_CNAME = 10 # analogous interpretation to MAX_PTR
|
||||||
MAX_RECURSION = 20
|
MAX_RECURSION = 20
|
||||||
ALL_MECHANISMS = ('a', 'mx', 'ptr', 'exists', 'include', 'ip4', 'ip6', 'all')
|
ALL_MECHANISMS = ('a', 'mx', 'ptr', 'exists', 'include', 'ip4', 'ip6', 'all')
|
||||||
COMMON_MISTAKES = { 'prt': 'ptr', 'ip': 'ip4', 'ipv4': 'ip4', 'ipv6': 'ip6' }
|
COMMON_MISTAKES = { 'prt': 'ptr', 'ip': 'ip4', 'ipv4': 'ip4', 'ipv6': 'ip6' }
|
||||||
@@ -412,6 +439,9 @@ class query(object):
|
|||||||
>>> q.check(spf='v=spf1 ip4:192.0.0.0/8 ?all moo')
|
>>> q.check(spf='v=spf1 ip4:192.0.0.0/8 ?all moo')
|
||||||
('unknown', 550, 'SPF Permanent Error: Unknown mechanism found: moo')
|
('unknown', 550, 'SPF Permanent Error: Unknown mechanism found: moo')
|
||||||
|
|
||||||
|
>>> q.check(spf='v=spf1 =a ?all moo')
|
||||||
|
('unknown', 550, 'SPF Permanent Error: Unknown qualifier, IETF draft para 4.6.1, found in: =a')
|
||||||
|
|
||||||
>>> q.check(spf='v=spf1 ip4:192.0.0.0/8 ~all')
|
>>> q.check(spf='v=spf1 ip4:192.0.0.0/8 ~all')
|
||||||
('pass', 250, 'sender SPF verified')
|
('pass', 250, 'sender SPF verified')
|
||||||
|
|
||||||
@@ -453,8 +483,6 @@ class query(object):
|
|||||||
self.perm_error.ext = rc
|
self.perm_error.ext = rc
|
||||||
raise self.perm_error
|
raise self.perm_error
|
||||||
return rc
|
return rc
|
||||||
except DNS.DNSError,x:
|
|
||||||
return ('error', 450, 'SPF DNS Error: ' + str(x))
|
|
||||||
except TempError,x:
|
except TempError,x:
|
||||||
return ('error', 450, 'SPF Temporary Error: ' + str(x))
|
return ('error', 450, 'SPF Temporary Error: ' + str(x))
|
||||||
except PermError,x:
|
except PermError,x:
|
||||||
@@ -847,7 +875,7 @@ class query(object):
|
|||||||
"""Get a list of domain names for an IP address."""
|
"""Get a list of domain names for an IP address."""
|
||||||
return self.dns(reverse_dots(i) + ".in-addr.arpa", 'PTR')
|
return self.dns(reverse_dots(i) + ".in-addr.arpa", 'PTR')
|
||||||
|
|
||||||
def dns(self, name, qtype):
|
def dns(self, name, qtype, cnames=None):
|
||||||
"""DNS query.
|
"""DNS query.
|
||||||
|
|
||||||
If the result is in cache, return that. Otherwise pull the
|
If the result is in cache, return that. Otherwise pull the
|
||||||
@@ -864,19 +892,21 @@ class query(object):
|
|||||||
result = self.cache.get( (name, qtype) )
|
result = self.cache.get( (name, qtype) )
|
||||||
cname = None
|
cname = None
|
||||||
if not result:
|
if not result:
|
||||||
req = DNS.DnsRequest(name, qtype=qtype)
|
for k,v in DNSLookup(name,qtype):
|
||||||
resp = req.req()
|
|
||||||
#resp.show()
|
|
||||||
for a in resp.answers:
|
|
||||||
# key k: ('wayforward.net', 'A'), value v
|
|
||||||
k, v = (a['name'], a['typename']), a['data']
|
|
||||||
if k == (name, 'CNAME'):
|
if k == (name, 'CNAME'):
|
||||||
cname = v
|
cname = v
|
||||||
self.cache.setdefault(k, []).append(v)
|
self.cache.setdefault(k, []).append(v)
|
||||||
result = self.cache.get( (name, qtype), [])
|
result = self.cache.get( (name, qtype), [])
|
||||||
if not result and cname:
|
if not result and cname:
|
||||||
self.check_lookups()
|
if not cnames:
|
||||||
result = self.dns(cname, qtype)
|
cnames = {}
|
||||||
|
elif len(cnames) >= MAX_CNAME:
|
||||||
|
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)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_header(self,res,receiver=None):
|
def get_header(self,res,receiver=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user