SPF updates from pyspf.
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
When training with spam, REJECT after data so that mistakenly blacklisted
|
||||||
|
senders at least get an error.
|
||||||
|
|
||||||
Reporting explanation for failure should show source of sender
|
Reporting explanation for failure should show source of sender
|
||||||
provided explanation.
|
provided explanation.
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,19 @@ For news, bugfixes, etc. visit the home page for this implementation at
|
|||||||
# Development taken over by Stuart Gathman <stuart@bmsi.com>.
|
# Development taken over by Stuart Gathman <stuart@bmsi.com>.
|
||||||
#
|
#
|
||||||
# $Log$
|
# $Log$
|
||||||
|
# Revision 1.100 2006/10/04 02:14:04 customdesigned
|
||||||
|
# Remove incomplete saving of result. Was messing up bmsmilter. Would
|
||||||
|
# be useful if done consistently - and disabled when passing spf= to check().
|
||||||
|
#
|
||||||
|
# Revision 1.99 2006/10/03 21:00:26 customdesigned
|
||||||
|
# Correct fat fingered merge error.
|
||||||
|
#
|
||||||
|
# Revision 1.98 2006/10/03 17:35:45 customdesigned
|
||||||
|
# Provide python inet_ntop and inet_pton when not socket.has_ipv6
|
||||||
|
#
|
||||||
|
# Revision 1.97 2006/10/02 17:10:13 customdesigned
|
||||||
|
# Test and fix for uppercase macros.
|
||||||
|
#
|
||||||
# Revision 1.96 2006/10/01 01:27:54 customdesigned
|
# Revision 1.96 2006/10/01 01:27:54 customdesigned
|
||||||
# Switch to pymilter lax processing convention:
|
# Switch to pymilter lax processing convention:
|
||||||
# Always return strict result, extended result in q.perm_error.ext
|
# Always return strict result, extended result in q.perm_error.ext
|
||||||
@@ -165,6 +178,7 @@ import re
|
|||||||
import socket # for inet_ntoa() and inet_aton()
|
import socket # for inet_ntoa() and inet_aton()
|
||||||
import struct # for pack() and unpack()
|
import struct # for pack() and unpack()
|
||||||
import time # for time()
|
import time # for time()
|
||||||
|
import urllib # for quote()
|
||||||
|
|
||||||
import DNS # http://pydns.sourceforge.net
|
import DNS # http://pydns.sourceforge.net
|
||||||
if not hasattr(DNS.Type, 'SPF'):
|
if not hasattr(DNS.Type, 'SPF'):
|
||||||
@@ -382,7 +396,7 @@ class query(object):
|
|||||||
self.l, self.o = split_email(s, h)
|
self.l, self.o = split_email(s, h)
|
||||||
self.t = str(int(time.time()))
|
self.t = str(int(time.time()))
|
||||||
self.d = self.o
|
self.d = self.o
|
||||||
self.p = None
|
self.p = None # lazy evaluation
|
||||||
if receiver:
|
if receiver:
|
||||||
self.r = receiver
|
self.r = receiver
|
||||||
else:
|
else:
|
||||||
@@ -397,6 +411,7 @@ class query(object):
|
|||||||
self.lookups = 0
|
self.lookups = 0
|
||||||
# strict can be False, True, or 2 (numeric) for harsh
|
# strict can be False, True, or 2 (numeric) for harsh
|
||||||
self.strict = strict
|
self.strict = strict
|
||||||
|
if i:
|
||||||
self.set_ip(i)
|
self.set_ip(i)
|
||||||
|
|
||||||
def set_ip(self, i):
|
def set_ip(self, i):
|
||||||
@@ -405,8 +420,7 @@ class query(object):
|
|||||||
self.ip = addr2bin(i)
|
self.ip = addr2bin(i)
|
||||||
ip6 = False
|
ip6 = False
|
||||||
else:
|
else:
|
||||||
assert socket.has_ipv6,"No IPv6 python support"
|
self.ip = bin2long6(inet_pton(i))
|
||||||
self.ip = bin2long6(socket.inet_pton(socket.AF_INET6, i))
|
|
||||||
if (self.ip >> 32) == 0xFFFF: # IP4 mapped address
|
if (self.ip >> 32) == 0xFFFF: # IP4 mapped address
|
||||||
self.ip = self.ip & 0xFFFFFFFFL
|
self.ip = self.ip & 0xFFFFFFFFL
|
||||||
ip6 = False
|
ip6 = False
|
||||||
@@ -414,7 +428,7 @@ class query(object):
|
|||||||
ip6 = True
|
ip6 = True
|
||||||
# NOTE: self.A is not lowercase, so isn't a macro. See query.expand()
|
# NOTE: self.A is not lowercase, so isn't a macro. See query.expand()
|
||||||
if ip6:
|
if ip6:
|
||||||
self.c = socket.inet_ntop(socket.AF_INET6,
|
self.c = inet_ntop(
|
||||||
struct.pack("!QQ", self.ip>>64, self.ip&0xFFFFFFFFFFFFFFFFL))
|
struct.pack("!QQ", self.ip>>64, self.ip&0xFFFFFFFFFFFFFFFFL))
|
||||||
self.i = '.'.join(list('%032X'%self.ip))
|
self.i = '.'.join(list('%032X'%self.ip))
|
||||||
self.A = 'AAAA'
|
self.A = 'AAAA'
|
||||||
@@ -529,7 +543,6 @@ class query(object):
|
|||||||
# will continue processing. However, the exception
|
# will continue processing. However, the exception
|
||||||
# that strict processing would raise is saved here
|
# that strict processing would raise is saved here
|
||||||
self.perm_error = None
|
self.perm_error = None
|
||||||
self.result = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.lookups = 0
|
self.lookups = 0
|
||||||
@@ -539,7 +552,6 @@ class query(object):
|
|||||||
spf = insert_libspf_local_policy(
|
spf = insert_libspf_local_policy(
|
||||||
spf, self.libspf_local)
|
spf, self.libspf_local)
|
||||||
rc = self.check1(spf, self.d, 0)
|
rc = self.check1(spf, self.d, 0)
|
||||||
self.result = rc[0]
|
|
||||||
if self.perm_error:
|
if self.perm_error:
|
||||||
# lax processing encountered a permerror, but continued
|
# lax processing encountered a permerror, but continued
|
||||||
self.perm_error.ext = rc
|
self.perm_error.ext = rc
|
||||||
@@ -840,7 +852,7 @@ class query(object):
|
|||||||
elif m == 'ip6':
|
elif m == 'ip6':
|
||||||
if self.v == 'ip6': # match own connection type only
|
if self.v == 'ip6': # match own connection type only
|
||||||
try:
|
try:
|
||||||
arg = socket.inet_pton(socket.AF_INET6,arg)
|
arg = inet_pton(arg)
|
||||||
if self.cidrmatch([arg], cidrlength): break
|
if self.cidrmatch([arg], cidrlength): break
|
||||||
except socket.error:
|
except socket.error:
|
||||||
raise PermError('syntax error', mech)
|
raise PermError('syntax error', mech)
|
||||||
@@ -1006,8 +1018,10 @@ class query(object):
|
|||||||
if expansion:
|
if expansion:
|
||||||
if expansion == self:
|
if expansion == self:
|
||||||
raise PermError('Unknown Macro Encountered', macro)
|
raise PermError('Unknown Macro Encountered', macro)
|
||||||
result += expand_one(expansion, macro[3:-1],
|
e = expand_one(expansion, macro[3:-1], JOINERS.get(letter))
|
||||||
JOINERS.get(letter))
|
if letter != macro[2]:
|
||||||
|
e = urllib.quote(e)
|
||||||
|
result += e
|
||||||
|
|
||||||
end = i.end()
|
end = i.end()
|
||||||
result += str[end:]
|
result += str[end:]
|
||||||
@@ -1357,11 +1371,104 @@ def addr2bin(str):
|
|||||||
"""
|
"""
|
||||||
return struct.unpack("!L", socket.inet_aton(str))[0]
|
return struct.unpack("!L", socket.inet_aton(str))[0]
|
||||||
|
|
||||||
if socket.has_ipv6:
|
|
||||||
def bin2long6(str):
|
def bin2long6(str):
|
||||||
h, l = struct.unpack("!QQ", str)
|
h, l = struct.unpack("!QQ", str)
|
||||||
return h << 64 | l
|
return h << 64 | l
|
||||||
|
|
||||||
|
if socket.has_ipv6:
|
||||||
|
def inet_ntop(s):
|
||||||
|
return socket.inet_ntop(socket.AF_INET6,s)
|
||||||
|
def inet_pton(s):
|
||||||
|
return socket.inet_pton(socket.AF_INET6,s)
|
||||||
|
else:
|
||||||
|
def inet_ntop(s):
|
||||||
|
"""Convert ip6 address to standard hex notation.
|
||||||
|
Examples:
|
||||||
|
>>> inet_ntop(struct.pack("!HHHHHHHH",0,0,0,0,0,0xFFFF,0x0102,0x0304))
|
||||||
|
'::FFFF:1.2.3.4'
|
||||||
|
>>> inet_ntop(struct.pack("!HHHHHHHH",0x1234,0x5678,0,0,0,0,0x0102,0x0304))
|
||||||
|
'1234:5678::102:304'
|
||||||
|
>>> inet_ntop(struct.pack("!HHHHHHHH",0,0,0,0x1234,0x5678,0,0x0102,0x0304))
|
||||||
|
'::1234:5678:0:102:304'
|
||||||
|
>>> inet_ntop(struct.pack("!HHHHHHHH",0x1234,0x5678,0,0x0102,0x0304,0,0,0))
|
||||||
|
'1234:5678:0:102:304::'
|
||||||
|
>>> inet_ntop(struct.pack("!HHHHHHHH",0,0,0,0,0,0,0,0))
|
||||||
|
'::'
|
||||||
|
"""
|
||||||
|
# convert to 8 words
|
||||||
|
a = struct.unpack("!HHHHHHHH",s)
|
||||||
|
n = (0,0,0,0,0,0,0,0) # null ip6
|
||||||
|
if a == n: return '::'
|
||||||
|
# check for ip4 mapped
|
||||||
|
if a[:5] == (0,0,0,0,0) and a[5] in (0,0xFFFF):
|
||||||
|
ip4 = '.'.join([str(i) for i in struct.unpack("!HHHHHHBBBB",s)[6:]])
|
||||||
|
if a[5]:
|
||||||
|
return "::FFFF:" + ip4
|
||||||
|
return "::" + ip4
|
||||||
|
# find index of longest sequence of 0
|
||||||
|
for l in (7,6,5,4,3,2,1):
|
||||||
|
e = n[:l]
|
||||||
|
for i in range(9-l):
|
||||||
|
if a[i:i+l] == e:
|
||||||
|
if i == 0:
|
||||||
|
return ':'+':%x'*(8-l) % a[l:]
|
||||||
|
if i == 8 - l:
|
||||||
|
return '%x:'*(8-l) % a[:-l] + ':'
|
||||||
|
return '%x:'*i % a[:i] + ':%x'*(8-l-i) % a[i+l:]
|
||||||
|
return "%x:%x:%x:%x:%x:%x:%x:%x" % a
|
||||||
|
|
||||||
|
def inet_pton(p):
|
||||||
|
"""Convert ip6 standard hex notation to ip6 address.
|
||||||
|
Examples:
|
||||||
|
>>> struct.unpack('!HHHHHHHH',inet_pton('::'))
|
||||||
|
(0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
>>> struct.unpack('!HHHHHHHH',inet_pton('::1234'))
|
||||||
|
(0, 0, 0, 0, 0, 0, 0, 4660)
|
||||||
|
>>> struct.unpack('!HHHHHHHH',inet_pton('1234::'))
|
||||||
|
(4660, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
>>> struct.unpack('!HHHHHHHH',inet_pton('1234::5678'))
|
||||||
|
(4660, 0, 0, 0, 0, 0, 0, 22136)
|
||||||
|
>>> struct.unpack('!HHHHHHHH',inet_pton('::FFFF:1.2.3.4'))
|
||||||
|
(0, 0, 0, 0, 0, 65535, 258, 772)
|
||||||
|
>>> struct.unpack('!HHHHHHHH',inet_pton('1.2.3.4'))
|
||||||
|
(0, 0, 0, 0, 0, 65535, 258, 772)
|
||||||
|
>>> try: inet_pton('::1.2.3.4.5')
|
||||||
|
... except ValueError,x: print x
|
||||||
|
::1.2.3.4.5
|
||||||
|
"""
|
||||||
|
if p == '::':
|
||||||
|
return '\0'*16
|
||||||
|
s = p
|
||||||
|
m = RE_IP4.search(s)
|
||||||
|
try:
|
||||||
|
if m:
|
||||||
|
pos = m.start()
|
||||||
|
ip4 = [int(i) for i in s[pos:].split('.')]
|
||||||
|
if not pos:
|
||||||
|
return struct.pack('!QLBBBB',0,65535,*ip4)
|
||||||
|
s = s[:pos]+'%x%02x:%x%02x'%tuple(ip4)
|
||||||
|
a = s.split('::')
|
||||||
|
if len(a) == 2:
|
||||||
|
l,r = a
|
||||||
|
if not l:
|
||||||
|
r = r.split(':')
|
||||||
|
return struct.pack('!HHHHHHHH',
|
||||||
|
*[0]*(8-len(r)) + [int(s,16) for s in r])
|
||||||
|
if not r:
|
||||||
|
l = l.split(':')
|
||||||
|
return struct.pack('!HHHHHHHH',
|
||||||
|
*[int(s,16) for s in l] + [0]*(8-len(l)))
|
||||||
|
l = l.split(':')
|
||||||
|
r = r.split(':')
|
||||||
|
return struct.pack('!HHHHHHHH',
|
||||||
|
*[int(s,16) for s in l] + [0]*(8-len(l)-len(r))
|
||||||
|
+ [int(s,16) for s in r])
|
||||||
|
if len(a) == 1:
|
||||||
|
return struct.pack('!HHHHHHHH',
|
||||||
|
*[int(s,16) for s in a[0].split(':')])
|
||||||
|
except ValueError: pass
|
||||||
|
raise ValueError,p
|
||||||
|
|
||||||
def expand_one(expansion, str, joiner):
|
def expand_one(expansion, str, joiner):
|
||||||
if not str:
|
if not str:
|
||||||
return expansion
|
return expansion
|
||||||
|
|||||||
Reference in New Issue
Block a user