Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
pymilter
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
misc
pymilter
Commits
8ad4b161
Commit
8ad4b161
authored
Jul 14, 2005
by
Stuart Gathman
Browse files
Options
Downloads
Patches
Plain Diff
Import bug fixes from pyspf module. CID xml support removed.
parent
b28a56ea
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
spf.py
+145
-182
145 additions, 182 deletions
spf.py
with
145 additions
and
182 deletions
spf.py
+
145
−
182
View file @
8ad4b161
#!/usr/bin/env python
#!/usr/bin/env python
"""
SPF (Sender
-Permitted From
) implementation.
"""
SPF (Sender
Policy Framework
) implementation.
Copyright (c) 2003, Terence Way
Copyright (c) 2003, Terence Way
Portions Copyright (c) 2004,2005 Stuart Gathman <stuart@bmsi.com>
Portions Copyright (c) 2004,2005 Stuart Gathman <stuart@bmsi.com>
...
@@ -19,10 +19,11 @@ AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
...
@@ -19,10 +19,11 @@ AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
For more information about SPF, a tool against email forgery, see
For more information about SPF, a tool against email forgery, see
http://spf.pobox.com
http://spf.pobox.com
/
For news, bugfixes, etc. visit the home page for this implementation at
For news, bugfixes, etc. visit the home page for this implementation at
http://www.wayforward.net/spf/
http://www.wayforward.net/spf/
http://sourceforge.net/projects/pymilter/
"""
"""
# Changes:
# Changes:
...
@@ -46,6 +47,37 @@ For news, bugfixes, etc. visit the home page for this implementation at
...
@@ -46,6 +47,37 @@ 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.7 2005/07/12 21:43:56 kitterma
# Added processing to clarify some cases of unknown
# qualifier errors (to distinguish between unknown qualifier and
# unknown mechanism).
# Also cleaned up comments from previous updates.
#
# Revision 1.6 2005/06/29 14:46:26 customdesigned
# Distinguish trivial recursion from missing arg for diagnostic purposes.
#
# Revision 1.5 2005/06/28 17:48:56 customdesigned
# Support extended processing results when a PermError should strictly occur.
#
# Revision 1.4 2005/06/22 15:54:54 customdesigned
# Correct spelling.
#
# Revision 1.3 2005/06/22 00:08:24 kitterma
# Changes from draft-mengwong overall DNS lookup and recursion
# depth limits to draft-schlitt-spf-classic-02 DNS lookup, MX lookup, and
# PTR lookup limits. Recursion code is still present and functioning, but
# it should be impossible to trip it.
#
# Revision 1.2 2005/06/21 16:46:09 kitterma
# Updated definition of SPF, added reference to the sourceforge project site,
# and deleted obsolete Microsoft Caller ID for Email XML translation routine.
#
# Revision 1.1.1.1 2005/06/20 19:57:32 customdesigned
# Move Python SPF to its own module.
#
# Revision 1.5 2005/06/14 20:31:26 customdesigned
# fix pychecker nits
#
# Revision 1.4 2005/06/02 04:18:55 customdesigned
# Revision 1.4 2005/06/02 04:18:55 customdesigned
# Update copyright notices after reading article on /.
# Update copyright notices after reading article on /.
#
#
...
@@ -144,135 +176,6 @@ import struct # for pack() and unpack()
...
@@ -144,135 +176,6 @@ import struct # for pack() and unpack()
import
time
# for time()
import
time
# for time()
import
DNS
# http://pydns.sourceforge.net
import
DNS
# http://pydns.sourceforge.net
import
xml.sax
# -------------------------------------------------------------------------
# Convert a MS Caller-ID entry (XML) to a SPF entry
#
# (c) 2004 by Ernesto Baschny
# (c) 2004 Python version by Stuart Gathman
#
# Date: 2004-02-25
#
# A complete reverse translation (SPF -> CID) might be impossible, since
# there are no ways to handle:
# - PTR and EXISTS mechanism
# - MX mechanism with an different domain as argument
# - macros
#
# References:
# http://www.microsoft.com/mscorp/twc/privacy/spam_callerid.mspx
# http://spf.pobox.com/
#
# Known bugs:
# - Currently it won't handle the exclusions provided in the A and R
# tags (prefix '!'). They will show up "as-is" in the SPF record
# - I really haven't read the MS-CID specs in-depth, so there are probably
# other bugs too :)
#
# Ernesto Baschny <ernst@baschny.de>
#
class
CIDParser
(
xml
.
sax
.
ContentHandler
):
"
Convert a MS Caller-ID entry (XML) to a SPF entry.
"
def
__init__
(
self
,
q
=
None
):
self
.
spf
=
[]
self
.
action
=
'
-all
'
self
.
has_servers
=
None
self
.
spf_entry
=
None
if
q
:
self
.
spf_query
=
q
else
:
self
.
spf_query
=
query
(
i
=
'
127.0.0.1
'
,
s
=
'
localhost
'
,
h
=
'
unknown
'
)
def
startElement
(
self
,
tag
,
attr
):
if
tag
==
'
m
'
:
if
self
.
has_servers
!=
None
and
not
self
.
has_servers
:
raise
ValueError
(
"
Declared <noMailServers\> and later <m>, this CID entry is not valid.
"
)
self
.
has_servers
=
True
elif
tag
==
'
noMailServers
'
:
if
self
.
has_servers
:
raise
ValueError
(
"
Declared <m> and later <noMailServers\>, this CID entry is not valid.
"
)
self
.
has_servers
=
False
elif
tag
==
'
ep
'
:
if
attr
.
has_key
(
'
testing
'
)
and
attr
.
getValue
(
'
testing
'
)
==
'
true
'
:
# A CID with 'testing' found:
# From the MS-specs:
# "Documents in which such attribute is present with a true
# value SHOULD be entirely ignored (one should act as if the
# document were absent)"
# From the SPF-specs:
# "Neutral (?): The SPF client MUST proceed as if a domain did
# not publish SPF data."
# So we set SPF action to "neutral":
self
.
action
=
'
?all
'
elif
tag
==
'
mx
'
:
# The empty MX-tag, same as SPF's MX-mechanism
self
.
spf
.
append
(
'
mx
'
)
self
.
tag
=
tag
def
characters
(
self
,
text
):
tag
=
self
.
tag
# Remove starting and trailing spaces from text:
text
=
text
.
strip
()
if
tag
==
'
a
'
or
tag
==
'
r
'
:
# The A and R tags from MS-CID are both handled by the
# ipv4/6-mechanisms from SPF:
if
text
.
find
(
'
:
'
)
<
0
:
mechanism
=
'
ip4
'
else
:
mechanism
=
'
ip6
'
self
.
spf
.
append
(
mechanism
+
'
:
'
+
text
)
elif
tag
==
'
indirect
'
:
# MS-CID's indirect is "sort of" the include from SPF:
# Not really true, because the <indirect> tag from MS-CID also
# provides a fallback in case the included domain doesn't provide
# _ep-records: The inbound MX-servers of the included domains
# are added to the list of allowed outgoing mailservers for the
# domain that declared the _ep-record with the <indirect> tag.
# In SPF you would use the 'mx:domain' to handle this, but this
# wouldn't depend on referred domain having or not SPF-records.
cid_xml
=
self
.
cid_txt
(
text
)
if
cid_xml
:
p
=
CIDParser
()
xml
.
sax
.
parseString
(
cid_xml
,
p
)
if
p
.
has_servers
!=
False
:
self
.
spf
+=
p
.
spf
else
:
self
.
spf
.
append
(
'
mx:
'
+
text
)
def
cid_txt
(
self
,
domain
):
q
=
self
.
spf_query
domain
=
'
_ep.
'
+
domain
a
=
q
.
dns_txt
(
domain
)
if
not
a
:
return
None
if
a
[
0
].
lower
().
startswith
(
'
<ep
'
)
and
a
[
-
1
].
lower
().
endswith
(
'
</ep>
'
):
return
''
.
join
(
a
)
return
None
def
endElement
(
self
,
tag
):
if
tag
==
'
ep
'
:
# This is the end... assemble what we've got
spf_entry
=
[
'
v=spf1
'
]
if
self
.
has_servers
!=
False
:
spf_entry
+=
self
.
spf
spf_entry
.
append
(
self
.
action
)
self
.
spf_entry
=
'
'
.
join
(
spf_entry
)
def
spf_txt
(
self
,
cid_xml
):
if
not
cid_xml
.
startswith
(
'
<
'
):
cid_xml
=
self
.
cid_txt
(
cid_xml
)
if
not
cid_xml
:
return
None
# Parse the beast. Any XML-problem will be reported by xlm.sax
self
.
spf_entry
=
None
xml
.
sax
.
parseString
(
cid_xml
,
self
)
return
self
.
spf_entry
# 32-bit IPv4 address mask
# 32-bit IPv4 address mask
MASK
=
0xFFFFFFFF
L
MASK
=
0xFFFFFFFF
L
...
@@ -297,7 +200,7 @@ RESULTS = {'+': 'pass', '-': 'fail', '?': 'neutral', '~': 'softfail',
...
@@ -297,7 +200,7 @@ RESULTS = {'+': 'pass', '-': 'fail', '?': 'neutral', '~': 'softfail',
'
none
'
:
'
none
'
,
'
deny
'
:
'
fail
'
}
'
none
'
:
'
none
'
,
'
deny
'
:
'
fail
'
}
EXPLANATIONS
=
{
'
pass
'
:
'
sender SPF verified
'
,
'
fail
'
:
'
access denied
'
,
EXPLANATIONS
=
{
'
pass
'
:
'
sender SPF verified
'
,
'
fail
'
:
'
access denied
'
,
'
unknown
'
:
'
SPF unknown
'
,
'
unknown
'
:
'
SPF unknown
(PermError)
'
,
'
softfail
'
:
'
domain in transition
'
,
'
softfail
'
:
'
domain in transition
'
,
'
neutral
'
:
'
access neither permitted nor denied
'
,
'
neutral
'
:
'
access neither permitted nor denied
'
,
'
none
'
:
''
'
none
'
:
''
...
@@ -320,7 +223,9 @@ except NameError:
...
@@ -320,7 +223,9 @@ except NameError:
DEFAULT_SPF
=
'
v=spf1 a/24 mx/24 ptr
'
DEFAULT_SPF
=
'
v=spf1 a/24 mx/24 ptr
'
# maximum DNS lookups allowed
# maximum DNS lookups allowed
MAX_LOOKUP
=
100
MAX_LOOKUP
=
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_RECURSION
=
20
MAX_RECURSION
=
20
class
TempError
(
Exception
):
class
TempError
(
Exception
):
...
@@ -328,10 +233,11 @@ class TempError(Exception):
...
@@ -328,10 +233,11 @@ class TempError(Exception):
class
PermError
(
Exception
):
class
PermError
(
Exception
):
"
Permanent SPF error
"
"
Permanent SPF error
"
def
__init__
(
self
,
msg
,
mech
=
None
):
def
__init__
(
self
,
msg
,
mech
=
None
,
ext
=
None
):
Exception
.
__init__
(
self
,
msg
,
mech
)
Exception
.
__init__
(
self
,
msg
,
mech
)
self
.
msg
=
msg
self
.
msg
=
msg
self
.
mech
=
mech
self
.
mech
=
mech
self
.
ext
=
ext
def
__str__
(
self
):
def
__str__
(
self
):
if
self
.
mech
:
if
self
.
mech
:
return
'
%s: %s
'
%
(
self
.
msg
,
self
.
mech
)
return
'
%s: %s
'
%
(
self
.
msg
,
self
.
mech
)
...
@@ -372,7 +278,7 @@ class query(object):
...
@@ -372,7 +278,7 @@ class query(object):
Also keeps cache: DNS cache.
Also keeps cache: DNS cache.
"""
"""
def
__init__
(
self
,
i
,
s
,
h
,
local
=
None
,
receiver
=
None
):
def
__init__
(
self
,
i
,
s
,
h
,
local
=
None
,
receiver
=
None
,
strict
=
True
):
self
.
i
,
self
.
s
,
self
.
h
=
i
,
s
,
h
self
.
i
,
self
.
s
,
self
.
h
=
i
,
s
,
h
if
not
s
and
h
:
if
not
s
and
h
:
self
.
s
=
'
postmaster@
'
+
h
self
.
s
=
'
postmaster@
'
+
h
...
@@ -387,6 +293,7 @@ class query(object):
...
@@ -387,6 +293,7 @@ class query(object):
self
.
exps
=
dict
(
EXPLANATIONS
)
self
.
exps
=
dict
(
EXPLANATIONS
)
self
.
local
=
local
# local policy
self
.
local
=
local
# local policy
self
.
lookups
=
0
self
.
lookups
=
0
self
.
strict
=
strict
def
set_default_explanation
(
self
,
exp
):
def
set_default_explanation
(
self
,
exp
):
exps
=
self
.
exps
exps
=
self
.
exps
...
@@ -412,6 +319,11 @@ class query(object):
...
@@ -412,6 +319,11 @@ class query(object):
result in [
'
fail
'
,
'
softfail
'
,
'
neutral
'
'
unknown
'
,
'
pass
'
,
'
error
'
]
result in [
'
fail
'
,
'
softfail
'
,
'
neutral
'
'
unknown
'
,
'
pass
'
,
'
error
'
]
"""
"""
self
.
mech
=
[]
# unknown mechanisms
self
.
mech
=
[]
# unknown mechanisms
# If not strict, certain PermErrors (mispelled
# mechanisms, strict processing limits exceeded)
# will continue processing. However, the exception
# that strict processing would raise is saved here
self
.
perm_error
=
None
if
self
.
i
.
startswith
(
'
127.
'
):
if
self
.
i
.
startswith
(
'
127.
'
):
return
(
'
pass
'
,
250
,
'
local connections always pass
'
)
return
(
'
pass
'
,
250
,
'
local connections always pass
'
)
...
@@ -421,7 +333,12 @@ class query(object):
...
@@ -421,7 +333,12 @@ class query(object):
spf
=
self
.
dns_spf
(
self
.
d
)
spf
=
self
.
dns_spf
(
self
.
d
)
if
self
.
local
and
spf
:
if
self
.
local
and
spf
:
spf
+=
'
'
+
self
.
local
spf
+=
'
'
+
self
.
local
return
self
.
check1
(
spf
,
self
.
d
,
0
)
rc
=
self
.
check1
(
spf
,
self
.
d
,
0
)
if
self
.
perm_error
:
# extended processing succeeded, but strict failed
self
.
perm_error
.
ext
=
rc
raise
self
.
perm_error
return
rc
except
DNS
.
DNSError
,
x
:
except
DNS
.
DNSError
,
x
:
return
(
'
error
'
,
450
,
'
SPF DNS Error:
'
+
str
(
x
))
return
(
'
error
'
,
450
,
'
SPF DNS Error:
'
+
str
(
x
))
except
TempError
,
x
:
except
TempError
,
x
:
...
@@ -431,8 +348,8 @@ class query(object):
...
@@ -431,8 +348,8 @@ class query(object):
self
.
mech
.
append
(
x
.
mech
)
self
.
mech
.
append
(
x
.
mech
)
# Pre-Lentczner draft treats this as an unknown result
# Pre-Lentczner draft treats this as an unknown result
# and equivalent to no SPF record.
# and equivalent to no SPF record.
#
return ('unknown', 550, 'SPF Permanent Error: ' + str(x))
return
(
'
unknown
'
,
550
,
'
SPF Permanent Error:
'
+
str
(
x
))
return
(
'
error
'
,
550
,
'
SPF Permanent Error:
'
+
str
(
x
))
#
return ('error', 550, 'SPF Permanent Error: ' + str(x))
def
check1
(
self
,
spf
,
domain
,
recursion
):
def
check1
(
self
,
spf
,
domain
,
recursion
):
# spf rfc: 3.7 Processing Limits
# spf rfc: 3.7 Processing Limits
...
@@ -479,6 +396,7 @@ class query(object):
...
@@ -479,6 +396,7 @@ class query(object):
exps
[
'
fail
'
]
=
exps
[
'
unknown
'
]
=
\
exps
[
'
fail
'
]
=
exps
[
'
unknown
'
]
=
\
self
.
get_explanation
(
m
[
1
])
self
.
get_explanation
(
m
[
1
])
elif
m
[
0
]
==
'
redirect
'
:
elif
m
[
0
]
==
'
redirect
'
:
self
.
check_lookups
()
redirect
=
self
.
expand
(
m
[
1
])
redirect
=
self
.
expand
(
m
[
1
])
elif
m
[
0
]
==
'
default
'
:
elif
m
[
0
]
==
'
default
'
:
# default=- is the same as default=fail
# default=- is the same as default=fail
...
@@ -502,11 +420,15 @@ class query(object):
...
@@ -502,11 +420,15 @@ class query(object):
# default pass
# default pass
result
=
'
pass
'
result
=
'
pass
'
if
m
in
[
'
a
'
,
'
mx
'
,
'
ptr
'
,
'
prt
'
,
'
exists
'
,
'
include
'
]:
if
m
in
(
'
a
'
,
'
mx
'
,
'
ptr
'
,
'
exists
'
,
'
include
'
):
self
.
check_lookups
()
arg
=
self
.
expand
(
arg
)
arg
=
self
.
expand
(
arg
)
if
m
==
'
include
'
:
if
m
==
'
include
'
:
if
arg
!=
self
.
d
:
if
arg
==
self
.
d
:
if
mech
!=
'
include
'
:
raise
PermError
(
'
include has trivial recursion
'
,
mech
)
raise
PermError
(
'
include mechanism missing domain
'
,
mech
)
res
,
code
,
txt
=
self
.
check1
(
self
.
dns_spf
(
arg
),
res
,
code
,
txt
=
self
.
check1
(
self
.
dns_spf
(
arg
),
arg
,
recursion
+
1
)
arg
,
recursion
+
1
)
if
res
==
'
pass
'
:
if
res
==
'
pass
'
:
...
@@ -516,8 +438,6 @@ class query(object):
...
@@ -516,8 +438,6 @@ class query(object):
'
No valid SPF record for included domain: %s
'
%
arg
,
'
No valid SPF record for included domain: %s
'
%
arg
,
mech
)
mech
)
continue
continue
else
:
raise
PermError
(
'
include mechanism missing domain
'
,
mech
)
elif
m
==
'
all
'
:
elif
m
==
'
all
'
:
break
break
...
@@ -536,6 +456,13 @@ class query(object):
...
@@ -536,6 +456,13 @@ class query(object):
break
break
elif
m
in
(
'
ip4
'
,
'
ipv4
'
,
'
ip
'
)
and
arg
!=
self
.
d
:
elif
m
in
(
'
ip4
'
,
'
ipv4
'
,
'
ip
'
)
and
arg
!=
self
.
d
:
try
:
if
m
!=
'
ip4
'
:
raise
PermError
(
'
Unknown mechanism found
'
,
mech
)
except
PermError
,
x
:
if
self
.
strict
:
raise
if
not
self
.
perm_error
:
self
.
perm_error
=
x
try
:
try
:
if
cidrmatch
(
self
.
i
,
[
arg
],
cidrlength
):
if
cidrmatch
(
self
.
i
,
[
arg
],
cidrlength
):
break
break
...
@@ -543,19 +470,41 @@ class query(object):
...
@@ -543,19 +470,41 @@ class query(object):
raise
PermError
(
'
syntax error
'
,
mech
)
raise
PermError
(
'
syntax error
'
,
mech
)
elif
m
in
(
'
ip6
'
,
'
ipv6
'
):
elif
m
in
(
'
ip6
'
,
'
ipv6
'
):
try
:
if
m
!=
'
ip6
'
:
raise
PermError
(
'
Unknown mechanism found
'
,
mech
)
except
PermError
,
x
:
if
self
.
strict
:
raise
if
not
self
.
perm_error
:
self
.
perm_error
=
x
# Until we support IPV6, we should never
# Until we support IPV6, we should never
# get an IPv6 connection. So this mech
# get an IPv6 connection. So this mech
# will never match.
# will never match.
pass
pass
elif
m
in
(
'
ptr
'
,
'
prt
'
):
elif
m
in
(
'
ptr
'
,
'
prt
'
):
if
domainmatch
(
self
.
validated_ptrs
(
self
.
i
),
try
:
arg
):
if
m
!=
'
ptr
'
:
raise
PermError
(
'
Unknown mechanism found
'
,
mech
)
except
PermError
,
x
:
if
self
.
strict
:
raise
if
not
self
.
perm_error
:
self
.
perm_error
=
x
self
.
check_lookups
()
if
domainmatch
(
self
.
validated_ptrs
(
self
.
i
),
arg
):
break
break
else
:
else
:
# unknown mechanisms cause immediate
unknown
# unknown mechanisms cause immediate
PermError
# abort results
# abort results
# first see if it might be an bad qualifier instead
# of an unknown mechanism (no change to the result, just
# fine tune the error).
# eat one character and try again:
m
=
m
[
1
:]
if
m
in
[
'
a
'
,
'
mx
'
,
'
ptr
'
,
'
exists
'
,
'
include
'
,
'
ip4
'
,
'
ip6
'
,
'
all
'
]:
raise
PermError
(
'
Unknown qualifier, IETF draft para 4.6.1, found in
'
,
mech
)
else
:
raise
PermError
(
'
Unknown mechanism found
'
,
mech
)
raise
PermError
(
'
Unknown mechanism found
'
,
mech
)
else
:
else
:
# no matches
# no matches
...
@@ -570,6 +519,17 @@ class query(object):
...
@@ -570,6 +519,17 @@ class query(object):
else
:
else
:
return
(
result
,
250
,
exps
[
result
])
return
(
result
,
250
,
exps
[
result
])
def
check_lookups
(
self
):
self
.
lookups
=
self
.
lookups
+
1
if
self
.
lookups
>
MAX_LOOKUP
:
try
:
if
self
.
strict
or
not
self
.
perm_error
:
raise
PermError
(
'
Too many DNS lookups
'
)
except
PermError
,
x
:
if
self
.
strict
or
self
.
lookups
>
MAX_LOOKUP
*
4
:
raise
x
self
.
perm_error
=
x
def
get_explanation
(
self
,
spec
):
def
get_explanation
(
self
,
spec
):
"""
Expand an explanation.
"""
"""
Expand an explanation.
"""
if
spec
:
if
spec
:
...
@@ -682,13 +642,6 @@ class query(object):
...
@@ -682,13 +642,6 @@ class query(object):
for
t
in
self
.
dns_txt
(
domain
+
'
._spf.
'
+
DELEGATE
)
for
t
in
self
.
dns_txt
(
domain
+
'
._spf.
'
+
DELEGATE
)
if
t
.
startswith
(
'
v=spf1
'
)
if
t
.
startswith
(
'
v=spf1
'
)
]
]
if
not
a
:
# No SPF record: convert and return CID if present
p
=
CIDParser
(
q
=
self
)
try
:
return
p
.
spf_txt
(
domain
)
except
xml
.
sax
.
_exceptions
.
SAXParseException
:
raise
PermError
(
"
Caller-ID parse error
"
,
domain
)
if
len
(
a
)
==
1
:
if
len
(
a
)
==
1
:
return
a
[
0
]
return
a
[
0
]
...
@@ -739,15 +692,22 @@ class query(object):
...
@@ -739,15 +692,22 @@ class query(object):
pre: qtype in [
'
A
'
,
'
AAAA
'
,
'
MX
'
,
'
PTR
'
,
'
TXT
'
,
'
SPF
'
]
pre: qtype in [
'
A
'
,
'
AAAA
'
,
'
MX
'
,
'
PTR
'
,
'
TXT
'
,
'
SPF
'
]
post: isinstance(__return__, types.ListType)
post: isinstance(__return__, types.ListType)
"""
"""
self
.
lookups
+=
1
if
self
.
lookups
>
MAX_LOOKUP
:
raise
PermError
(
'
Too many DNS lookups
'
)
result
=
self
.
cache
.
get
(
(
name
,
qtype
)
)
result
=
self
.
cache
.
get
(
(
name
,
qtype
)
)
cname
=
None
cname
=
None
if
not
result
:
if
not
result
:
mxcount
=
0
ptrcount
=
0
req
=
DNS
.
DnsRequest
(
name
,
qtype
=
qtype
)
req
=
DNS
.
DnsRequest
(
name
,
qtype
=
qtype
)
resp
=
req
.
req
()
resp
=
req
.
req
()
for
a
in
resp
.
answers
:
for
a
in
resp
.
answers
:
if
a
[
'
typename
'
]
==
'
MX
'
:
mxcount
=
mxcount
+
1
if
mxcount
>
MAX_MX
:
raise
PermError
(
'
Too many MX lookups
'
)
if
a
[
'
typename
'
]
==
'
PTR
'
:
ptrcount
=
ptrcount
+
1
if
ptrcount
>
MAX_PTR
:
raise
PermError
(
'
Too many PTR lookups
'
)
# key k: ('wayforward.net', 'A'), value v
# key k: ('wayforward.net', 'A'), value v
k
,
v
=
(
a
[
'
name
'
],
a
[
'
typename
'
]),
a
[
'
data
'
]
k
,
v
=
(
a
[
'
name
'
],
a
[
'
typename
'
]),
a
[
'
data
'
]
if
k
==
(
name
,
'
CNAME
'
):
if
k
==
(
name
,
'
CNAME
'
):
...
@@ -838,6 +798,9 @@ def parse_mechanism(str, d):
...
@@ -838,6 +798,9 @@ def parse_mechanism(str, d):
>>>
parse_mechanism
(
'
a:bar.com/16
'
,
'
foo.com
'
)
>>>
parse_mechanism
(
'
a:bar.com/16
'
,
'
foo.com
'
)
(
'
a
'
,
'
bar.com
'
,
16
)
(
'
a
'
,
'
bar.com
'
,
16
)
>>>
parse_mechanism
(
'
A:bar.com/16
'
,
'
foo.com
'
)
(
'
a
'
,
'
bar.com
'
,
16
)
"""
"""
a
=
str
.
split
(
'
/
'
)
a
=
str
.
split
(
'
/
'
)
if
len
(
a
)
==
2
:
if
len
(
a
)
==
2
:
...
@@ -847,9 +810,9 @@ def parse_mechanism(str, d):
...
@@ -847,9 +810,9 @@ def parse_mechanism(str, d):
b
=
a
.
split
(
'
:
'
)
b
=
a
.
split
(
'
:
'
)
if
len
(
b
)
==
2
:
if
len
(
b
)
==
2
:
return
b
[
0
],
b
[
1
],
port
return
b
[
0
]
.
lower
()
,
b
[
1
],
port
else
:
else
:
return
a
,
d
,
port
return
a
.
lower
()
,
d
,
port
def
reverse_dots
(
name
):
def
reverse_dots
(
name
):
"""
Reverse dotted IP addresses or domain names.
"""
Reverse dotted IP addresses or domain names.
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
sign in
to comment