Skip to content
Snippets Groups Projects
Commit 8df5cd02 authored by Stuart Gathman's avatar Stuart Gathman
Browse files

Limit CNAME chains independently of DNS lookup limit

parent 0cbfc0d2
No related branches found
No related tags found
No related merge requests found
...@@ -24,7 +24,7 @@ ALT="Viewable With Any Browser" BORDER="0"></A> ...@@ -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. ...@@ -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
......
%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} ...@@ -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. ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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'): ...@@ -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' ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment