From fb39c6fb5edf70097ee31e1b8638838dfc081892 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Fri, 7 Nov 2014 20:09:10 +0000 Subject: Move dnspython to third_party. Signed-off-by: Jelmer Vernooij Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed Nov 12 22:40:53 CET 2014 on sn-devel-104 --- lib/dnspython/.gitignore | 7 - lib/dnspython/ChangeLog | 1194 -------------------- lib/dnspython/LICENSE | 14 - lib/dnspython/MANIFEST.in | 3 - lib/dnspython/Makefile | 56 - lib/dnspython/README | 442 -------- lib/dnspython/TODO | 17 - lib/dnspython/dns/__init__.py | 54 - lib/dnspython/dns/dnssec.py | 372 ------ lib/dnspython/dns/e164.py | 79 -- lib/dnspython/dns/edns.py | 142 --- lib/dnspython/dns/entropy.py | 123 -- lib/dnspython/dns/exception.py | 40 - lib/dnspython/dns/flags.py | 106 -- lib/dnspython/dns/hash.py | 67 -- lib/dnspython/dns/inet.py | 108 -- lib/dnspython/dns/ipv4.py | 42 - lib/dnspython/dns/ipv6.py | 163 --- lib/dnspython/dns/message.py | 1088 ------------------ lib/dnspython/dns/name.py | 702 ------------ lib/dnspython/dns/namedict.py | 59 - lib/dnspython/dns/node.py | 172 --- lib/dnspython/dns/opcode.py | 104 -- lib/dnspython/dns/query.py | 492 -------- lib/dnspython/dns/rcode.py | 119 -- lib/dnspython/dns/rdata.py | 478 -------- lib/dnspython/dns/rdataclass.py | 114 -- lib/dnspython/dns/rdataset.py | 329 ------ lib/dnspython/dns/rdatatype.py | 232 ---- lib/dnspython/dns/rdtypes/ANY/AFSDB.py | 51 - lib/dnspython/dns/rdtypes/ANY/CERT.py | 131 --- lib/dnspython/dns/rdtypes/ANY/CNAME.py | 24 - lib/dnspython/dns/rdtypes/ANY/DLV.py | 20 - lib/dnspython/dns/rdtypes/ANY/DNAME.py | 21 - lib/dnspython/dns/rdtypes/ANY/DNSKEY.py | 94 -- lib/dnspython/dns/rdtypes/ANY/DS.py | 20 - lib/dnspython/dns/rdtypes/ANY/GPOS.py | 156 --- lib/dnspython/dns/rdtypes/ANY/HINFO.py | 83 -- lib/dnspython/dns/rdtypes/ANY/HIP.py | 140 --- lib/dnspython/dns/rdtypes/ANY/ISDN.py | 96 -- lib/dnspython/dns/rdtypes/ANY/LOC.py | 334 ------ lib/dnspython/dns/rdtypes/ANY/MX.py | 20 - lib/dnspython/dns/rdtypes/ANY/NS.py | 20 - lib/dnspython/dns/rdtypes/ANY/NSEC.py | 128 --- lib/dnspython/dns/rdtypes/ANY/NSEC3.py | 182 --- lib/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py | 88 -- lib/dnspython/dns/rdtypes/ANY/PTR.py | 20 - lib/dnspython/dns/rdtypes/ANY/RP.py | 86 -- lib/dnspython/dns/rdtypes/ANY/RRSIG.py | 155 --- lib/dnspython/dns/rdtypes/ANY/RT.py | 20 - lib/dnspython/dns/rdtypes/ANY/SOA.py | 127 --- lib/dnspython/dns/rdtypes/ANY/SPF.py | 22 - lib/dnspython/dns/rdtypes/ANY/SSHFP.py | 77 -- lib/dnspython/dns/rdtypes/ANY/TXT.py | 20 - lib/dnspython/dns/rdtypes/ANY/X25.py | 62 - lib/dnspython/dns/rdtypes/ANY/__init__.py | 45 - lib/dnspython/dns/rdtypes/IN/A.py | 57 - lib/dnspython/dns/rdtypes/IN/AAAA.py | 58 - lib/dnspython/dns/rdtypes/IN/APL.py | 170 --- lib/dnspython/dns/rdtypes/IN/DHCID.py | 60 - lib/dnspython/dns/rdtypes/IN/IPSECKEY.py | 159 --- lib/dnspython/dns/rdtypes/IN/KX.py | 20 - lib/dnspython/dns/rdtypes/IN/NAPTR.py | 132 --- lib/dnspython/dns/rdtypes/IN/NSAP.py | 59 - lib/dnspython/dns/rdtypes/IN/NSAP_PTR.py | 20 - lib/dnspython/dns/rdtypes/IN/PX.py | 97 -- lib/dnspython/dns/rdtypes/IN/SRV.py | 89 -- lib/dnspython/dns/rdtypes/IN/WKS.py | 113 -- lib/dnspython/dns/rdtypes/IN/__init__.py | 30 - lib/dnspython/dns/rdtypes/__init__.py | 23 - lib/dnspython/dns/rdtypes/dsbase.py | 92 -- lib/dnspython/dns/rdtypes/mxbase.py | 105 -- lib/dnspython/dns/rdtypes/nsbase.py | 82 -- lib/dnspython/dns/rdtypes/txtbase.py | 87 -- lib/dnspython/dns/renderer.py | 325 ------ lib/dnspython/dns/resolver.py | 1161 ------------------- lib/dnspython/dns/reversename.py | 75 -- lib/dnspython/dns/rrset.py | 175 --- lib/dnspython/dns/set.py | 263 ----- lib/dnspython/dns/tokenizer.py | 547 --------- lib/dnspython/dns/tsig.py | 223 ---- lib/dnspython/dns/tsigkeyring.py | 44 - lib/dnspython/dns/ttl.py | 64 -- lib/dnspython/dns/update.py | 245 ---- lib/dnspython/dns/version.py | 34 - lib/dnspython/dns/wiredata.py | 59 - lib/dnspython/dns/zone.py | 855 -------------- lib/dnspython/examples/ddns.py | 51 - lib/dnspython/examples/e164.py | 6 - lib/dnspython/examples/mx.py | 7 - lib/dnspython/examples/name.py | 13 - lib/dnspython/examples/reverse.py | 40 - lib/dnspython/examples/reverse_name.py | 6 - lib/dnspython/examples/xfr.py | 14 - lib/dnspython/examples/zonediff.py | 270 ----- lib/dnspython/setup.py | 60 - lib/dnspython/tests/Makefile | 26 - lib/dnspython/tests/bugs.py | 44 - lib/dnspython/tests/dnssec.py | 146 --- lib/dnspython/tests/example | 226 ---- lib/dnspython/tests/example1.good | 114 -- lib/dnspython/tests/example2.good | 114 -- lib/dnspython/tests/flags.py | 59 - lib/dnspython/tests/message.py | 179 --- lib/dnspython/tests/name.py | 697 ------------ lib/dnspython/tests/namedict.py | 102 -- lib/dnspython/tests/ntoaaton.py | 197 ---- lib/dnspython/tests/rdtypeandclass.py | 123 -- lib/dnspython/tests/resolver.py | 127 --- lib/dnspython/tests/rrset.py | 54 - lib/dnspython/tests/set.py | 208 ---- lib/dnspython/tests/tokenizer.py | 190 ---- lib/dnspython/tests/update.py | 114 -- lib/dnspython/tests/zone.py | 389 ------- lib/dnspython/util/COPYRIGHT | 14 - lib/dnspython/util/copyrights | 116 -- lib/wscript_build | 1 - source4/scripting/bin/samba_dnsupdate | 2 +- source4/scripting/bin/samba_upgradedns | 2 +- third_party/dnspython/.gitignore | 7 + third_party/dnspython/ChangeLog | 1194 ++++++++++++++++++++ third_party/dnspython/LICENSE | 14 + third_party/dnspython/MANIFEST.in | 3 + third_party/dnspython/Makefile | 56 + third_party/dnspython/README | 442 ++++++++ third_party/dnspython/TODO | 17 + third_party/dnspython/dns/__init__.py | 54 + third_party/dnspython/dns/dnssec.py | 372 ++++++ third_party/dnspython/dns/e164.py | 79 ++ third_party/dnspython/dns/edns.py | 142 +++ third_party/dnspython/dns/entropy.py | 123 ++ third_party/dnspython/dns/exception.py | 40 + third_party/dnspython/dns/flags.py | 106 ++ third_party/dnspython/dns/hash.py | 67 ++ third_party/dnspython/dns/inet.py | 108 ++ third_party/dnspython/dns/ipv4.py | 42 + third_party/dnspython/dns/ipv6.py | 163 +++ third_party/dnspython/dns/message.py | 1088 ++++++++++++++++++ third_party/dnspython/dns/name.py | 702 ++++++++++++ third_party/dnspython/dns/namedict.py | 59 + third_party/dnspython/dns/node.py | 172 +++ third_party/dnspython/dns/opcode.py | 104 ++ third_party/dnspython/dns/query.py | 492 ++++++++ third_party/dnspython/dns/rcode.py | 119 ++ third_party/dnspython/dns/rdata.py | 478 ++++++++ third_party/dnspython/dns/rdataclass.py | 114 ++ third_party/dnspython/dns/rdataset.py | 329 ++++++ third_party/dnspython/dns/rdatatype.py | 232 ++++ third_party/dnspython/dns/rdtypes/ANY/AFSDB.py | 51 + third_party/dnspython/dns/rdtypes/ANY/CERT.py | 131 +++ third_party/dnspython/dns/rdtypes/ANY/CNAME.py | 24 + third_party/dnspython/dns/rdtypes/ANY/DLV.py | 20 + third_party/dnspython/dns/rdtypes/ANY/DNAME.py | 21 + third_party/dnspython/dns/rdtypes/ANY/DNSKEY.py | 94 ++ third_party/dnspython/dns/rdtypes/ANY/DS.py | 20 + third_party/dnspython/dns/rdtypes/ANY/GPOS.py | 156 +++ third_party/dnspython/dns/rdtypes/ANY/HINFO.py | 83 ++ third_party/dnspython/dns/rdtypes/ANY/HIP.py | 140 +++ third_party/dnspython/dns/rdtypes/ANY/ISDN.py | 96 ++ third_party/dnspython/dns/rdtypes/ANY/LOC.py | 334 ++++++ third_party/dnspython/dns/rdtypes/ANY/MX.py | 20 + third_party/dnspython/dns/rdtypes/ANY/NS.py | 20 + third_party/dnspython/dns/rdtypes/ANY/NSEC.py | 128 +++ third_party/dnspython/dns/rdtypes/ANY/NSEC3.py | 182 +++ .../dnspython/dns/rdtypes/ANY/NSEC3PARAM.py | 88 ++ third_party/dnspython/dns/rdtypes/ANY/PTR.py | 20 + third_party/dnspython/dns/rdtypes/ANY/RP.py | 86 ++ third_party/dnspython/dns/rdtypes/ANY/RRSIG.py | 155 +++ third_party/dnspython/dns/rdtypes/ANY/RT.py | 20 + third_party/dnspython/dns/rdtypes/ANY/SOA.py | 127 +++ third_party/dnspython/dns/rdtypes/ANY/SPF.py | 22 + third_party/dnspython/dns/rdtypes/ANY/SSHFP.py | 77 ++ third_party/dnspython/dns/rdtypes/ANY/TXT.py | 20 + third_party/dnspython/dns/rdtypes/ANY/X25.py | 62 + third_party/dnspython/dns/rdtypes/ANY/__init__.py | 45 + third_party/dnspython/dns/rdtypes/IN/A.py | 57 + third_party/dnspython/dns/rdtypes/IN/AAAA.py | 58 + third_party/dnspython/dns/rdtypes/IN/APL.py | 170 +++ third_party/dnspython/dns/rdtypes/IN/DHCID.py | 60 + third_party/dnspython/dns/rdtypes/IN/IPSECKEY.py | 159 +++ third_party/dnspython/dns/rdtypes/IN/KX.py | 20 + third_party/dnspython/dns/rdtypes/IN/NAPTR.py | 132 +++ third_party/dnspython/dns/rdtypes/IN/NSAP.py | 59 + third_party/dnspython/dns/rdtypes/IN/NSAP_PTR.py | 20 + third_party/dnspython/dns/rdtypes/IN/PX.py | 97 ++ third_party/dnspython/dns/rdtypes/IN/SRV.py | 89 ++ third_party/dnspython/dns/rdtypes/IN/WKS.py | 113 ++ third_party/dnspython/dns/rdtypes/IN/__init__.py | 30 + third_party/dnspython/dns/rdtypes/__init__.py | 23 + third_party/dnspython/dns/rdtypes/dsbase.py | 92 ++ third_party/dnspython/dns/rdtypes/mxbase.py | 105 ++ third_party/dnspython/dns/rdtypes/nsbase.py | 82 ++ third_party/dnspython/dns/rdtypes/txtbase.py | 87 ++ third_party/dnspython/dns/renderer.py | 325 ++++++ third_party/dnspython/dns/resolver.py | 1161 +++++++++++++++++++ third_party/dnspython/dns/reversename.py | 75 ++ third_party/dnspython/dns/rrset.py | 175 +++ third_party/dnspython/dns/set.py | 263 +++++ third_party/dnspython/dns/tokenizer.py | 547 +++++++++ third_party/dnspython/dns/tsig.py | 223 ++++ third_party/dnspython/dns/tsigkeyring.py | 44 + third_party/dnspython/dns/ttl.py | 64 ++ third_party/dnspython/dns/update.py | 245 ++++ third_party/dnspython/dns/version.py | 34 + third_party/dnspython/dns/wiredata.py | 59 + third_party/dnspython/dns/zone.py | 855 ++++++++++++++ third_party/dnspython/examples/ddns.py | 51 + third_party/dnspython/examples/e164.py | 6 + third_party/dnspython/examples/mx.py | 7 + third_party/dnspython/examples/name.py | 13 + third_party/dnspython/examples/reverse.py | 40 + third_party/dnspython/examples/reverse_name.py | 6 + third_party/dnspython/examples/xfr.py | 14 + third_party/dnspython/examples/zonediff.py | 270 +++++ third_party/dnspython/setup.py | 60 + third_party/dnspython/tests/Makefile | 26 + third_party/dnspython/tests/bugs.py | 44 + third_party/dnspython/tests/dnssec.py | 146 +++ third_party/dnspython/tests/example | 226 ++++ third_party/dnspython/tests/example1.good | 114 ++ third_party/dnspython/tests/example2.good | 114 ++ third_party/dnspython/tests/flags.py | 59 + third_party/dnspython/tests/message.py | 179 +++ third_party/dnspython/tests/name.py | 697 ++++++++++++ third_party/dnspython/tests/namedict.py | 102 ++ third_party/dnspython/tests/ntoaaton.py | 197 ++++ third_party/dnspython/tests/rdtypeandclass.py | 123 ++ third_party/dnspython/tests/resolver.py | 127 +++ third_party/dnspython/tests/rrset.py | 54 + third_party/dnspython/tests/set.py | 208 ++++ third_party/dnspython/tests/tokenizer.py | 190 ++++ third_party/dnspython/tests/update.py | 114 ++ third_party/dnspython/tests/zone.py | 389 +++++++ third_party/dnspython/util/COPYRIGHT | 14 + third_party/dnspython/util/copyrights | 116 ++ third_party/wscript_build | 1 + 236 files changed, 18392 insertions(+), 18392 deletions(-) delete mode 100644 lib/dnspython/.gitignore delete mode 100644 lib/dnspython/ChangeLog delete mode 100644 lib/dnspython/LICENSE delete mode 100644 lib/dnspython/MANIFEST.in delete mode 100644 lib/dnspython/Makefile delete mode 100644 lib/dnspython/README delete mode 100644 lib/dnspython/TODO delete mode 100644 lib/dnspython/dns/__init__.py delete mode 100644 lib/dnspython/dns/dnssec.py delete mode 100644 lib/dnspython/dns/e164.py delete mode 100644 lib/dnspython/dns/edns.py delete mode 100644 lib/dnspython/dns/entropy.py delete mode 100644 lib/dnspython/dns/exception.py delete mode 100644 lib/dnspython/dns/flags.py delete mode 100644 lib/dnspython/dns/hash.py delete mode 100644 lib/dnspython/dns/inet.py delete mode 100644 lib/dnspython/dns/ipv4.py delete mode 100644 lib/dnspython/dns/ipv6.py delete mode 100644 lib/dnspython/dns/message.py delete mode 100644 lib/dnspython/dns/name.py delete mode 100644 lib/dnspython/dns/namedict.py delete mode 100644 lib/dnspython/dns/node.py delete mode 100644 lib/dnspython/dns/opcode.py delete mode 100644 lib/dnspython/dns/query.py delete mode 100644 lib/dnspython/dns/rcode.py delete mode 100644 lib/dnspython/dns/rdata.py delete mode 100644 lib/dnspython/dns/rdataclass.py delete mode 100644 lib/dnspython/dns/rdataset.py delete mode 100644 lib/dnspython/dns/rdatatype.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/AFSDB.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/CERT.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/CNAME.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/DLV.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/DNAME.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/DNSKEY.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/DS.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/GPOS.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/HINFO.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/HIP.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/ISDN.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/LOC.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/MX.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/NS.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/NSEC.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/NSEC3.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/PTR.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/RP.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/RRSIG.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/RT.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/SOA.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/SPF.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/SSHFP.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/TXT.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/X25.py delete mode 100644 lib/dnspython/dns/rdtypes/ANY/__init__.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/A.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/AAAA.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/APL.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/DHCID.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/IPSECKEY.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/KX.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/NAPTR.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/NSAP.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/NSAP_PTR.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/PX.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/SRV.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/WKS.py delete mode 100644 lib/dnspython/dns/rdtypes/IN/__init__.py delete mode 100644 lib/dnspython/dns/rdtypes/__init__.py delete mode 100644 lib/dnspython/dns/rdtypes/dsbase.py delete mode 100644 lib/dnspython/dns/rdtypes/mxbase.py delete mode 100644 lib/dnspython/dns/rdtypes/nsbase.py delete mode 100644 lib/dnspython/dns/rdtypes/txtbase.py delete mode 100644 lib/dnspython/dns/renderer.py delete mode 100644 lib/dnspython/dns/resolver.py delete mode 100644 lib/dnspython/dns/reversename.py delete mode 100644 lib/dnspython/dns/rrset.py delete mode 100644 lib/dnspython/dns/set.py delete mode 100644 lib/dnspython/dns/tokenizer.py delete mode 100644 lib/dnspython/dns/tsig.py delete mode 100644 lib/dnspython/dns/tsigkeyring.py delete mode 100644 lib/dnspython/dns/ttl.py delete mode 100644 lib/dnspython/dns/update.py delete mode 100644 lib/dnspython/dns/version.py delete mode 100644 lib/dnspython/dns/wiredata.py delete mode 100644 lib/dnspython/dns/zone.py delete mode 100755 lib/dnspython/examples/ddns.py delete mode 100755 lib/dnspython/examples/e164.py delete mode 100755 lib/dnspython/examples/mx.py delete mode 100755 lib/dnspython/examples/name.py delete mode 100755 lib/dnspython/examples/reverse.py delete mode 100755 lib/dnspython/examples/reverse_name.py delete mode 100755 lib/dnspython/examples/xfr.py delete mode 100755 lib/dnspython/examples/zonediff.py delete mode 100755 lib/dnspython/setup.py delete mode 100644 lib/dnspython/tests/Makefile delete mode 100644 lib/dnspython/tests/bugs.py delete mode 100644 lib/dnspython/tests/dnssec.py delete mode 100644 lib/dnspython/tests/example delete mode 100644 lib/dnspython/tests/example1.good delete mode 100644 lib/dnspython/tests/example2.good delete mode 100644 lib/dnspython/tests/flags.py delete mode 100644 lib/dnspython/tests/message.py delete mode 100644 lib/dnspython/tests/name.py delete mode 100644 lib/dnspython/tests/namedict.py delete mode 100644 lib/dnspython/tests/ntoaaton.py delete mode 100644 lib/dnspython/tests/rdtypeandclass.py delete mode 100644 lib/dnspython/tests/resolver.py delete mode 100644 lib/dnspython/tests/rrset.py delete mode 100644 lib/dnspython/tests/set.py delete mode 100644 lib/dnspython/tests/tokenizer.py delete mode 100644 lib/dnspython/tests/update.py delete mode 100644 lib/dnspython/tests/zone.py delete mode 100644 lib/dnspython/util/COPYRIGHT delete mode 100644 lib/dnspython/util/copyrights create mode 100644 third_party/dnspython/.gitignore create mode 100644 third_party/dnspython/ChangeLog create mode 100644 third_party/dnspython/LICENSE create mode 100644 third_party/dnspython/MANIFEST.in create mode 100644 third_party/dnspython/Makefile create mode 100644 third_party/dnspython/README create mode 100644 third_party/dnspython/TODO create mode 100644 third_party/dnspython/dns/__init__.py create mode 100644 third_party/dnspython/dns/dnssec.py create mode 100644 third_party/dnspython/dns/e164.py create mode 100644 third_party/dnspython/dns/edns.py create mode 100644 third_party/dnspython/dns/entropy.py create mode 100644 third_party/dnspython/dns/exception.py create mode 100644 third_party/dnspython/dns/flags.py create mode 100644 third_party/dnspython/dns/hash.py create mode 100644 third_party/dnspython/dns/inet.py create mode 100644 third_party/dnspython/dns/ipv4.py create mode 100644 third_party/dnspython/dns/ipv6.py create mode 100644 third_party/dnspython/dns/message.py create mode 100644 third_party/dnspython/dns/name.py create mode 100644 third_party/dnspython/dns/namedict.py create mode 100644 third_party/dnspython/dns/node.py create mode 100644 third_party/dnspython/dns/opcode.py create mode 100644 third_party/dnspython/dns/query.py create mode 100644 third_party/dnspython/dns/rcode.py create mode 100644 third_party/dnspython/dns/rdata.py create mode 100644 third_party/dnspython/dns/rdataclass.py create mode 100644 third_party/dnspython/dns/rdataset.py create mode 100644 third_party/dnspython/dns/rdatatype.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/AFSDB.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/CERT.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/CNAME.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/DLV.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/DNAME.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/DNSKEY.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/DS.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/GPOS.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/HINFO.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/HIP.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/ISDN.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/LOC.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/MX.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/NS.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/NSEC.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/NSEC3.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/PTR.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/RP.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/RRSIG.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/RT.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/SOA.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/SPF.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/SSHFP.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/TXT.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/X25.py create mode 100644 third_party/dnspython/dns/rdtypes/ANY/__init__.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/A.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/AAAA.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/APL.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/DHCID.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/IPSECKEY.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/KX.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/NAPTR.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/NSAP.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/NSAP_PTR.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/PX.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/SRV.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/WKS.py create mode 100644 third_party/dnspython/dns/rdtypes/IN/__init__.py create mode 100644 third_party/dnspython/dns/rdtypes/__init__.py create mode 100644 third_party/dnspython/dns/rdtypes/dsbase.py create mode 100644 third_party/dnspython/dns/rdtypes/mxbase.py create mode 100644 third_party/dnspython/dns/rdtypes/nsbase.py create mode 100644 third_party/dnspython/dns/rdtypes/txtbase.py create mode 100644 third_party/dnspython/dns/renderer.py create mode 100644 third_party/dnspython/dns/resolver.py create mode 100644 third_party/dnspython/dns/reversename.py create mode 100644 third_party/dnspython/dns/rrset.py create mode 100644 third_party/dnspython/dns/set.py create mode 100644 third_party/dnspython/dns/tokenizer.py create mode 100644 third_party/dnspython/dns/tsig.py create mode 100644 third_party/dnspython/dns/tsigkeyring.py create mode 100644 third_party/dnspython/dns/ttl.py create mode 100644 third_party/dnspython/dns/update.py create mode 100644 third_party/dnspython/dns/version.py create mode 100644 third_party/dnspython/dns/wiredata.py create mode 100644 third_party/dnspython/dns/zone.py create mode 100755 third_party/dnspython/examples/ddns.py create mode 100755 third_party/dnspython/examples/e164.py create mode 100755 third_party/dnspython/examples/mx.py create mode 100755 third_party/dnspython/examples/name.py create mode 100755 third_party/dnspython/examples/reverse.py create mode 100755 third_party/dnspython/examples/reverse_name.py create mode 100755 third_party/dnspython/examples/xfr.py create mode 100755 third_party/dnspython/examples/zonediff.py create mode 100755 third_party/dnspython/setup.py create mode 100644 third_party/dnspython/tests/Makefile create mode 100644 third_party/dnspython/tests/bugs.py create mode 100644 third_party/dnspython/tests/dnssec.py create mode 100644 third_party/dnspython/tests/example create mode 100644 third_party/dnspython/tests/example1.good create mode 100644 third_party/dnspython/tests/example2.good create mode 100644 third_party/dnspython/tests/flags.py create mode 100644 third_party/dnspython/tests/message.py create mode 100644 third_party/dnspython/tests/name.py create mode 100644 third_party/dnspython/tests/namedict.py create mode 100644 third_party/dnspython/tests/ntoaaton.py create mode 100644 third_party/dnspython/tests/rdtypeandclass.py create mode 100644 third_party/dnspython/tests/resolver.py create mode 100644 third_party/dnspython/tests/rrset.py create mode 100644 third_party/dnspython/tests/set.py create mode 100644 third_party/dnspython/tests/tokenizer.py create mode 100644 third_party/dnspython/tests/update.py create mode 100644 third_party/dnspython/tests/zone.py create mode 100644 third_party/dnspython/util/COPYRIGHT create mode 100644 third_party/dnspython/util/copyrights diff --git a/lib/dnspython/.gitignore b/lib/dnspython/.gitignore deleted file mode 100644 index 5592c971b0..0000000000 --- a/lib/dnspython/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -build -dist -MANIFEST -html -html.zip -html.tar.gz -tests/*.out diff --git a/lib/dnspython/ChangeLog b/lib/dnspython/ChangeLog deleted file mode 100644 index 71b7961490..0000000000 --- a/lib/dnspython/ChangeLog +++ /dev/null @@ -1,1194 +0,0 @@ -2011-08-22 Robert Halley - - * dns/resolver.py: Added LRUCache, which allows a maximum number - of nodes to be cached, and removes the least-recently used node - when adding a new node to a full cache. - -2011-07-13 Bob Halley - - * dns/resolver.py: dns.resolver.override_system_resolver() - overrides the socket module's versions of getaddrinfo(), - getnameinfo(), getfqdn(), gethostbyname(), gethostbyname_ex() and - gethostbyaddr() with an implementation which uses a dnspython stub - resolver instead of the system's stub resolver. This can be - useful in testing situations where you want to control the - resolution behavior of python code without having to change the - system's resolver settings (e.g. /etc/resolv.conf). - dns.resolver.restore_system_resolver() undoes the change. - -2011-07-08 Bob Halley - - * dns/ipv4.py: dnspython now provides its own, stricter, versions - of IPv4 inet_ntoa() and inet_aton() instead of using the OS's - versions. - - * dns/ipv6.py: inet_aton() now bounds checks embedded IPv4 addresses - more strictly. Also, now only dns.exception.SyntaxError can be - raised on bad input. - -2011-04-05 Bob Halley - - * Old DNSSEC types (KEY, NXT, and SIG) have been removed. - - * Bounds checking of slices in rdata wire processing is now more - strict, and bounds errors (e.g. we got less data than was - expected) now raise dns.exception.FormError rather than - IndexError. - -2011-03-28 Bob Halley - - * (Version 1.9.4 released) - -2011-03-24 Bob Halley - - * dns/rdata.py (Rdata._wire_cmp): We need to specify no - compression and an origin to _wire_cmp() in case names in the - rdata are relative names. - - * dns/rdtypes/ANY/SIG.py (SIG._cmp): Add missing 'import struct'. - Thanks to Arfrever Frehtes Taifersar Arahesis for reporting the - problem. - -2011-03-24 Bob Halley - - * (Version 1.9.3 released) - -2011-03-22 Bob Halley - - * dns/resolver.py: a boolean parameter, 'raise_on_no_answer', has - been added to the query() methods. In no-error, no-data - situations, this parameter determines whether NoAnswer should be - raised or not. If True, NoAnswer is raised. If False, then an - Answer() object with a None rrset will be returned. - - * dns/resolver.py: Answer() objects now have a canonical_name field. - -2011-01-11 Bob Halley - - * Dnspython was erroneously doing case-insensitive comparisons - of the names in NSEC and RRSIG RRs. Thanks to Casey Deccio for - reporting this bug. - -2010-12-17 Bob Halley - - * dns/message.py (_WireReader._get_section): use "is" and not "==" - when testing what section an RR is in. Thanks to James Raftery - for reporting this bug. - -2010-12-10 Bob Halley - - * dns/resolver.py (Resolver.query): disallow metaqueries. - - * dns/rdata.py (Rdata.__hash__): Added a __hash__ method for rdata. - -2010-11-23 Bob Halley - - * (Version 1.9.2 released) - -2010-11-23 Bob Halley - - * dns/dnssec.py (_need_pycrypto): DSA and RSA are modules, not - functions, and I didn't notice because the test suite masked - the bug! *sigh* - -2010-11-22 Bob Halley - - * (Version 1.9.1 released) - -2010-11-22 Bob Halley - - * dns/dnssec.py: the "from" style import used to get DSA from - PyCrypto trashed a DSA constant. Now a normal import is used - to avoid namespace contamination. - -2010-11-20 Bob Halley - - * (Version 1.9.0 released) - -2010-11-07 Bob Halley - - * dns/dnssec.py: Added validate() to do basic DNSSEC validation - (requires PyCrypto). Thanks to Brian Wellington for the patch. - - * dns/hash.py: Hash compatibility handling is now its own module. - -2010-10-31 Bob Halley - - * dns/resolver.py (zone_for_name): A query name resulting in a - CNAME or DNAME response to a node which had an SOA was incorrectly - treated as a zone origin. In these cases, we should just look - higher. Thanks to Gert Berger for reporting this problem. - - * Added zonediff.py to examples. This program compares two zones - and shows the differences either in diff-like plain text, or - HTML. Thanks to Dennis Kaarsemaker for contributing this - useful program. - -2010-10-27 Bob Halley - - * Incorporate a patch to use poll() instead of select() by - default on platforms which support it. Thanks to - Peter Schüller and Spotify for the contribution. - -2010-10-17 Bob Halley - - * Python prior to 2.5.2 doesn't compute the correct values for - HMAC-SHA384 and HMAC-SHA512. We now detect attempts to use - them and raise NotImplemented if the Python version is too old. - Thanks to Kevin Chen for reporting the problem. - - * Various routines that took the string forms of rdata types and - classes did not permit the strings to be Unicode strings. - Thanks to Ryan Workman for reporting the issue. - - * dns/tsig.py: Added symbolic constants for the algorithm strings. - E.g. you can now say dns.tsig.HMAC_MD5 instead of - "HMAC-MD5.SIG-ALG.REG.INT". Thanks to Cillian Sharkey for - suggesting this improvement. - - * dns/tsig.py (get_algorithm): fix hashlib compatibility; thanks to - Kevin Chen for the patch. - - * dns/dnssec.py: Added key_id() and make_ds(). - - * dns/message.py: message.py needs to import dns.edns since it uses - it. - -2010-05-04 Bob Halley - - * dns/rrset.py (RRset.__init__): "covers" was not passed to the - superclass __init__(). Thanks to Shanmuga Rajan for reporting - the problem. - -2010-03-10 Bob Halley - - * The TSIG algorithm value was passed to use_tsig() incorrectly - in some cases. Thanks to 'ducciovigolo' for reporting the problem. - -2010-01-26 Bob Halley - - * (Version 1.8.0 released) - -2010-01-13 Bob Halley - - * dns/dnssec.py: Added RSASHA256 and RSASHA512 codepoints; added - other missing codepoints to _algorithm_by_text. - -2010-01-12 Bob Halley - - * Escapes in masterfiles now work correctly. Previously they were - only working correctly when the text involved was part of a domain - name. - - * dns/tokenizer.py: The tokenizer's get() method now returns Token - objects, not (type, text) tuples. - -2009-11-13 Bob Halley - - * Support has been added for hmac-sha1, hmac-sha224, hmac-sha256, - hmac-sha384 and hmac-sha512. Thanks to Kevin Chen for a - thoughtful, high quality patch. - - * dns/update.py (Update::present): A zero TTL was not added if - present() was called with a single rdata, causing _add() to be - unhappy. Thanks to Eugene Kim for reporting the problem and - submitting a patch. - - * dns/entropy.py: Use os.urandom() if present. Don't seed until - someone wants randomness. - -2009-09-16 Bob Halley - - * dns/entropy.py: The entropy module needs locking in order to be - used safely in a multithreaded environment. Thanks to Beda Kosata - for reporting the problem. - -2009-07-27 Bob Halley - - * dns/query.py (xfr): The socket was not set to nonblocking mode. - Thanks to Erik Romijn for reporting this problem. - -2009-07-23 Bob Halley - - * dns/rdtypes/IN/SRV.py (SRV._cmp): SRV records were compared - incorrectly due to a cut-and-paste error. Thanks to Tommie - Gannert for reporting this bug. - - * dns/e164.py (query): The resolver parameter was not used. - Thanks to Matías Bellone for reporting this bug. - -2009-06-23 Bob Halley - - * dns/entropy.py (EntropyPool.__init__): open /dev/random unbuffered; - there's no need to consume more randomness than we need. Thanks - to Brian Wellington for the patch. - -2009-06-19 Bob Halley - - * (Version 1.7.1 released) - -2009-06-19 Bob Halley - - * DLV.py was omitted from the kit - - * Negative prerequisites were not handled correctly in _get_section(). - -2009-06-19 Bob Halley - - * (Version 1.7.0 released) - -2009-06-19 Bob Halley - - * On Windows, the resolver set the domain incorrectly. Thanks - to Brandon Carpenter for reporting this bug. - - * Added a to_digestable() method to rdata classes; it returns the - digestable form (i.e. DNSSEC canonical form) of the rdata. For - most rdata types this is the same uncompressed wire form. For - certain older DNS RR types, however, domain names in the rdata - are downcased. - - * Added support for the HIP RR type. - -2009-06-18 Bob Halley - - * Added support for the DLV RR type. - - * Added various DNSSEC related constants (e.g. algorithm identifiers, - flag values). - - * dns/tsig.py: Added support for BADTRUNC result code. - - * dns/query.py (udp): When checking that addresses are the same, - use the binary form of the address in the comparison. This - ensures that we don't treat addresses as different if they have - equivalent but differing textual representations. E.g. "1:00::1" - and "1::1" represent the same address but are not textually equal. - Thanks to Kim Davies for reporting this bug. - - * The resolver's query() method now has an optional 'source' parameter, - allowing the source IP address to be specified. Thanks to - Alexander Lind for suggesting the change and sending a patch. - - * Added NSEC3 and NSEC3PARAM support. - -2009-06-17 Bob Halley - - * Fixed NSEC.to_text(), which was only printing the last window. - Thanks to Brian Wellington for finding the problem and fixing it. - -2009-03-30 Bob Halley - - * dns/query.py (xfr): Allow UDP IXFRs. Use "one_rr_per_rrset" mode when - doing IXFR. - -2009-03-30 Bob Halley - - * Add "one_rr_per_rrset" mode switch to methods which parse - messages from wire format (e.g. dns.message.from_wire(), - dns.query.udp(), dns.query.tcp()). If set, each RR read is - placed in its own RRset (instead of being coalesced). - -2009-03-30 Bob Halley - - * Added EDNS option support. - -2008-10-16 Bob Halley - - * dns/rdtypes/ANY/DS.py: The from_text() parser for DS RRs did not - allow multiple Base64 chunks. Thanks to Rakesh Banka for - finding this bug and submitting a patch. - -2008-10-08 Bob Halley - - * Add entropy module. - - * When validating TSIGs, we need to use the absolute name. - -2008-06-03 Bob Halley - - * dns/message.py (Message.set_rcode): The mask used preserved the - extended rcode, instead of everything else in ednsflags. - - * dns/message.py (Message.use_edns): ednsflags was not kept - coherent with the specified edns version. - -2008-02-06 Bob Halley - - * dns/ipv6.py (inet_aton): We could raise an exception other than - dns.exception.SyntaxError in some cases. - - * dns/tsig.py: Raise an exception when the peer has set a non-zero - TSIG error. - -2007-11-25 Bob Halley - - * (Version 1.6.0 released) - -2007-11-25 Bob Halley - - * dns/query.py (_wait_for): if select() raises an exception due to - EINTR, we should just select() again. - -2007-06-13 Bob Halley - - * dns/inet.py: Added is_multicast(). - - * dns/query.py (udp): If the queried address is a multicast address, then - don't check that the address of the response is the same as the address - queried. - -2007-05-24 Bob Halley - - * dns/rdtypes/IN/NAPTR.py: NAPTR comparisons didn't compare the - preference field due to a typo. - -2007-02-07 Bob Halley - - * dns/resolver.py: Integrate code submitted by Paul Marks to - determine whether a Windows NIC is enabled. The way dnspython - used to do this does not work on Windows Vista. - -2006-12-10 Bob Halley - - * (Version 1.5.0 released) - -2006-11-03 Bob Halley - - * dns/rdtypes/IN/DHCID.py: Added support for the DHCID RR type. - -2006-11-02 Bob Halley - - * dns/query.py (udp): Messages from unexpected sources can now be - ignored by setting ignore_unexpected to True. - -2006-10-31 Bob Halley - - * dns/query.py (udp): When raising UnexpectedSource, add more - detail about what went wrong to the exception. - -2006-09-22 Bob Halley - - * dns/message.py (Message.use_edns): add reasonable defaults for - the ednsflags, payload, and request_payload parameters. - - * dns/message.py (Message.want_dnssec): add a convenience method for - enabling/disabling the "DNSSEC desired" flag in requests. - - * dns/message.py (make_query): add "use_edns" and "want_dnssec" - parameters. - -2006-08-17 Bob Halley - - * dns/resolver.py (Resolver.read_resolv_conf): If /etc/resolv.conf - doesn't exist, just use the default resolver configuration (i.e. - the same thing we would have used if resolv.conf had existed and - been empty). - -2006-07-26 Bob Halley - - * dns/resolver.py (Resolver._config_win32_fromkey): fix - cut-and-paste error where we passed the wrong variable to - self._config_win32_search(). Thanks to David Arnold for finding - the bug and submitting a patch. - -2006-07-20 Bob Halley - - * dns/resolver.py (Answer): Add more support for the sequence - protocol, forwarding requests to the answer object's rrset. - E.g. "for a in answer" is equivalent to "for a in answer.rrset", - "answer[i]" is equivalent to "answer.rrset[i]", and - "answer[i:j]" is equivalent to "answer.rrset[i:j]". - -2006-07-19 Bob Halley - - * dns/query.py (xfr): Add IXFR support. - -2006-06-22 Bob Halley - - * dns/rdtypes/IN/IPSECKEY.py: Added support for the IPSECKEY RR type. - -2006-06-21 Bob Halley - - * dns/rdtypes/ANY/SPF.py: Added support for the SPF RR type. - -2006-06-02 Bob Halley - - * (Version 1.4.0 released) - -2006-04-25 Bob Halley - - * dns/rrset.py (RRset.to_rdataset): Added a convenience method - to convert an rrset into an rdataset. - -2006-03-27 Bob Halley - - * Added dns.e164.query(). This function can be used to look for - NAPTR RRs for a specified number in several domains, e.g.: - - dns.e164.query('16505551212', - ['e164.dnspython.org.', 'e164.arpa.']) - -2006-03-26 Bob Halley - - * dns/resolver.py (Resolver.query): The resolver deleted from - a list while iterating it, which makes the iterator unhappy. - -2006-03-17 Bob Halley - - * dns/resolver.py (Resolver.query): The resolver needlessly - delayed responses for successful queries. - -2006-01-18 Bob Halley - - * dns/rdata.py: added a validate() method to the rdata class. If - you change an rdata by assigning to its fields, it is a good - idea to call validate() when you are done making changes. - For example, if 'r' is an MX record and then you execute: - - r.preference = 100000 # invalid, because > 65535 - r.validate() - - The validation will fail and an exception will be raised. - -2006-01-11 Bob Halley - - * dns/ttl.py: TTLs are now bounds checked to be within the closed - interval [0, 2^31 - 1]. - - * The BIND 8 TTL syntax is now accepted in the SOA refresh, retry, - expire, and minimum fields, and in the original_ttl field of - SIG and RRSIG records. - -2006-01-04 Bob Halley - - * dns/resolver.py: The windows registry irritatingly changes the - list element delimiter in between ' ' and ',' (and vice-versa) - in various versions of windows. We now cope by always looking - for either one (' ' first). - -2005-12-27 Bob Halley - - * dns/e164.py: Added routines to convert between E.164 numbers and - their ENUM domain name equivalents. - - * dns/reversename.py: Added routines to convert between IPv4 and - IPv6 addresses and their DNS reverse-map equivalents. - -2005-12-18 Bob Halley - - * dns/rdtypes/ANY/LOC.py (_tuple_to_float): The sign was lost when - converting a tuple into a float, which broke conversions of - south latitudes and west longitudes. - -2005-11-17 Bob Halley - - * dns/zone.py: The 'origin' parameter to from_text() and from_file() - is now optional. If not specified, dnspython will use the - first $ORIGIN in the text as the zone's origin. - - * dns/zone.py: Sanity checks of the zone's origin node can now - be disabled. - -2005-11-12 Bob Halley - - * dns/name.py: Preliminary Unicode support has been added for - domain names. Running dns.name.from_text() on a Unicode string - will now encode each label using the IDN ACE encoding. The - to_unicode() method may be used to convert a dns.name.Name with - IDN ACE labels back into a Unicode string. This functionality - requires Python 2.3 or greater. - -2005-10-31 Bob Halley - - * (Version 1.3.5 released) - -2005-10-12 Bob Halley - - * dns/zone.py: Zone.iterate_rdatasets() and Zone.iterate_rdatas() - did not have a default rdtype of dns.rdatatype.ANY as their - docstrings said they did. They do now. - -2005-10-06 Bob Halley - - * dns/name.py: Added the parent() method, which returns the - parent of a name. - -2005-10-01 Bob Halley - - * dns/resolver.py: Added zone_for_name() helper, which returns - the name of the zone which contains the specified name. - - * dns/resolver.py: Added get_default_resolver(), which returns - the default resolver, initializing it if necessary. - -2005-09-29 Bob Halley - - * dns/resolver.py (Resolver._compute_timeout): If time goes - backwards a little bit, ignore it. - -2005-07-31 Bob Halley - - * (Version 1.3.4 released) - -2005-07-31 Bob Halley - - * dns/message.py (make_response): Trying to respond to a response - threw a NameError while trying to throw a FormErr since it used - the wrong name for the FormErr exception. - - * dns/query.py (_connect): We needed to ignore EALREADY too. - - * dns/query.py: Optional "source" and "source_port" parameters - have been added to udp(), tcp(), and xfr(). Thanks to Ralf - Weber for suggesting the change and providing a patch. - -2005-06-05 Bob Halley - - * dns/query.py: The requirement that the "where" parameter be - an IPv4 or IPv6 address is now documented. - -2005-06-04 Bob Halley - - * dns/resolver.py: The resolver now does exponential backoff - each time it runs through all of the nameservers. - - * dns/resolver.py: rcodes which indicate a nameserver is likely - to be a "permanent failure" for a query cause the nameserver - to be removed from the mix for that query. - -2005-01-30 Bob Halley - - * (Version 1.3.3 released) - -2004-10-25 Bob Halley - - * dns/rdtypes/ANY/TXT.py (TXT.from_text): The masterfile parser - incorrectly rejected TXT records where a value was not quoted. - -2004-10-11 Bob Halley - - * dns/message.py: Added make_response(), which creates a skeletal - response for the specified query. Added opcode() and set_opcode() - convenience methods to the Message class. Added the request_payload - attribute to the Message class. - -2004-10-10 Bob Halley - - * dns/zone.py (from_xfr): dns.zone.from_xfr() in relativization - mode incorrectly set zone.origin to the empty name. - -2004-09-02 Bob Halley - - * dns/name.py (Name.to_wire): The 'file' parameter to - Name.to_wire() is now optional; if omitted, the wire form will - be returned as the value of the function. - -2004-08-14 Bob Halley - - * dns/message.py (Message.find_rrset): find_rrset() now uses an - index, vastly improving the from_wire() performance of large - messages such as zone transfers. - -2004-08-07 Bob Halley - - * (Version 1.3.2 released) - -2004-08-04 Bob Halley - - * dns/query.py: sending queries to a nameserver via IPv6 now - works. - - * dns/inet.py (af_for_address): Add af_for_address(), which looks - at a textual-form address and attempts to determine which address - family it is. - - * dns/query.py: the default for the 'af' parameter of the udp(), - tcp(), and xfr() functions has been changed from AF_INET to None, - which causes dns.inet.af_for_address() to be used to determine the - address family. If dns.inet.af_for_address() can't figure it out, - we fall back to AF_INET and hope for the best. - -2004-07-31 Bob Halley - - * dns/rdtypes/ANY/NSEC.py (NSEC.from_text): The NSEC text format - does not allow specifying types by number, so we shouldn't either. - - * dns/renderer.py: the renderer module didn't import random, - causing an exception to be raised if a query id wasn't provided - when a Renderer was created. - - * dns/resolver.py (Resolver.query): the resolver wasn't catching - dns.exception.Timeout, so a timeout erroneously caused the whole - resolution to fail instead of just going on to the next server. - -2004-06-16 Bob Halley - - * dns/rdtypes/ANY/LOC.py (LOC.from_text): LOC milliseconds values - were converted incorrectly if the length of the milliseconds - string was less than 3. - -2004-06-06 Bob Halley - - * (Version 1.3.1 released) - -2004-05-22 Bob Halley - - * dns/update.py (Update.delete): We erroneously specified a - "deleting" value of dns.rdatatype.NONE instead of - dns.rdataclass.NONE when the thing being deleted was either an - Rdataset instance or an Rdata instance. - - * dns/rdtypes/ANY/SSHFP.py: Added support for the proposed SSHFP - RR type. - -2004-05-14 Bob Halley - - * dns/rdata.py (from_text): The masterfile reader did not - accept the unknown RR syntax when used with a known RR type. - -2004-05-08 Bob Halley - - * dns/name.py (from_text): dns.name.from_text() did not raise - an exception if a backslash escape ended prematurely. - -2004-04-09 Bob Halley - - * dns/zone.py (_MasterReader._rr_line): The masterfile reader - erroneously treated lines starting with leading whitespace but - not having any RR definition as an error. It now treats - them like a blank line (which is not an error). - -2004-04-01 Bob Halley - - * (Version 1.3.0 released) - -2004-03-19 Bob Halley - - * Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. - -2004-01-16 Bob Halley - - * dns/query.py (_connect): Windows returns EWOULDBLOCK instead - of EINPROGRESS when trying to connect a nonblocking socket. - -2003-11-13 Bob Halley - - * dns/rdtypes/ANY/LOC.py (LOC.to_wire): We encoded and decoded LOC - incorrectly, since we were interpreting the values of altitiude, - size, hprec, and vprec in meters instead of centimeters. - - * dns/rdtypes/IN/WKS.py (WKS.from_wire): The WKS protocol value is - encoded with just one octet, not two! - -2003-11-09 Bob Halley - - * dns/resolver.py (Cache.maybe_clean): The cleaner deleted items - from the dictionary while iterating it, causing a RuntimeError - to be raised. Thanks to Mark R. Levinson for the bug report, - regression test, and fix. - -2003-11-07 Bob Halley - - * (Version 1.2.0 released) - -2003-11-03 Bob Halley - - * dns/zone.py (_MasterReader.read): The saved_state now includes - the default TTL. - -2003-11-01 Bob Halley - - * dns/tokenizer.py (Tokenizer.get): The tokenizer didn't - handle escaped delimiters. - -2003-10-27 Bob Halley - - * dns/resolver.py (Resolver.read_resolv_conf): If no nameservers - are configured in /etc/resolv.conf, the default nameserver - list should be ['127.0.0.1']. - -2003-09-08 Bob Halley - - * dns/resolver.py (Resolver._config_win32_fromkey): We didn't - catch WindowsError, which can happen if a key is not defined - in the registry. - -2003-09-06 Bob Halley - - * (Version 1.2.0b1 released) - -2003-09-05 Bob Halley - - * dns/query.py: Timeout support has been overhauled to provide - timeouts under Python 2.2 as well as 2.3, and to provide more - accurate expiration. - -2003-08-30 Bob Halley - - * dns/zone.py: dns.exception.SyntaxError is raised for unknown - master file directives. - -2003-08-28 Bob Halley - - * dns/zone.py: $INCLUDE processing is now enabled/disabled using - the allow_include parameter. The default is to process $INCLUDE - for from_file(), and to disallow $INCLUDE for from_text(). The - master reader now calls zone.check_origin_node() by default after - the zone has been read. find_rdataset() called get_node() instead - of find_node(), which result in an incorrect exception. The - relativization state of a zone is now remembered and applied - consistently when looking up names. from_xfr() now supports - relativization like the _MasterReader. - -2003-08-22 Bob Halley - - * dns/zone.py: The _MasterReader now understands $INCLUDE. - -2003-08-12 Bob Halley - - * dns/zone.py: The _MasterReader now specifies the file and line - number when a syntax error occurs. The BIND 8 TTL format is now - understood when loading a zone, though it will never be emitted. - The from_file() function didn't pass the zone_factory parameter - to from_text(). - -2003-08-10 Bob Halley - - * (Version 1.1.0 released) - -2003-08-07 Bob Halley - - * dns/update.py (Update._add): A typo meant that _add would - fail if the thing being added was an Rdata object (as - opposed to an Rdataset or the textual form of an Rdata). - -2003-08-05 Bob Halley - - * dns/set.py: the simple Set class has been moved to its - own module, and augmented to support more set operations. - -2003-08-04 Bob Halley - - * Node and all rdata types have been "slotted". This speeds - things up a little and reduces memory usage noticeably. - -2003-08-02 Bob Halley - - * (Version 1.1.0c1 released) - -2003-08-02 Bob Halley - - * dns/rdataset.py: SimpleSets now support more set options. - - * dns/message.py: Added the get_rrset() method. from_file() now - allows Unicode filenames and turns on universal newline support if - it opens the file itself. - - * dns/node.py: Added the delete_rdataset() and replace_rdataset() - methods. - - * dns/zone.py: Added the delete_node(), delete_rdataset(), and - replace_rdataset() methods. from_file() now allows Unicode - filenames and turns on universal newline support if it opens the - file itself. Added a to_file() method. - -2003-08-01 Bob Halley - - * dns/opcode.py: Opcode from/to text converters now understand - numeric opcodes. The to_text() method will return a numeric opcode - string if it doesn't know a text name for the opcode. - - * dns/message.py: Added set_rcode(). Fixed code where ednsflags - wasn't treated as a long. - - * dns/rcode.py: ednsflags wasn't treated as a long. Rcode from/to - text converters now understand numeric rcodes. The to_text() - method will return a numeric rcode string if it doesn't know - a text name for the rcode. - - * examples/reverse.py: Added a new example program that builds a - reverse (address-to-name) mapping table from the name-to-address - mapping specified by A RRs in zone files. - - * dns/node.py: Added get_rdataset() method. - - * dns/zone.py: Added get_rdataset() and get_rrset() methods. Added - iterate_rdatas(). - -2003-07-31 Bob Halley - - * dns/zone.py: Added the iterate_rdatasets() method which returns - a generator which yields (name, rdataset) tuples for all the - rdatasets in the zone matching the specified rdatatype. - -2003-07-30 Bob Halley - - * (Version 1.1.0b2 released) - -2003-07-30 Bob Halley - - * dns/zone.py: Added find_rrset() and find_rdataset() convenience - methods. They let you retrieve rdata with the specified name - and type in one call. - - * dns/node.py: Nodes no longer have names; owner names are - associated with nodes in the Zone object's nodes dictionary. - - * dns/zone.py: Zone objects now implement more of the standard - mapping interface. __iter__ has been changed to iterate the keys - rather than values to match the standard mapping interface's - behavior. - -2003-07-20 Bob Halley - - * dns/ipv6.py (inet_ntoa): Handle embedded IPv4 addresses. - -2003-07-19 Bob Halley - - * (Version 1.1.0b1 released) - -2003-07-18 Bob Halley - - * dns/tsig.py: The TSIG validation of TCP streams where not - every message is signed now works correctly. - - * dns/zone.py: Zones can now be compared for equality and - inequality. If the other object in the comparison is also - a zone, then "the right thing" happens; i.e. the zones are - equal iff.: they have the same rdclass, origin, and nodes. - -2003-07-17 Bob Halley - - * dns/message.py (Message.use_tsig): The method now allows for - greater control over the various fields in the generated signature - (e.g. fudge). - (_WireReader._get_section): UnknownTSIGKey is now raised if an - unknown key is encountered, or if a signed message has no keyring. - -2003-07-16 Bob Halley - - * dns/tokenizer.py (Tokenizer._get_char): get_char and unget_char - have been renamed to _get_char and _unget_char since they are not - useful to clients of the tokenizer. - -2003-07-15 Bob Halley - - * dns/zone.py (_MasterReader._rr_line): owner names were being - unconditionally relativized; it makes much more sense for them - to be relativized according to the relativization setting of - the reader. - -2003-07-12 Bob Halley - - * dns/resolver.py (Resolver.read_resolv_conf): The resolv.conf - parser did not allow blank / whitespace-only lines, nor did it - allow comments. Both are now supported. - -2003-07-11 Bob Halley - - * dns/name.py (Name.to_digestable): to_digestable() now - requires an origin to be specified if the name is relative. - It will raise NeedAbsoluteNameOrOrigin if the name is - relative and there is either no origin or the origin is - itself relative. - (Name.split): returned the wrong answer if depth was 0 or depth - was the length of the name. split() now does bounds checking - on depth, and raises ValueError if depth < 0 or depth > the length - of the name. - -2003-07-10 Bob Halley - - * dns/ipv6.py (inet_ntoa): The routine now minimizes its output - strings. E.g. the IPv6 address - "0000:0000:0000:0000:0000:0000:0000:0001" is minimized to "::1". - We do not, however, make any effort to display embedded IPv4 - addresses in the dot-quad notation. - -2003-07-09 Bob Halley - - * dns/inet.py: We now supply our own AF_INET and AF_INET6 - constants since AF_INET6 may not always be available. If the - socket module has AF_INET6, we will use it. If not, we will - use our own value for the constant. - - * dns/query.py: the functions now take an optional af argument - specifying the address family to use when creating the socket. - - * dns/rdatatype.py (is_metatype): a typo caused the function - return true only for type OPT. - - * dns/message.py: message section list elements are now RRsets - instead of Nodes. This API change makes processing messages - easier for many applications. - -2003-07-07 Bob Halley - - * dns/rrset.py: added. An RRset is a named rdataset. - - * dns/rdataset.py (Rdataset.__eq__): rdatasets may now be compared - for equality and inequality with other objects. Rdataset instance - variables are now slotted. - - * dns/message.py: The wire format and text format readers are now - classes. Variables related to reader state have been moved out - of the message class. - -2003-07-06 Bob Halley - - * dns/name.py (from_text): '@' was not interpreted as the empty - name. - - * dns/zone.py: the master file reader derelativized names in rdata - relative to the zone's origin, not relative to the current origin. - The reader now deals with relativization in two steps. The rdata - is read and derelativized using the current origin. The rdata's - relativity is then chosen using the zone origin and the relativize - boolean. Here's an example. - - $ORIGIN foo.example. - $TTL 300 - bar MX 0 blaz - - If the zone origin is example., and relativization is on, then - This fragment will become: - - bar.foo.example. 300 IN MX 0 blaz.foo.example. - - after the first step (derelativization to current origin), and - - bar.foo 300 IN MX 0 blaz.foo - - after the second step (relativiation to zone origin). - - * dns/namedict.py: added. - - * dns/zone.py: The master file reader has been made into its - own class. Reader-related instance variables have been moved - form the zone class into the reader class. - - * dns/zone.py: Add node_factory class attribute. An application - can now subclass Zone and Node and have a zone whose nodes are of - the subclassed Node type. The from_text(), from_file(), and - from_xfr() algorithms now take an optional zone_factory argument. - This allows the algorithms to be used to create zones whose class - is a subclass of Zone. - - -2003-07-04 Bob Halley - - * dns/renderer.py: added new wire format rendering module and - converted message.py to use it. Applications which want - fine-grained control over the conversion to wire format may call - the renderer directy, instead of having it called on their behalf - by the message code. - -2003-07-02 Bob Halley - - * dns/name.py (_validate_labels): The NameTooLong test was - incorrect. - - * dns/message.py (Message.to_wire): dns.exception.TooBig is - now raised if the wire encoding exceeds the specified - maximum size. - -2003-07-01 Bob Halley - - * dns/message.py: EDNS encoding was broken. from_text() - didn't parse rcodes, flags, or eflags correctly. Comparing - messages with other types of objects didn't work. - -2003-06-30 Bob Halley - - * (Version 1.0.0 released) - -2003-06-30 Bob Halley - - * dns/rdata.py: Rdatas now implement rich comparisons instead of - __cmp__. - - * dns/name.py: Names now implement rich comparisons instead of - __cmp__. - - * dns/inet.py (inet_ntop): Always use our code, since the code - in the socket module doesn't support AF_INET6 conversions if - IPv6 sockets are not available on the system. - - * dns/resolver.py (Answer.__init__): A dangling CNAME chain was - not raising NoAnswer. - - * Added a simple resolver Cache class. - - * Added an expiration attribute to answer instances. - -2003-06-24 Bob Halley - - * (Version 1.0.0b3 released) - -2003-06-24 Bob Halley - - * Renamed module "DNS" to "dns" to avoid conflicting with - PyDNS. - -2003-06-23 Bob Halley - - * The from_text() relativization controls now work the same way as - the to_text() controls. - - * DNS/rdata.py: The parsing of generic rdata was broken. - -2003-06-21 Bob Halley - - * (Version 1.0.0b2 released) - -2003-06-21 Bob Halley - - * The Python 2.2 socket.inet_aton() doesn't seem to like - '255.255.255.255'. We work around this. - - * Fixed bugs in rdata to_wire() and from_wire() routines of a few - types. These bugs were discovered by running the tests/zone.py - Torture1 test. - - * Added implementation of type APL. - -2003-06-20 Bob Halley - - * DNS/rdtypes/IN/AAAA.py: Use our own versions of inet_ntop and - inet_pton if the socket module doesn't provide them for us. - - * The resolver now does a better job handling exceptions. In - particular, it no longer eats all exceptions; rather it handles - those exceptions it understands, and leaves the rest uncaught. - - * Exceptions have been pulled into their own module. Almost all - exceptions raised by the code are now subclasses of - DNS.exception.DNSException. All form errors are subclasses of - DNS.exception.FormError (which is itself a subclass of - DNS.exception.DNSException). - -2003-06-19 Bob Halley - - * Added implementations of types DS, NXT, SIG, and WKS. - - * __cmp__ for type A and AAAA could produce incorrect results. - -2003-06-18 Bob Halley - - * Started test suites for zone.py and tokenizer.py. - - * Added implementation of type KEY. - - * DNS/rdata.py(_base64ify): \n could be emitted erroneously. - - * DNS/rdtypes/ANY/SOA.py (SOA.from_text): The SOA RNAME field could - be set to the value of MNAME in common cases. - - * DNS/rdtypes/ANY/X25.py: __init__ was broken. - - * DNS/zone.py (from_text): $TTL handling erroneously caused the - next line to be eaten. - - * DNS/tokenizer.py (Tokenizer.get): parsing was broken for empty - quoted strings. Quoted strings didn't handle \ddd escapes. Such - escapes are appear not to comply with RFC 1035, but BIND allows - them and they seem useful, so we allow them too. - - * DNS/rdtypes/ANY/ISDN.py (ISDN.from_text): parsing was - broken for ISDN RRs without subaddresses. - - * DNS/zone.py (from_file): from_file() didn't work because - some required parameters were not passed to from_text(). - -2003-06-17 Bob Halley - - * (Version 1.0.0b1 released) - -2003-06-17 Bob Halley - - * Added implementation of type PX. - -2003-06-16 Bob Halley - - * Added implementation of types CERT, GPOS, LOC, NSAP, NSAP-PTR. - - * DNS/rdatatype.py (_by_value): A cut-and-paste error had broken - NSAP and NSAP-PTR. - -2003-06-12 Bob Halley - - * Created a tests directory and started adding tests. - - * Added "and its documentation" to the permission grant in the - license. - -2003-06-12 Bob Halley - - * DNS/name.py (Name.is_wild): is_wild() erroneously raised IndexError - if the name was empty. - -2003-06-10 Bob Halley - - * Added implementations of types AFSDB, X25, and ISDN. - - * The documentation associated with the various rdata types has been - improved. In particular, instance variables are now described. - -2003-06-09 Bob Halley - - * Added implementations of types HINFO, RP, and RT. - - * DNS/message.py (make_query): Document that make_query() sets - flags to DNS.flags.RD, and chooses a random query id. - -2003-06-05 Bob Halley - - * (Version 1.0.0a2 released) - -2003-06-05 Bob Halley - - * DNS/node.py: removed __getitem__ and __setitem__, since - they are not used by the codebase and were not useful in - general either. - - * DNS/message.py (from_file): from_file() now allows a - filename to be specified instead of a file object. - - * DNS/rdataset.py: The is_compatible() method of the - DNS.rdataset.Rdataset class was deleted. - -2003-06-04 Bob Halley - - * DNS/name.py (class Name): Names are now immutable. - - * DNS/name.py: the is_comparable() method has been removed, since - names are always comparable. - - * DNS/resolver.py (Resolver.query): A query could run for up - to the lifetime + the timeout. This has been corrected and the - query will now only run up to the lifetime. - -2003-06-03 Bob Halley - - * DNS/resolver.py: removed the 'new' function since it is not the - style of the library to have such a function. Call - DNS.resolver.Resolver() to make a new resolver. - -2003-06-03 Bob Halley - - * DNS/resolver.py (Resolver._config_win32_fromkey): The DhcpServer - list is space separated, not comma separated. - -2003-06-03 Bob Halley - - * DNS/update.py: Added an update module to make generating updates - easier. - -2003-06-03 Bob Halley - - * Commas were missing in some of the __all__ entries in various - __init__.py files. - -2003-05-30 Bob Halley - - * (Version 1.0.0a1 released) diff --git a/lib/dnspython/LICENSE b/lib/dnspython/LICENSE deleted file mode 100644 index 633c18c1e7..0000000000 --- a/lib/dnspython/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2001-2003 Nominum, Inc. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose with or without fee is hereby granted, -provided that the above copyright notice and this permission notice -appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/lib/dnspython/MANIFEST.in b/lib/dnspython/MANIFEST.in deleted file mode 100644 index d58fb8ba04..0000000000 --- a/lib/dnspython/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include LICENSE ChangeLog TODO -recursive-include examples *.txt *.py -recursive-include tests *.txt *.py Makefile *.good example diff --git a/lib/dnspython/Makefile b/lib/dnspython/Makefile deleted file mode 100644 index 1f62c96c24..0000000000 --- a/lib/dnspython/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -# $Id: Makefile,v 1.16 2004/03/19 00:17:27 halley Exp $ - -PYTHON=python - -all: - ${PYTHON} ./setup.py build - -install: - ${PYTHON} ./setup.py install - -clean: - ${PYTHON} ./setup.py clean --all - find . -name '*.pyc' -exec rm {} \; - find . -name '*.pyo' -exec rm {} \; - rm -f TAGS - -distclean: clean docclean - rm -rf build dist - rm -f MANIFEST - -doc: - epydoc -n dnspython -u http://www.dnspython.org \ - dns/*.py dns/rdtypes/*.py dns/rdtypes/ANY/*.py \ - dns/rdtypes/IN/*.py - -dockits: doc - mv html dnspython-html - tar czf html.tar.gz dnspython-html - zip -r html.zip dnspython-html - mv dnspython-html html - -docclean: - rm -rf html.tar.gz html.zip html - -kits: - ${PYTHON} ./setup.py sdist --formats=gztar,zip -# ${PYTHON} ./setup.py bdist_wininst -# ${PYTHON} ./setup.py bdist_rpm - -tags: - find . -name '*.py' -print | etags - diff --git a/lib/dnspython/README b/lib/dnspython/README deleted file mode 100644 index 367e7a2b76..0000000000 --- a/lib/dnspython/README +++ /dev/null @@ -1,442 +0,0 @@ -dnspython - -INTRODUCTION - -dnspython is a DNS toolkit for Python. It supports almost all record -types. It can be used for queries, zone transfers, and dynamic -updates. It supports TSIG authenticated messages and EDNS0. - -dnspython provides both high and low level access to DNS. The high -level classes perform queries for data of a given name, type, and -class, and return an answer set. The low level classes allow direct -manipulation of DNS zones, messages, names, and records. - -To see a few of the ways dnspython can be used, look in the examples/ -directory. - -dnspython originated at Nominum where it was developed to facilitate -the testing of DNS software. Nominum has generously allowed it to be -open sourced under a BSD-style license, and helps support its future -development by continuing to employ the author :). - - -ABOUT THIS RELEASE - -This is dnspython 1.10.0 - -New since 1.9.4: - - XXX TBS. - -Bugs fixed since 1.9.4: - - XXX TBS. - -New since 1.9.3: - - Nothing. - -Bugs fixed since 1.9.3: - - The rdata _wire_cmp() routine now handles relative names. - - The SIG RR implementation was missing 'import struct'. - -New since 1.9.2: - - A boolean parameter, 'raise_on_no_answer', has been added to - the query() methods. In no-error, no-data situations, this - parameter determines whether NoAnswer should be raised or not. - If True, NoAnswer is raised. If False, then an Answer() - object with a None rrset will be returned. - - Resolver Answer() objects now have a canonical_name field. - - Rdata now have a __hash__ method. - -Bugs fixed since 1.9.2: - - Dnspython was erroneously doing case-insensitive comparisons - of the names in NSEC and RRSIG RRs. - - We now use "is" and not "==" when testing what section an RR - is in. - - The resolver now disallows metaqueries. - -New since 1.9.1: - - Nothing. - -Bugs fixed since 1.9.1: - - The dns.dnssec module didn't work at all due to missing - imports that escaped detection in testing because the test - suite also did the imports. The third time is the charm! - -New since 1.9.0: - - Nothing. - -Bugs fixed since 1.9.0: - - The dns.dnssec module didn't work with DSA due to namespace - contamination from a "from"-style import. - -New since 1.8.0: - - dnspython now uses poll() instead of select() when available. - - Basic DNSSEC validation can be done using dns.dnsec.validate() - and dns.dnssec.validate_rrsig() if you have PyCrypto 2.3 or - later installed. Complete secure resolution is not yet - available. - - Added key_id() to the DNSSEC module, which computes the DNSSEC - key id of a DNSKEY rdata. - - Added make_ds() to the DNSSEC module, which returns the DS RR - for a given DNSKEY rdata. - - dnspython now raises an exception if HMAC-SHA284 or - HMAC-SHA512 are used with a Python older than 2.5.2. (Older - Pythons do not compute the correct value.) - - Symbolic constants are now available for TSIG algorithm names. - -Bugs fixed since 1.8.0 - - dns.resolver.zone_for_name() didn't handle a query response - with a CNAME or DNAME correctly in some cases. - - When specifying rdata types and classes as text, Unicode - strings may now be used. - - Hashlib compatibility issues have been fixed. - - dns.message now imports dns.edns. - - The TSIG algorithm value was passed incorrectly to use_tsig() - in some cases. - -New since 1.7.1: - - Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384 - and hmac-sha512 has been contributed by Kevin Chen. - - The tokenizer's tokens are now Token objects instead of (type, - value) tuples. - -Bugs fixed since 1.7.1: - - Escapes in masterfiles now work correctly. Previously they - were only working correctly when the text involved was part of - a domain name. - - When constructing a DDNS update, if the present() method was - used with a single rdata, a zero TTL was not added. - - The entropy pool needed locking to be thread safe. - - The entropy pool's reading of /dev/random could cause - dnspython to block. - - The entropy pool did buffered reads, potentially consuming more - randomness than we needed. - - The entropy pool did not seed with high quality randomness on - Windows. - - SRV records were compared incorrectly. - - In the e164 query function, the resolver parameter was not - used. - -New since 1.7.0: - - Nothing - -Bugs fixed since 1.7.0: - - The 1.7.0 kitting process inadventently omitted the code for the - DLV RR. - - Negative DDNS prerequisites are now handled correctly. - -New since 1.6.0: - - Rdatas now have a to_digestable() method, which returns the - DNSSEC canonical form of the rdata, suitable for use in - signature computations. - - The NSEC3, NSEC3PARAM, DLV, and HIP RR types are now supported. - - An entropy module has been added and is used to randomize query ids. - - EDNS0 options are now supported. - - UDP IXFR is now supported. - - The wire format parser now has a 'one_rr_per_rrset' mode, which - suppresses the usual coalescing of all RRs of a given type into a - single RRset. - - Various helpful DNSSEC-related constants are now defined. - - The resolver's query() method now has an optional 'source' parameter, - allowing the source IP address to be specified. - -Bugs fixed since 1.6.0: - - On Windows, the resolver set the domain incorrectly. - - DS RR parsing only allowed one Base64 chunk. - - TSIG validation didn't always use absolute names. - - NSEC.to_text() only printed the last window. - - We did not canonicalize IPv6 addresses before comparing them; we - would thus treat equivalent but different textual forms, e.g. - "1:00::1" and "1::1" as being non-equivalent. - - If the peer set a TSIG error, we didn't raise an exception. - - Some EDNS bugs in the message code have been fixed (see the ChangeLog - for details). - -New since 1.5.0: - Added dns.inet.is_multicast(). - -Bugs fixed since 1.5.0: - - If select() raises an exception due to EINTR, we should just - select() again. - - If the queried address is a multicast address, then don't - check that the address of the response is the same as the - address queried. - - NAPTR comparisons didn't compare the preference field due to a - typo. - - Testing of whether a Windows NIC is enabled now works on Vista - thanks to code contributed by Paul Marks. - -New since 1.4.0: - - Answer objects now support more of the python sequence - protocol, forwarding the requests to the answer rrset. - E.g. "for a in answer" is equivalent to "for a in - answer.rrset", "answer[i]" is equivalent to "answer.rrset[i]", - and "answer[i:j]" is equivalent to "answer.rrset[i:j]". - - Making requests using EDNS, including indicating DNSSEC awareness, - is now easier. For example, you can now say: - - q = dns.message.make_query('www.dnspython.org', 'MX', - want_dnssec=True) - - dns.query.xfr() can now be used for IXFR. - - Support has been added for the DHCID, IPSECKEY, and SPF RR types. - - UDP messages from unexpected sources can now be ignored by - setting ignore_unexpected to True when calling dns.query.udp. - -Bugs fixed since 1.4.0: - - If /etc/resolv.conf didn't exist, we raised an exception - instead of simply using the default resolver configuration. - - In dns.resolver.Resolver._config_win32_fromkey(), we were - passing the wrong variable to self._config_win32_search(). - -New since 1.3.5: - - You can now convert E.164 numbers to/from their ENUM name - forms: - - >>> import dns.e164 - >>> n = dns.e164.from_e164("+1 555 1212") - >>> n - - >>> dns.e164.to_e164(n) - '+15551212' - - You can now convert IPv4 and IPv6 address to/from their - corresponding DNS reverse map names: - - >>> import dns.reversename - >>> n = dns.reversename.from_address("127.0.0.1") - >>> n - - >>> dns.reversename.to_address(n) - '127.0.0.1' - - You can now convert between Unicode strings and their IDN ACE - form: - - >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.') - >>> n - - >>> n.to_unicode() - u'les-\xe9l\xe8ves.example.' - - The origin parameter to dns.zone.from_text() and dns.zone.to_text() - is now optional. If not specified, the origin will be taken from - the first $ORIGIN statement in the master file. - - Sanity checking of a zone can be disabled; this is useful when - working with files which are zone fragments. - -Bugs fixed since 1.3.5: - - The correct delimiter was not used when retrieving the - list of nameservers from the registry in certain versions of - windows. - - The floating-point version of latitude and longitude in LOC RRs - (float_latitude and float_longitude) had incorrect signs for - south latitudes and west longitudes. - - BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. - SOA fields refresh, retry, expire, and minimum; SIG/RRSIG - field original_ttl). - - TTLs are now bounds checked when their text form is parsed, - and their values must be in the closed interval [0, 2^31 - 1]. - -New since 1.3.4: - - In the resolver, if time goes backward a little bit, ignore - it. - - zone_for_name() has been added to the resolver module. It - returns the zone which is authoritative for the specified - name, which is handy for dynamic update. E.g. - - import dns.resolver - print dns.resolver.zone_for_name('www.dnspython.org') - - will output "dnspython.org." and - - print dns.resolver.zone_for_name('a.b.c.d.e.f.example.') - - will output ".". - - The default resolver can be fetched with the - get_default_resolver() method. - - You can now get the parent (immediate superdomain) of a name - by using the parent() method. - - Zone.iterate_rdatasets() and Zone.iterate_rdatas() now have - a default rdtype of dns.rdatatype.ANY like the documentation - says. - - A Dynamic DNS example, ddns.py, has been added. - -New since 1.3.3: - - The source address and port may now be specified when calling - dns.query.{udp,tcp,xfr}. - - The resolver now does exponential backoff each time it runs - through all of the nameservers. - - Rcodes which indicate a nameserver is likely to be a - "permanent failure" for a query cause the nameserver to be removed - from the mix for that query. - -New since 1.3.2: - - dns.message.Message.find_rrset() now uses an index, vastly - improving the from_wire() performance of large messages such - as zone transfers. - - Added dns.message.make_response(), which creates a skeletal - response for the specified query. - - Added opcode() and set_opcode() convenience methods to the - dns.message.Message class. Added the request_payload - attribute to the Message class. - - The 'file' parameter of dns.name.Name.to_wire() is now - optional; if omitted, the wire form will be returned as the - value of the function. - - dns.zone.from_xfr() in relativization mode incorrectly set - zone.origin to the empty name. - - The masterfile parser incorrectly rejected TXT records where a - value was not quoted. - -New since 1.3.1: - - The NSEC format doesn't allow specifying types by number, so - we shouldn't either. (Using the unknown type format is still - OK though.) - - The resolver wasn't catching dns.exception.Timeout, so a timeout - erroneously caused the whole resolution to fail instead of just - going on to the next server. - - The renderer module didn't import random, causing an exception - to be raised if a query id wasn't provided when a Renderer was - created. - - The conversion of LOC milliseconds values from text to binary was - incorrect if the length of the milliseconds string was not 3. - -New since 1.3.0: - - Added support for the SSHFP type. - -New since 1.2.0: - - Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. - -This release fixes all known bugs. - -See the ChangeLog file for more detailed information on changes since -the prior release. - - -REQUIREMENTS - -Python 2.4 or later. - - -INSTALLATION - -To build and install dnspython, type - - python setup.py install - - -HOME PAGE - -For the latest in releases, documentation, and information, visit the -dnspython home page at - - http://www.dnspython.org/ - - - -DOCUMENTATION - -Documentation is sparse at the moment. Use pydoc, or read the HTML -documentation at the dnspython home page, or download the HTML -documentation. - - -BUG REPORTS - -Bug reports may be sent to bugs@dnspython.org - - -MAILING LISTS - -A number of mailing lists are available. Visit the dnspython home -page to subscribe or unsubscribe. diff --git a/lib/dnspython/TODO b/lib/dnspython/TODO deleted file mode 100644 index 59ce1be1ce..0000000000 --- a/lib/dnspython/TODO +++ /dev/null @@ -1,17 +0,0 @@ -Tutorial documentation - -More examples - -It would be nice to have a tokenizer that used regular expressions -because it would be faster. - -Teach the resolver about DNAME (right now it relies on the server adding -synthesized CNAMEs) - -Add TKEY support. - -TSIG works, but needs cleaning up -- probably better encapsulation of -TSIG state to make things much simpler and easier to use. - -Pickling support. - diff --git a/lib/dnspython/dns/__init__.py b/lib/dnspython/dns/__init__.py deleted file mode 100644 index c848e48581..0000000000 --- a/lib/dnspython/dns/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""dnspython DNS toolkit""" - -__all__ = [ - 'dnssec', - 'e164', - 'edns', - 'entropy', - 'exception', - 'flags', - 'hash', - 'inet', - 'ipv4', - 'ipv6', - 'message', - 'name', - 'namedict', - 'node', - 'opcode', - 'query', - 'rcode', - 'rdata', - 'rdataclass', - 'rdataset', - 'rdatatype', - 'renderer', - 'resolver', - 'reversename', - 'rrset', - 'set', - 'tokenizer', - 'tsig', - 'tsigkeyring', - 'ttl', - 'rdtypes', - 'update', - 'version', - 'wiredata', - 'zone', -] diff --git a/lib/dnspython/dns/dnssec.py b/lib/dnspython/dns/dnssec.py deleted file mode 100644 index dd6a27a536..0000000000 --- a/lib/dnspython/dns/dnssec.py +++ /dev/null @@ -1,372 +0,0 @@ -# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNSSEC-related functions and constants.""" - -import cStringIO -import struct -import time - -import dns.exception -import dns.hash -import dns.name -import dns.node -import dns.rdataset -import dns.rdata -import dns.rdatatype -import dns.rdataclass - -class UnsupportedAlgorithm(dns.exception.DNSException): - """Raised if an algorithm is not supported.""" - pass - -class ValidationFailure(dns.exception.DNSException): - """The DNSSEC signature is invalid.""" - pass - -RSAMD5 = 1 -DH = 2 -DSA = 3 -ECC = 4 -RSASHA1 = 5 -DSANSEC3SHA1 = 6 -RSASHA1NSEC3SHA1 = 7 -RSASHA256 = 8 -RSASHA512 = 10 -INDIRECT = 252 -PRIVATEDNS = 253 -PRIVATEOID = 254 - -_algorithm_by_text = { - 'RSAMD5' : RSAMD5, - 'DH' : DH, - 'DSA' : DSA, - 'ECC' : ECC, - 'RSASHA1' : RSASHA1, - 'DSANSEC3SHA1' : DSANSEC3SHA1, - 'RSASHA1NSEC3SHA1' : RSASHA1NSEC3SHA1, - 'RSASHA256' : RSASHA256, - 'RSASHA512' : RSASHA512, - 'INDIRECT' : INDIRECT, - 'PRIVATEDNS' : PRIVATEDNS, - 'PRIVATEOID' : PRIVATEOID, - } - -# We construct the inverse mapping programmatically to ensure that we -# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that -# would cause the mapping not to be true inverse. - -_algorithm_by_value = dict([(y, x) for x, y in _algorithm_by_text.iteritems()]) - -def algorithm_from_text(text): - """Convert text into a DNSSEC algorithm value - @rtype: int""" - - value = _algorithm_by_text.get(text.upper()) - if value is None: - value = int(text) - return value - -def algorithm_to_text(value): - """Convert a DNSSEC algorithm value to text - @rtype: string""" - - text = _algorithm_by_value.get(value) - if text is None: - text = str(value) - return text - -def _to_rdata(record, origin): - s = cStringIO.StringIO() - record.to_wire(s, origin=origin) - return s.getvalue() - -def key_id(key, origin=None): - rdata = _to_rdata(key, origin) - if key.algorithm == RSAMD5: - return (ord(rdata[-3]) << 8) + ord(rdata[-2]) - else: - total = 0 - for i in range(len(rdata) // 2): - total += (ord(rdata[2 * i]) << 8) + ord(rdata[2 * i + 1]) - if len(rdata) % 2 != 0: - total += ord(rdata[len(rdata) - 1]) << 8 - total += ((total >> 16) & 0xffff); - return total & 0xffff - -def make_ds(name, key, algorithm, origin=None): - if algorithm.upper() == 'SHA1': - dsalg = 1 - hash = dns.hash.get('SHA1')() - elif algorithm.upper() == 'SHA256': - dsalg = 2 - hash = dns.hash.get('SHA256')() - else: - raise UnsupportedAlgorithm, 'unsupported algorithm "%s"' % algorithm - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, origin) - hash.update(name.canonicalize().to_wire()) - hash.update(_to_rdata(key, origin)) - digest = hash.digest() - - dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest - return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, - len(dsrdata)) - -def _find_key(keys, rrsig): - value = keys.get(rrsig.signer) - if value is None: - return None - if isinstance(value, dns.node.Node): - try: - rdataset = node.find_rdataset(dns.rdataclass.IN, - dns.rdatatype.DNSKEY) - except KeyError: - return None - else: - rdataset = value - for rdata in rdataset: - if rdata.algorithm == rrsig.algorithm and \ - key_id(rdata) == rrsig.key_tag: - return rdata - return None - -def _is_rsa(algorithm): - return algorithm in (RSAMD5, RSASHA1, - RSASHA1NSEC3SHA1, RSASHA256, - RSASHA512) - -def _is_dsa(algorithm): - return algorithm in (DSA, DSANSEC3SHA1) - -def _is_md5(algorithm): - return algorithm == RSAMD5 - -def _is_sha1(algorithm): - return algorithm in (DSA, RSASHA1, - DSANSEC3SHA1, RSASHA1NSEC3SHA1) - -def _is_sha256(algorithm): - return algorithm == RSASHA256 - -def _is_sha512(algorithm): - return algorithm == RSASHA512 - -def _make_hash(algorithm): - if _is_md5(algorithm): - return dns.hash.get('MD5')() - if _is_sha1(algorithm): - return dns.hash.get('SHA1')() - if _is_sha256(algorithm): - return dns.hash.get('SHA256')() - if _is_sha512(algorithm): - return dns.hash.get('SHA512')() - raise ValidationFailure, 'unknown hash for algorithm %u' % algorithm - -def _make_algorithm_id(algorithm): - if _is_md5(algorithm): - oid = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05] - elif _is_sha1(algorithm): - oid = [0x2b, 0x0e, 0x03, 0x02, 0x1a] - elif _is_sha256(algorithm): - oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01] - elif _is_sha512(algorithm): - oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03] - else: - raise ValidationFailure, 'unknown algorithm %u' % algorithm - olen = len(oid) - dlen = _make_hash(algorithm).digest_size - idbytes = [0x30] + [8 + olen + dlen] + \ - [0x30, olen + 4] + [0x06, olen] + oid + \ - [0x05, 0x00] + [0x04, dlen] - return ''.join(map(chr, idbytes)) - -def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): - """Validate an RRset against a single signature rdata - - The owner name of the rrsig is assumed to be the same as the owner name - of the rrset. - - @param rrset: The RRset to validate - @type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) - tuple - @param rrsig: The signature rdata - @type rrsig: dns.rrset.Rdata - @param keys: The key dictionary. - @type keys: a dictionary keyed by dns.name.Name with node or rdataset values - @param origin: The origin to use for relative names - @type origin: dns.name.Name or None - @param now: The time to use when validating the signatures. The default - is the current time. - @type now: int - """ - - if isinstance(origin, (str, unicode)): - origin = dns.name.from_text(origin, dns.name.root) - - key = _find_key(keys, rrsig) - if not key: - raise ValidationFailure, 'unknown key' - - # For convenience, allow the rrset to be specified as a (name, rdataset) - # tuple as well as a proper rrset - if isinstance(rrset, tuple): - rrname = rrset[0] - rdataset = rrset[1] - else: - rrname = rrset.name - rdataset = rrset - - if now is None: - now = time.time() - if rrsig.expiration < now: - raise ValidationFailure, 'expired' - if rrsig.inception > now: - raise ValidationFailure, 'not yet valid' - - hash = _make_hash(rrsig.algorithm) - - if _is_rsa(rrsig.algorithm): - keyptr = key.key - (bytes,) = struct.unpack('!B', keyptr[0:1]) - keyptr = keyptr[1:] - if bytes == 0: - (bytes,) = struct.unpack('!H', keyptr[0:2]) - keyptr = keyptr[2:] - rsa_e = keyptr[0:bytes] - rsa_n = keyptr[bytes:] - keylen = len(rsa_n) * 8 - pubkey = Crypto.PublicKey.RSA.construct( - (Crypto.Util.number.bytes_to_long(rsa_n), - Crypto.Util.number.bytes_to_long(rsa_e))) - sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),) - elif _is_dsa(rrsig.algorithm): - keyptr = key.key - (t,) = struct.unpack('!B', keyptr[0:1]) - keyptr = keyptr[1:] - octets = 64 + t * 8 - dsa_q = keyptr[0:20] - keyptr = keyptr[20:] - dsa_p = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_g = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_y = keyptr[0:octets] - pubkey = Crypto.PublicKey.DSA.construct( - (Crypto.Util.number.bytes_to_long(dsa_y), - Crypto.Util.number.bytes_to_long(dsa_g), - Crypto.Util.number.bytes_to_long(dsa_p), - Crypto.Util.number.bytes_to_long(dsa_q))) - (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:]) - sig = (Crypto.Util.number.bytes_to_long(dsa_r), - Crypto.Util.number.bytes_to_long(dsa_s)) - else: - raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm - - hash.update(_to_rdata(rrsig, origin)[:18]) - hash.update(rrsig.signer.to_digestable(origin)) - - if rrsig.labels < len(rrname) - 1: - suffix = rrname.split(rrsig.labels + 1)[1] - rrname = dns.name.from_text('*', suffix) - rrnamebuf = rrname.to_digestable(origin) - rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, - rrsig.original_ttl) - rrlist = sorted(rdataset); - for rr in rrlist: - hash.update(rrnamebuf) - hash.update(rrfixed) - rrdata = rr.to_digestable(origin) - rrlen = struct.pack('!H', len(rrdata)) - hash.update(rrlen) - hash.update(rrdata) - - digest = hash.digest() - - if _is_rsa(rrsig.algorithm): - # PKCS1 algorithm identifier goop - digest = _make_algorithm_id(rrsig.algorithm) + digest - padlen = keylen // 8 - len(digest) - 3 - digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest - elif _is_dsa(rrsig.algorithm): - pass - else: - # Raise here for code clarity; this won't actually ever happen - # since if the algorithm is really unknown we'd already have - # raised an exception above - raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm - - if not pubkey.verify(digest, sig): - raise ValidationFailure, 'verify failure' - -def _validate(rrset, rrsigset, keys, origin=None, now=None): - """Validate an RRset - - @param rrset: The RRset to validate - @type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) - tuple - @param rrsigset: The signature RRset - @type rrsigset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) - tuple - @param keys: The key dictionary. - @type keys: a dictionary keyed by dns.name.Name with node or rdataset values - @param origin: The origin to use for relative names - @type origin: dns.name.Name or None - @param now: The time to use when validating the signatures. The default - is the current time. - @type now: int - """ - - if isinstance(origin, (str, unicode)): - origin = dns.name.from_text(origin, dns.name.root) - - if isinstance(rrset, tuple): - rrname = rrset[0] - else: - rrname = rrset.name - - if isinstance(rrsigset, tuple): - rrsigname = rrsigset[0] - rrsigrdataset = rrsigset[1] - else: - rrsigname = rrsigset.name - rrsigrdataset = rrsigset - - rrname = rrname.choose_relativity(origin) - rrsigname = rrname.choose_relativity(origin) - if rrname != rrsigname: - raise ValidationFailure, "owner names do not match" - - for rrsig in rrsigrdataset: - try: - _validate_rrsig(rrset, rrsig, keys, origin, now) - return - except ValidationFailure, e: - pass - raise ValidationFailure, "no RRSIGs validated" - -def _need_pycrypto(*args, **kwargs): - raise NotImplementedError, "DNSSEC validation requires pycrypto" - -try: - import Crypto.PublicKey.RSA - import Crypto.PublicKey.DSA - import Crypto.Util.number - validate = _validate - validate_rrsig = _validate_rrsig -except ImportError: - validate = _need_pycrypto - validate_rrsig = _need_pycrypto diff --git a/lib/dnspython/dns/e164.py b/lib/dnspython/dns/e164.py deleted file mode 100644 index d6dcd1b186..0000000000 --- a/lib/dnspython/dns/e164.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (C) 2006, 2007, 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS E.164 helpers - -@var public_enum_domain: The DNS public ENUM domain, e164.arpa. -@type public_enum_domain: dns.name.Name object -""" - -import dns.exception -import dns.name -import dns.resolver - -public_enum_domain = dns.name.from_text('e164.arpa.') - -def from_e164(text, origin=public_enum_domain): - """Convert an E.164 number in textual form into a Name object whose - value is the ENUM domain name for that number. - @param text: an E.164 number in textual form. - @type text: str - @param origin: The domain in which the number should be constructed. - The default is e164.arpa. - @type: dns.name.Name object or None - @rtype: dns.name.Name object - """ - parts = [d for d in text if d.isdigit()] - parts.reverse() - return dns.name.from_text('.'.join(parts), origin=origin) - -def to_e164(name, origin=public_enum_domain, want_plus_prefix=True): - """Convert an ENUM domain name into an E.164 number. - @param name: the ENUM domain name. - @type name: dns.name.Name object. - @param origin: A domain containing the ENUM domain name. The - name is relativized to this domain before being converted to text. - @type: dns.name.Name object or None - @param want_plus_prefix: if True, add a '+' to the beginning of the - returned number. - @rtype: str - """ - if not origin is None: - name = name.relativize(origin) - dlabels = [d for d in name.labels if (d.isdigit() and len(d) == 1)] - if len(dlabels) != len(name.labels): - raise dns.exception.SyntaxError('non-digit labels in ENUM domain name') - dlabels.reverse() - text = ''.join(dlabels) - if want_plus_prefix: - text = '+' + text - return text - -def query(number, domains, resolver=None): - """Look for NAPTR RRs for the specified number in the specified domains. - - e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) - """ - if resolver is None: - resolver = dns.resolver.get_default_resolver() - for domain in domains: - if isinstance(domain, (str, unicode)): - domain = dns.name.from_text(domain) - qname = dns.e164.from_e164(number, domain) - try: - return resolver.query(qname, 'NAPTR') - except dns.resolver.NXDOMAIN: - pass - raise dns.resolver.NXDOMAIN diff --git a/lib/dnspython/dns/edns.py b/lib/dnspython/dns/edns.py deleted file mode 100644 index f8b6009d3f..0000000000 --- a/lib/dnspython/dns/edns.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (C) 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""EDNS Options""" - -NSID = 3 - -class Option(object): - """Base class for all EDNS option types. - """ - - def __init__(self, otype): - """Initialize an option. - @param rdtype: The rdata type - @type rdtype: int - """ - self.otype = otype - - def to_wire(self, file): - """Convert an option to wire format. - """ - raise NotImplementedError - - def from_wire(cls, otype, wire, current, olen): - """Build an EDNS option object from wire format - - @param otype: The option type - @type otype: int - @param wire: The wire-format message - @type wire: string - @param current: The offet in wire of the beginning of the rdata. - @type current: int - @param olen: The length of the wire-format option data - @type olen: int - @rtype: dns.ends.Option instance""" - raise NotImplementedError - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - """Compare an ENDS option with another option of the same type. - Return < 0 if self < other, 0 if self == other, and > 0 if self > other. - """ - raise NotImplementedError - - def __eq__(self, other): - if not isinstance(other, Option): - return False - if self.otype != other.otype: - return False - return self._cmp(other) == 0 - - def __ne__(self, other): - if not isinstance(other, Option): - return False - if self.otype != other.otype: - return False - return self._cmp(other) != 0 - - def __lt__(self, other): - if not isinstance(other, Option) or \ - self.otype != other.otype: - return NotImplemented - return self._cmp(other) < 0 - - def __le__(self, other): - if not isinstance(other, Option) or \ - self.otype != other.otype: - return NotImplemented - return self._cmp(other) <= 0 - - def __ge__(self, other): - if not isinstance(other, Option) or \ - self.otype != other.otype: - return NotImplemented - return self._cmp(other) >= 0 - - def __gt__(self, other): - if not isinstance(other, Option) or \ - self.otype != other.otype: - return NotImplemented - return self._cmp(other) > 0 - - -class GenericOption(Option): - """Generate Rdata Class - - This class is used for EDNS option types for which we have no better - implementation. - """ - - def __init__(self, otype, data): - super(GenericOption, self).__init__(otype) - self.data = data - - def to_wire(self, file): - file.write(self.data) - - def from_wire(cls, otype, wire, current, olen): - return cls(otype, wire[current : current + olen]) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - return cmp(self.data, other.data) - -_type_to_class = { -} - -def get_option_class(otype): - cls = _type_to_class.get(otype) - if cls is None: - cls = GenericOption - return cls - -def option_from_wire(otype, wire, current, olen): - """Build an EDNS option object from wire format - - @param otype: The option type - @type otype: int - @param wire: The wire-format message - @type wire: string - @param current: The offet in wire of the beginning of the rdata. - @type current: int - @param olen: The length of the wire-format option data - @type olen: int - @rtype: dns.ends.Option instance""" - - cls = get_option_class(otype) - return cls.from_wire(otype, wire, current, olen) diff --git a/lib/dnspython/dns/entropy.py b/lib/dnspython/dns/entropy.py deleted file mode 100644 index d380cf8c0d..0000000000 --- a/lib/dnspython/dns/entropy.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (C) 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import os -import time -try: - import threading as _threading -except ImportError: - import dummy_threading as _threading - -class EntropyPool(object): - def __init__(self, seed=None): - self.pool_index = 0 - self.digest = None - self.next_byte = 0 - self.lock = _threading.Lock() - try: - import hashlib - self.hash = hashlib.sha1() - self.hash_len = 20 - except: - try: - import sha - self.hash = sha.new() - self.hash_len = 20 - except: - import md5 - self.hash = md5.new() - self.hash_len = 16 - self.pool = '\0' * self.hash_len - if not seed is None: - self.stir(seed) - self.seeded = True - else: - self.seeded = False - - def stir(self, entropy, already_locked=False): - if not already_locked: - self.lock.acquire() - try: - bytes = [ord(c) for c in self.pool] - for c in entropy: - if self.pool_index == self.hash_len: - self.pool_index = 0 - b = ord(c) & 0xff - bytes[self.pool_index] ^= b - self.pool_index += 1 - self.pool = ''.join([chr(c) for c in bytes]) - finally: - if not already_locked: - self.lock.release() - - def _maybe_seed(self): - if not self.seeded: - try: - seed = os.urandom(16) - except: - try: - r = file('/dev/urandom', 'r', 0) - try: - seed = r.read(16) - finally: - r.close() - except: - seed = str(time.time()) - self.seeded = True - self.stir(seed, True) - - def random_8(self): - self.lock.acquire() - self._maybe_seed() - try: - if self.digest is None or self.next_byte == self.hash_len: - self.hash.update(self.pool) - self.digest = self.hash.digest() - self.stir(self.digest, True) - self.next_byte = 0 - value = ord(self.digest[self.next_byte]) - self.next_byte += 1 - finally: - self.lock.release() - return value - - def random_16(self): - return self.random_8() * 256 + self.random_8() - - def random_32(self): - return self.random_16() * 65536 + self.random_16() - - def random_between(self, first, last): - size = last - first + 1 - if size > 4294967296L: - raise ValueError('too big') - if size > 65536: - rand = self.random_32 - max = 4294967295L - elif size > 256: - rand = self.random_16 - max = 65535 - else: - rand = self.random_8 - max = 255 - return (first + size * rand() // (max + 1)) - -pool = EntropyPool() - -def random_16(): - return pool.random_16() - -def between(first, last): - return pool.random_between(first, last) diff --git a/lib/dnspython/dns/exception.py b/lib/dnspython/dns/exception.py deleted file mode 100644 index db6ef6e6e3..0000000000 --- a/lib/dnspython/dns/exception.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNS Exceptions.""" - -class DNSException(Exception): - """Abstract base class shared by all dnspython exceptions.""" - pass - -class FormError(DNSException): - """DNS message is malformed.""" - pass - -class SyntaxError(DNSException): - """Text input is malformed.""" - pass - -class UnexpectedEnd(SyntaxError): - """Raised if text input ends unexpectedly.""" - pass - -class TooBig(DNSException): - """The message is too big.""" - pass - -class Timeout(DNSException): - """The operation timed out.""" - pass diff --git a/lib/dnspython/dns/flags.py b/lib/dnspython/dns/flags.py deleted file mode 100644 index 35a8305e80..0000000000 --- a/lib/dnspython/dns/flags.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Message Flags.""" - -# Standard DNS flags - -QR = 0x8000 -AA = 0x0400 -TC = 0x0200 -RD = 0x0100 -RA = 0x0080 -AD = 0x0020 -CD = 0x0010 - -# EDNS flags - -DO = 0x8000 - -_by_text = { - 'QR' : QR, - 'AA' : AA, - 'TC' : TC, - 'RD' : RD, - 'RA' : RA, - 'AD' : AD, - 'CD' : CD -} - -_edns_by_text = { - 'DO' : DO -} - - -# We construct the inverse mappings programmatically to ensure that we -# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that -# would cause the mappings not to be true inverses. - -_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) - -_edns_by_value = dict([(y, x) for x, y in _edns_by_text.iteritems()]) - -def _order_flags(table): - order = list(table.iteritems()) - order.sort() - order.reverse() - return order - -_flags_order = _order_flags(_by_value) - -_edns_flags_order = _order_flags(_edns_by_value) - -def _from_text(text, table): - flags = 0 - tokens = text.split() - for t in tokens: - flags = flags | table[t.upper()] - return flags - -def _to_text(flags, table, order): - text_flags = [] - for k, v in order: - if flags & k != 0: - text_flags.append(v) - return ' '.join(text_flags) - -def from_text(text): - """Convert a space-separated list of flag text values into a flags - value. - @rtype: int""" - - return _from_text(text, _by_text) - -def to_text(flags): - """Convert a flags value into a space-separated list of flag text - values. - @rtype: string""" - - return _to_text(flags, _by_value, _flags_order) - - -def edns_from_text(text): - """Convert a space-separated list of EDNS flag text values into a EDNS - flags value. - @rtype: int""" - - return _from_text(text, _edns_by_text) - -def edns_to_text(flags): - """Convert an EDNS flags value into a space-separated list of EDNS flag - text values. - @rtype: string""" - - return _to_text(flags, _edns_by_value, _edns_flags_order) diff --git a/lib/dnspython/dns/hash.py b/lib/dnspython/dns/hash.py deleted file mode 100644 index 0c708036b1..0000000000 --- a/lib/dnspython/dns/hash.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Hashing backwards compatibility wrapper""" - -import sys - -_hashes = None - -def _need_later_python(alg): - def func(*args, **kwargs): - raise NotImplementedError("TSIG algorithm " + alg + - " requires Python 2.5.2 or later") - return func - -def _setup(): - global _hashes - _hashes = {} - try: - import hashlib - _hashes['MD5'] = hashlib.md5 - _hashes['SHA1'] = hashlib.sha1 - _hashes['SHA224'] = hashlib.sha224 - _hashes['SHA256'] = hashlib.sha256 - if sys.hexversion >= 0x02050200: - _hashes['SHA384'] = hashlib.sha384 - _hashes['SHA512'] = hashlib.sha512 - else: - _hashes['SHA384'] = _need_later_python('SHA384') - _hashes['SHA512'] = _need_later_python('SHA512') - - if sys.hexversion < 0x02050000: - # hashlib doesn't conform to PEP 247: API for - # Cryptographic Hash Functions, which hmac before python - # 2.5 requires, so add the necessary items. - class HashlibWrapper: - def __init__(self, basehash): - self.basehash = basehash - self.digest_size = self.basehash().digest_size - - def new(self, *args, **kwargs): - return self.basehash(*args, **kwargs) - - for name in _hashes: - _hashes[name] = HashlibWrapper(_hashes[name]) - - except ImportError: - import md5, sha - _hashes['MD5'] = md5 - _hashes['SHA1'] = sha - -def get(algorithm): - if _hashes is None: - _setup() - return _hashes[algorithm.upper()] diff --git a/lib/dnspython/dns/inet.py b/lib/dnspython/dns/inet.py deleted file mode 100644 index 3b7e88f7b1..0000000000 --- a/lib/dnspython/dns/inet.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Generic Internet address helper functions.""" - -import socket - -import dns.ipv4 -import dns.ipv6 - - -# We assume that AF_INET is always defined. - -AF_INET = socket.AF_INET - -# AF_INET6 might not be defined in the socket module, but we need it. -# We'll try to use the socket module's value, and if it doesn't work, -# we'll use our own value. - -try: - AF_INET6 = socket.AF_INET6 -except AttributeError: - AF_INET6 = 9999 - -def inet_pton(family, text): - """Convert the textual form of a network address into its binary form. - - @param family: the address family - @type family: int - @param text: the textual address - @type text: string - @raises NotImplementedError: the address family specified is not - implemented. - @rtype: string - """ - - if family == AF_INET: - return dns.ipv4.inet_aton(text) - elif family == AF_INET6: - return dns.ipv6.inet_aton(text) - else: - raise NotImplementedError - -def inet_ntop(family, address): - """Convert the binary form of a network address into its textual form. - - @param family: the address family - @type family: int - @param address: the binary address - @type address: string - @raises NotImplementedError: the address family specified is not - implemented. - @rtype: string - """ - if family == AF_INET: - return dns.ipv4.inet_ntoa(address) - elif family == AF_INET6: - return dns.ipv6.inet_ntoa(address) - else: - raise NotImplementedError - -def af_for_address(text): - """Determine the address family of a textual-form network address. - - @param text: the textual address - @type text: string - @raises ValueError: the address family cannot be determined from the input. - @rtype: int - """ - try: - junk = dns.ipv4.inet_aton(text) - return AF_INET - except: - try: - junk = dns.ipv6.inet_aton(text) - return AF_INET6 - except: - raise ValueError - -def is_multicast(text): - """Is the textual-form network address a multicast address? - - @param text: the textual address - @raises ValueError: the address family cannot be determined from the input. - @rtype: bool - """ - try: - first = ord(dns.ipv4.inet_aton(text)[0]) - return (first >= 224 and first <= 239) - except: - try: - first = ord(dns.ipv6.inet_aton(text)[0]) - return (first == 255) - except: - raise ValueError - diff --git a/lib/dnspython/dns/ipv4.py b/lib/dnspython/dns/ipv4.py deleted file mode 100644 index e117966e5f..0000000000 --- a/lib/dnspython/dns/ipv4.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""IPv4 helper functions.""" - -import struct - -import dns.exception - -def inet_ntoa(address): - if len(address) != 4: - raise dns.exception.SyntaxError - return '%u.%u.%u.%u' % (ord(address[0]), ord(address[1]), - ord(address[2]), ord(address[3])) - -def inet_aton(text): - parts = text.split('.') - if len(parts) != 4: - raise dns.exception.SyntaxError - for part in parts: - if not part.isdigit(): - raise dns.exception.SyntaxError - if len(part) > 1 and part[0] == '0': - # No leading zeros - raise dns.exception.SyntaxError - try: - bytes = [int(part) for part in parts] - return struct.pack('BBBB', *bytes) - except: - raise dns.exception.SyntaxError diff --git a/lib/dnspython/dns/ipv6.py b/lib/dnspython/dns/ipv6.py deleted file mode 100644 index 69db34a7de..0000000000 --- a/lib/dnspython/dns/ipv6.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""IPv6 helper functions.""" - -import re - -import dns.exception -import dns.ipv4 - -_leading_zero = re.compile(r'0+([0-9a-f]+)') - -def inet_ntoa(address): - """Convert a network format IPv6 address into text. - - @param address: the binary address - @type address: string - @rtype: string - @raises ValueError: the address isn't 16 bytes long - """ - - if len(address) != 16: - raise ValueError("IPv6 addresses are 16 bytes long") - hex = address.encode('hex_codec') - chunks = [] - i = 0 - l = len(hex) - while i < l: - chunk = hex[i : i + 4] - # strip leading zeros. we do this with an re instead of - # with lstrip() because lstrip() didn't support chars until - # python 2.2.2 - m = _leading_zero.match(chunk) - if not m is None: - chunk = m.group(1) - chunks.append(chunk) - i += 4 - # - # Compress the longest subsequence of 0-value chunks to :: - # - best_start = 0 - best_len = 0 - start = -1 - last_was_zero = False - for i in xrange(8): - if chunks[i] != '0': - if last_was_zero: - end = i - current_len = end - start - if current_len > best_len: - best_start = start - best_len = current_len - last_was_zero = False - elif not last_was_zero: - start = i - last_was_zero = True - if last_was_zero: - end = 8 - current_len = end - start - if current_len > best_len: - best_start = start - best_len = current_len - if best_len > 0: - if best_start == 0 and \ - (best_len == 6 or - best_len == 5 and chunks[5] == 'ffff'): - # We have an embedded IPv4 address - if best_len == 6: - prefix = '::' - else: - prefix = '::ffff:' - hex = prefix + dns.ipv4.inet_ntoa(address[12:]) - else: - hex = ':'.join(chunks[:best_start]) + '::' + \ - ':'.join(chunks[best_start + best_len:]) - else: - hex = ':'.join(chunks) - return hex - -_v4_ending = re.compile(r'(.*):(\d+\.\d+\.\d+\.\d+)$') -_colon_colon_start = re.compile(r'::.*') -_colon_colon_end = re.compile(r'.*::$') - -def inet_aton(text): - """Convert a text format IPv6 address into network format. - - @param text: the textual address - @type text: string - @rtype: string - @raises dns.exception.SyntaxError: the text was not properly formatted - """ - - # - # Our aim here is not something fast; we just want something that works. - # - - if text == '::': - text = '0::' - # - # Get rid of the icky dot-quad syntax if we have it. - # - m = _v4_ending.match(text) - if not m is None: - b = dns.ipv4.inet_aton(m.group(2)) - text = "%s:%02x%02x:%02x%02x" % (m.group(1), ord(b[0]), ord(b[1]), - ord(b[2]), ord(b[3])) - # - # Try to turn '::' into ':'; if no match try to - # turn '::' into ':' - # - m = _colon_colon_start.match(text) - if not m is None: - text = text[1:] - else: - m = _colon_colon_end.match(text) - if not m is None: - text = text[:-1] - # - # Now canonicalize into 8 chunks of 4 hex digits each - # - chunks = text.split(':') - l = len(chunks) - if l > 8: - raise dns.exception.SyntaxError - seen_empty = False - canonical = [] - for c in chunks: - if c == '': - if seen_empty: - raise dns.exception.SyntaxError - seen_empty = True - for i in xrange(0, 8 - l + 1): - canonical.append('0000') - else: - lc = len(c) - if lc > 4: - raise dns.exception.SyntaxError - if lc != 4: - c = ('0' * (4 - lc)) + c - canonical.append(c) - if l < 8 and not seen_empty: - raise dns.exception.SyntaxError - text = ''.join(canonical) - - # - # Finally we can go to binary. - # - try: - return text.decode('hex_codec') - except TypeError: - raise dns.exception.SyntaxError diff --git a/lib/dnspython/dns/message.py b/lib/dnspython/dns/message.py deleted file mode 100644 index cf29133934..0000000000 --- a/lib/dnspython/dns/message.py +++ /dev/null @@ -1,1088 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Messages""" - -import cStringIO -import random -import struct -import sys -import time - -import dns.edns -import dns.exception -import dns.flags -import dns.name -import dns.opcode -import dns.entropy -import dns.rcode -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rrset -import dns.renderer -import dns.tsig -import dns.wiredata - -class ShortHeader(dns.exception.FormError): - """Raised if the DNS packet passed to from_wire() is too short.""" - pass - -class TrailingJunk(dns.exception.FormError): - """Raised if the DNS packet passed to from_wire() has extra junk - at the end of it.""" - pass - -class UnknownHeaderField(dns.exception.DNSException): - """Raised if a header field name is not recognized when converting from - text into a message.""" - pass - -class BadEDNS(dns.exception.FormError): - """Raised if an OPT record occurs somewhere other than the start of - the additional data section.""" - pass - -class BadTSIG(dns.exception.FormError): - """Raised if a TSIG record occurs somewhere other than the end of - the additional data section.""" - pass - -class UnknownTSIGKey(dns.exception.DNSException): - """Raised if we got a TSIG but don't know the key.""" - pass - -class Message(object): - """A DNS message. - - @ivar id: The query id; the default is a randomly chosen id. - @type id: int - @ivar flags: The DNS flags of the message. @see: RFC 1035 for an - explanation of these flags. - @type flags: int - @ivar question: The question section. - @type question: list of dns.rrset.RRset objects - @ivar answer: The answer section. - @type answer: list of dns.rrset.RRset objects - @ivar authority: The authority section. - @type authority: list of dns.rrset.RRset objects - @ivar additional: The additional data section. - @type additional: list of dns.rrset.RRset objects - @ivar edns: The EDNS level to use. The default is -1, no Edns. - @type edns: int - @ivar ednsflags: The EDNS flags - @type ednsflags: long - @ivar payload: The EDNS payload size. The default is 0. - @type payload: int - @ivar options: The EDNS options - @type options: list of dns.edns.Option objects - @ivar request_payload: The associated request's EDNS payload size. - @type request_payload: int - @ivar keyring: The TSIG keyring to use. The default is None. - @type keyring: dict - @ivar keyname: The TSIG keyname to use. The default is None. - @type keyname: dns.name.Name object - @ivar keyalgorithm: The TSIG algorithm to use; defaults to - dns.tsig.default_algorithm. Constants for TSIG algorithms are defined - in dns.tsig, and the currently implemented algorithms are - HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and - HMAC_SHA512. - @type keyalgorithm: string - @ivar request_mac: The TSIG MAC of the request message associated with - this message; used when validating TSIG signatures. @see: RFC 2845 for - more information on TSIG fields. - @type request_mac: string - @ivar fudge: TSIG time fudge; default is 300 seconds. - @type fudge: int - @ivar original_id: TSIG original id; defaults to the message's id - @type original_id: int - @ivar tsig_error: TSIG error code; default is 0. - @type tsig_error: int - @ivar other_data: TSIG other data. - @type other_data: string - @ivar mac: The TSIG MAC for this message. - @type mac: string - @ivar xfr: Is the message being used to contain the results of a DNS - zone transfer? The default is False. - @type xfr: bool - @ivar origin: The origin of the zone in messages which are used for - zone transfers or for DNS dynamic updates. The default is None. - @type origin: dns.name.Name object - @ivar tsig_ctx: The TSIG signature context associated with this - message. The default is None. - @type tsig_ctx: hmac.HMAC object - @ivar had_tsig: Did the message decoded from wire format have a TSIG - signature? - @type had_tsig: bool - @ivar multi: Is this message part of a multi-message sequence? The - default is false. This variable is used when validating TSIG signatures - on messages which are part of a zone transfer. - @type multi: bool - @ivar first: Is this message standalone, or the first of a multi - message sequence? This variable is used when validating TSIG signatures - on messages which are part of a zone transfer. - @type first: bool - @ivar index: An index of rrsets in the message. The index key is - (section, name, rdclass, rdtype, covers, deleting). Indexing can be - disabled by setting the index to None. - @type index: dict - """ - - def __init__(self, id=None): - if id is None: - self.id = dns.entropy.random_16() - else: - self.id = id - self.flags = 0 - self.question = [] - self.answer = [] - self.authority = [] - self.additional = [] - self.edns = -1 - self.ednsflags = 0 - self.payload = 0 - self.options = [] - self.request_payload = 0 - self.keyring = None - self.keyname = None - self.keyalgorithm = dns.tsig.default_algorithm - self.request_mac = '' - self.other_data = '' - self.tsig_error = 0 - self.fudge = 300 - self.original_id = self.id - self.mac = '' - self.xfr = False - self.origin = None - self.tsig_ctx = None - self.had_tsig = False - self.multi = False - self.first = True - self.index = {} - - def __repr__(self): - return '' - - def __str__(self): - return self.to_text() - - def to_text(self, origin=None, relativize=True, **kw): - """Convert the message to text. - - The I{origin}, I{relativize}, and any other keyword - arguments are passed to the rrset to_wire() method. - - @rtype: string - """ - - s = cStringIO.StringIO() - print >> s, 'id %d' % self.id - print >> s, 'opcode %s' % \ - dns.opcode.to_text(dns.opcode.from_flags(self.flags)) - rc = dns.rcode.from_flags(self.flags, self.ednsflags) - print >> s, 'rcode %s' % dns.rcode.to_text(rc) - print >> s, 'flags %s' % dns.flags.to_text(self.flags) - if self.edns >= 0: - print >> s, 'edns %s' % self.edns - if self.ednsflags != 0: - print >> s, 'eflags %s' % \ - dns.flags.edns_to_text(self.ednsflags) - print >> s, 'payload', self.payload - is_update = dns.opcode.is_update(self.flags) - if is_update: - print >> s, ';ZONE' - else: - print >> s, ';QUESTION' - for rrset in self.question: - print >> s, rrset.to_text(origin, relativize, **kw) - if is_update: - print >> s, ';PREREQ' - else: - print >> s, ';ANSWER' - for rrset in self.answer: - print >> s, rrset.to_text(origin, relativize, **kw) - if is_update: - print >> s, ';UPDATE' - else: - print >> s, ';AUTHORITY' - for rrset in self.authority: - print >> s, rrset.to_text(origin, relativize, **kw) - print >> s, ';ADDITIONAL' - for rrset in self.additional: - print >> s, rrset.to_text(origin, relativize, **kw) - # - # We strip off the final \n so the caller can print the result without - # doing weird things to get around eccentricities in Python print - # formatting - # - return s.getvalue()[:-1] - - def __eq__(self, other): - """Two messages are equal if they have the same content in the - header, question, answer, and authority sections. - @rtype: bool""" - if not isinstance(other, Message): - return False - if self.id != other.id: - return False - if self.flags != other.flags: - return False - for n in self.question: - if n not in other.question: - return False - for n in other.question: - if n not in self.question: - return False - for n in self.answer: - if n not in other.answer: - return False - for n in other.answer: - if n not in self.answer: - return False - for n in self.authority: - if n not in other.authority: - return False - for n in other.authority: - if n not in self.authority: - return False - return True - - def __ne__(self, other): - """Are two messages not equal? - @rtype: bool""" - return not self.__eq__(other) - - def is_response(self, other): - """Is other a response to self? - @rtype: bool""" - if other.flags & dns.flags.QR == 0 or \ - self.id != other.id or \ - dns.opcode.from_flags(self.flags) != \ - dns.opcode.from_flags(other.flags): - return False - if dns.rcode.from_flags(other.flags, other.ednsflags) != \ - dns.rcode.NOERROR: - return True - if dns.opcode.is_update(self.flags): - return True - for n in self.question: - if n not in other.question: - return False - for n in other.question: - if n not in self.question: - return False - return True - - def section_number(self, section): - if section is self.question: - return 0 - elif section is self.answer: - return 1 - elif section is self.authority: - return 2 - elif section is self.additional: - return 3 - else: - raise ValueError('unknown section') - - def find_rrset(self, section, name, rdclass, rdtype, - covers=dns.rdatatype.NONE, deleting=None, create=False, - force_unique=False): - """Find the RRset with the given attributes in the specified section. - - @param section: the section of the message to look in, e.g. - self.answer. - @type section: list of dns.rrset.RRset objects - @param name: the name of the RRset - @type name: dns.name.Name object - @param rdclass: the class of the RRset - @type rdclass: int - @param rdtype: the type of the RRset - @type rdtype: int - @param covers: the covers value of the RRset - @type covers: int - @param deleting: the deleting value of the RRset - @type deleting: int - @param create: If True, create the RRset if it is not found. - The created RRset is appended to I{section}. - @type create: bool - @param force_unique: If True and create is also True, create a - new RRset regardless of whether a matching RRset exists already. - @type force_unique: bool - @raises KeyError: the RRset was not found and create was False - @rtype: dns.rrset.RRset object""" - - key = (self.section_number(section), - name, rdclass, rdtype, covers, deleting) - if not force_unique: - if not self.index is None: - rrset = self.index.get(key) - if not rrset is None: - return rrset - else: - for rrset in section: - if rrset.match(name, rdclass, rdtype, covers, deleting): - return rrset - if not create: - raise KeyError - rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) - section.append(rrset) - if not self.index is None: - self.index[key] = rrset - return rrset - - def get_rrset(self, section, name, rdclass, rdtype, - covers=dns.rdatatype.NONE, deleting=None, create=False, - force_unique=False): - """Get the RRset with the given attributes in the specified section. - - If the RRset is not found, None is returned. - - @param section: the section of the message to look in, e.g. - self.answer. - @type section: list of dns.rrset.RRset objects - @param name: the name of the RRset - @type name: dns.name.Name object - @param rdclass: the class of the RRset - @type rdclass: int - @param rdtype: the type of the RRset - @type rdtype: int - @param covers: the covers value of the RRset - @type covers: int - @param deleting: the deleting value of the RRset - @type deleting: int - @param create: If True, create the RRset if it is not found. - The created RRset is appended to I{section}. - @type create: bool - @param force_unique: If True and create is also True, create a - new RRset regardless of whether a matching RRset exists already. - @type force_unique: bool - @rtype: dns.rrset.RRset object or None""" - - try: - rrset = self.find_rrset(section, name, rdclass, rdtype, covers, - deleting, create, force_unique) - except KeyError: - rrset = None - return rrset - - def to_wire(self, origin=None, max_size=0, **kw): - """Return a string containing the message in DNS compressed wire - format. - - Additional keyword arguments are passed to the rrset to_wire() - method. - - @param origin: The origin to be appended to any relative names. - @type origin: dns.name.Name object - @param max_size: The maximum size of the wire format output; default - is 0, which means 'the message's request payload, if nonzero, or - 65536'. - @type max_size: int - @raises dns.exception.TooBig: max_size was exceeded - @rtype: string - """ - - if max_size == 0: - if self.request_payload != 0: - max_size = self.request_payload - else: - max_size = 65535 - if max_size < 512: - max_size = 512 - elif max_size > 65535: - max_size = 65535 - r = dns.renderer.Renderer(self.id, self.flags, max_size, origin) - for rrset in self.question: - r.add_question(rrset.name, rrset.rdtype, rrset.rdclass) - for rrset in self.answer: - r.add_rrset(dns.renderer.ANSWER, rrset, **kw) - for rrset in self.authority: - r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw) - if self.edns >= 0: - r.add_edns(self.edns, self.ednsflags, self.payload, self.options) - for rrset in self.additional: - r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw) - r.write_header() - if not self.keyname is None: - r.add_tsig(self.keyname, self.keyring[self.keyname], - self.fudge, self.original_id, self.tsig_error, - self.other_data, self.request_mac, - self.keyalgorithm) - self.mac = r.mac - return r.get_wire() - - def use_tsig(self, keyring, keyname=None, fudge=300, - original_id=None, tsig_error=0, other_data='', - algorithm=dns.tsig.default_algorithm): - """When sending, a TSIG signature using the specified keyring - and keyname should be added. - - @param keyring: The TSIG keyring to use; defaults to None. - @type keyring: dict - @param keyname: The name of the TSIG key to use; defaults to None. - The key must be defined in the keyring. If a keyring is specified - but a keyname is not, then the key used will be the first key in the - keyring. Note that the order of keys in a dictionary is not defined, - so applications should supply a keyname when a keyring is used, unless - they know the keyring contains only one key. - @type keyname: dns.name.Name or string - @param fudge: TSIG time fudge; default is 300 seconds. - @type fudge: int - @param original_id: TSIG original id; defaults to the message's id - @type original_id: int - @param tsig_error: TSIG error code; default is 0. - @type tsig_error: int - @param other_data: TSIG other data. - @type other_data: string - @param algorithm: The TSIG algorithm to use; defaults to - dns.tsig.default_algorithm - """ - - self.keyring = keyring - if keyname is None: - self.keyname = self.keyring.keys()[0] - else: - if isinstance(keyname, (str, unicode)): - keyname = dns.name.from_text(keyname) - self.keyname = keyname - self.keyalgorithm = algorithm - self.fudge = fudge - if original_id is None: - self.original_id = self.id - else: - self.original_id = original_id - self.tsig_error = tsig_error - self.other_data = other_data - - def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None): - """Configure EDNS behavior. - @param edns: The EDNS level to use. Specifying None, False, or -1 - means 'do not use EDNS', and in this case the other parameters are - ignored. Specifying True is equivalent to specifying 0, i.e. 'use - EDNS0'. - @type edns: int or bool or None - @param ednsflags: EDNS flag values. - @type ednsflags: int - @param payload: The EDNS sender's payload field, which is the maximum - size of UDP datagram the sender can handle. - @type payload: int - @param request_payload: The EDNS payload size to use when sending - this message. If not specified, defaults to the value of payload. - @type request_payload: int or None - @param options: The EDNS options - @type options: None or list of dns.edns.Option objects - @see: RFC 2671 - """ - if edns is None or edns is False: - edns = -1 - if edns is True: - edns = 0 - if request_payload is None: - request_payload = payload - if edns < 0: - ednsflags = 0 - payload = 0 - request_payload = 0 - options = [] - else: - # make sure the EDNS version in ednsflags agrees with edns - ednsflags &= 0xFF00FFFFL - ednsflags |= (edns << 16) - if options is None: - options = [] - self.edns = edns - self.ednsflags = ednsflags - self.payload = payload - self.options = options - self.request_payload = request_payload - - def want_dnssec(self, wanted=True): - """Enable or disable 'DNSSEC desired' flag in requests. - @param wanted: Is DNSSEC desired? If True, EDNS is enabled if - required, and then the DO bit is set. If False, the DO bit is - cleared if EDNS is enabled. - @type wanted: bool - """ - if wanted: - if self.edns < 0: - self.use_edns() - self.ednsflags |= dns.flags.DO - elif self.edns >= 0: - self.ednsflags &= ~dns.flags.DO - - def rcode(self): - """Return the rcode. - @rtype: int - """ - return dns.rcode.from_flags(self.flags, self.ednsflags) - - def set_rcode(self, rcode): - """Set the rcode. - @param rcode: the rcode - @type rcode: int - """ - (value, evalue) = dns.rcode.to_flags(rcode) - self.flags &= 0xFFF0 - self.flags |= value - self.ednsflags &= 0x00FFFFFFL - self.ednsflags |= evalue - if self.ednsflags != 0 and self.edns < 0: - self.edns = 0 - - def opcode(self): - """Return the opcode. - @rtype: int - """ - return dns.opcode.from_flags(self.flags) - - def set_opcode(self, opcode): - """Set the opcode. - @param opcode: the opcode - @type opcode: int - """ - self.flags &= 0x87FF - self.flags |= dns.opcode.to_flags(opcode) - -class _WireReader(object): - """Wire format reader. - - @ivar wire: the wire-format message. - @type wire: string - @ivar message: The message object being built - @type message: dns.message.Message object - @ivar current: When building a message object from wire format, this - variable contains the offset from the beginning of wire of the next octet - to be read. - @type current: int - @ivar updating: Is the message a dynamic update? - @type updating: bool - @ivar one_rr_per_rrset: Put each RR into its own RRset? - @type one_rr_per_rrset: bool - @ivar zone_rdclass: The class of the zone in messages which are - DNS dynamic updates. - @type zone_rdclass: int - """ - - def __init__(self, wire, message, question_only=False, - one_rr_per_rrset=False): - self.wire = dns.wiredata.maybe_wrap(wire) - self.message = message - self.current = 0 - self.updating = False - self.zone_rdclass = dns.rdataclass.IN - self.question_only = question_only - self.one_rr_per_rrset = one_rr_per_rrset - - def _get_question(self, qcount): - """Read the next I{qcount} records from the wire data and add them to - the question section. - @param qcount: the number of questions in the message - @type qcount: int""" - - if self.updating and qcount > 1: - raise dns.exception.FormError - - for i in xrange(0, qcount): - (qname, used) = dns.name.from_wire(self.wire, self.current) - if not self.message.origin is None: - qname = qname.relativize(self.message.origin) - self.current = self.current + used - (rdtype, rdclass) = \ - struct.unpack('!HH', - self.wire[self.current:self.current + 4]) - self.current = self.current + 4 - self.message.find_rrset(self.message.question, qname, - rdclass, rdtype, create=True, - force_unique=True) - if self.updating: - self.zone_rdclass = rdclass - - def _get_section(self, section, count): - """Read the next I{count} records from the wire data and add them to - the specified section. - @param section: the section of the message to which to add records - @type section: list of dns.rrset.RRset objects - @param count: the number of records to read - @type count: int""" - - if self.updating or self.one_rr_per_rrset: - force_unique = True - else: - force_unique = False - seen_opt = False - for i in xrange(0, count): - rr_start = self.current - (name, used) = dns.name.from_wire(self.wire, self.current) - absolute_name = name - if not self.message.origin is None: - name = name.relativize(self.message.origin) - self.current = self.current + used - (rdtype, rdclass, ttl, rdlen) = \ - struct.unpack('!HHIH', - self.wire[self.current:self.current + 10]) - self.current = self.current + 10 - if rdtype == dns.rdatatype.OPT: - if not section is self.message.additional or seen_opt: - raise BadEDNS - self.message.payload = rdclass - self.message.ednsflags = ttl - self.message.edns = (ttl & 0xff0000) >> 16 - self.message.options = [] - current = self.current - optslen = rdlen - while optslen > 0: - (otype, olen) = \ - struct.unpack('!HH', - self.wire[current:current + 4]) - current = current + 4 - opt = dns.edns.option_from_wire(otype, self.wire, current, olen) - self.message.options.append(opt) - current = current + olen - optslen = optslen - 4 - olen - seen_opt = True - elif rdtype == dns.rdatatype.TSIG: - if not (section is self.message.additional and - i == (count - 1)): - raise BadTSIG - if self.message.keyring is None: - raise UnknownTSIGKey('got signed message without keyring') - secret = self.message.keyring.get(absolute_name) - if secret is None: - raise UnknownTSIGKey("key '%s' unknown" % name) - self.message.tsig_ctx = \ - dns.tsig.validate(self.wire, - absolute_name, - secret, - int(time.time()), - self.message.request_mac, - rr_start, - self.current, - rdlen, - self.message.tsig_ctx, - self.message.multi, - self.message.first) - self.message.had_tsig = True - else: - if ttl < 0: - ttl = 0 - if self.updating and \ - (rdclass == dns.rdataclass.ANY or - rdclass == dns.rdataclass.NONE): - deleting = rdclass - rdclass = self.zone_rdclass - else: - deleting = None - if deleting == dns.rdataclass.ANY or \ - (deleting == dns.rdataclass.NONE and \ - section is self.message.answer): - covers = dns.rdatatype.NONE - rd = None - else: - rd = dns.rdata.from_wire(rdclass, rdtype, self.wire, - self.current, rdlen, - self.message.origin) - covers = rd.covers() - if self.message.xfr and rdtype == dns.rdatatype.SOA: - force_unique = True - rrset = self.message.find_rrset(section, name, - rdclass, rdtype, covers, - deleting, True, force_unique) - if not rd is None: - rrset.add(rd, ttl) - self.current = self.current + rdlen - - def read(self): - """Read a wire format DNS message and build a dns.message.Message - object.""" - - l = len(self.wire) - if l < 12: - raise ShortHeader - (self.message.id, self.message.flags, qcount, ancount, - aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12]) - self.current = 12 - if dns.opcode.is_update(self.message.flags): - self.updating = True - self._get_question(qcount) - if self.question_only: - return - self._get_section(self.message.answer, ancount) - self._get_section(self.message.authority, aucount) - self._get_section(self.message.additional, adcount) - if self.current != l: - raise TrailingJunk - if self.message.multi and self.message.tsig_ctx and \ - not self.message.had_tsig: - self.message.tsig_ctx.update(self.wire) - - -def from_wire(wire, keyring=None, request_mac='', xfr=False, origin=None, - tsig_ctx = None, multi = False, first = True, - question_only = False, one_rr_per_rrset = False): - """Convert a DNS wire format message into a message - object. - - @param keyring: The keyring to use if the message is signed. - @type keyring: dict - @param request_mac: If the message is a response to a TSIG-signed request, - I{request_mac} should be set to the MAC of that request. - @type request_mac: string - @param xfr: Is this message part of a zone transfer? - @type xfr: bool - @param origin: If the message is part of a zone transfer, I{origin} - should be the origin name of the zone. - @type origin: dns.name.Name object - @param tsig_ctx: The ongoing TSIG context, used when validating zone - transfers. - @type tsig_ctx: hmac.HMAC object - @param multi: Is this message part of a multiple message sequence? - @type multi: bool - @param first: Is this message standalone, or the first of a multi - message sequence? - @type first: bool - @param question_only: Read only up to the end of the question section? - @type question_only: bool - @param one_rr_per_rrset: Put each RR into its own RRset - @type one_rr_per_rrset: bool - @raises ShortHeader: The message is less than 12 octets long. - @raises TrailingJunk: There were octets in the message past the end - of the proper DNS message. - @raises BadEDNS: An OPT record was in the wrong section, or occurred more - than once. - @raises BadTSIG: A TSIG record was not the last record of the additional - data section. - @rtype: dns.message.Message object""" - - m = Message(id=0) - m.keyring = keyring - m.request_mac = request_mac - m.xfr = xfr - m.origin = origin - m.tsig_ctx = tsig_ctx - m.multi = multi - m.first = first - - reader = _WireReader(wire, m, question_only, one_rr_per_rrset) - reader.read() - - return m - - -class _TextReader(object): - """Text format reader. - - @ivar tok: the tokenizer - @type tok: dns.tokenizer.Tokenizer object - @ivar message: The message object being built - @type message: dns.message.Message object - @ivar updating: Is the message a dynamic update? - @type updating: bool - @ivar zone_rdclass: The class of the zone in messages which are - DNS dynamic updates. - @type zone_rdclass: int - @ivar last_name: The most recently read name when building a message object - from text format. - @type last_name: dns.name.Name object - """ - - def __init__(self, text, message): - self.message = message - self.tok = dns.tokenizer.Tokenizer(text) - self.last_name = None - self.zone_rdclass = dns.rdataclass.IN - self.updating = False - - def _header_line(self, section): - """Process one line from the text format header section.""" - - token = self.tok.get() - what = token.value - if what == 'id': - self.message.id = self.tok.get_int() - elif what == 'flags': - while True: - token = self.tok.get() - if not token.is_identifier(): - self.tok.unget(token) - break - self.message.flags = self.message.flags | \ - dns.flags.from_text(token.value) - if dns.opcode.is_update(self.message.flags): - self.updating = True - elif what == 'edns': - self.message.edns = self.tok.get_int() - self.message.ednsflags = self.message.ednsflags | \ - (self.message.edns << 16) - elif what == 'eflags': - if self.message.edns < 0: - self.message.edns = 0 - while True: - token = self.tok.get() - if not token.is_identifier(): - self.tok.unget(token) - break - self.message.ednsflags = self.message.ednsflags | \ - dns.flags.edns_from_text(token.value) - elif what == 'payload': - self.message.payload = self.tok.get_int() - if self.message.edns < 0: - self.message.edns = 0 - elif what == 'opcode': - text = self.tok.get_string() - self.message.flags = self.message.flags | \ - dns.opcode.to_flags(dns.opcode.from_text(text)) - elif what == 'rcode': - text = self.tok.get_string() - self.message.set_rcode(dns.rcode.from_text(text)) - else: - raise UnknownHeaderField - self.tok.get_eol() - - def _question_line(self, section): - """Process one line from the text format question section.""" - - token = self.tok.get(want_leading = True) - if not token.is_whitespace(): - self.last_name = dns.name.from_text(token.value, None) - name = self.last_name - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except: - rdclass = dns.rdataclass.IN - # Type - rdtype = dns.rdatatype.from_text(token.value) - self.message.find_rrset(self.message.question, name, - rdclass, rdtype, create=True, - force_unique=True) - if self.updating: - self.zone_rdclass = rdclass - self.tok.get_eol() - - def _rr_line(self, section): - """Process one line from the text format answer, authority, or - additional data sections. - """ - - deleting = None - # Name - token = self.tok.get(want_leading = True) - if not token.is_whitespace(): - self.last_name = dns.name.from_text(token.value, None) - name = self.last_name - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # TTL - try: - ttl = int(token.value, 0) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except: - ttl = 0 - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE: - deleting = rdclass - rdclass = self.zone_rdclass - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except: - rdclass = dns.rdataclass.IN - # Type - rdtype = dns.rdatatype.from_text(token.value) - token = self.tok.get() - if not token.is_eol_or_eof(): - self.tok.unget(token) - rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None) - covers = rd.covers() - else: - rd = None - covers = dns.rdatatype.NONE - rrset = self.message.find_rrset(section, name, - rdclass, rdtype, covers, - deleting, True, self.updating) - if not rd is None: - rrset.add(rd, ttl) - - def read(self): - """Read a text format DNS message and build a dns.message.Message - object.""" - - line_method = self._header_line - section = None - while 1: - token = self.tok.get(True, True) - if token.is_eol_or_eof(): - break - if token.is_comment(): - u = token.value.upper() - if u == 'HEADER': - line_method = self._header_line - elif u == 'QUESTION' or u == 'ZONE': - line_method = self._question_line - section = self.message.question - elif u == 'ANSWER' or u == 'PREREQ': - line_method = self._rr_line - section = self.message.answer - elif u == 'AUTHORITY' or u == 'UPDATE': - line_method = self._rr_line - section = self.message.authority - elif u == 'ADDITIONAL': - line_method = self._rr_line - section = self.message.additional - self.tok.get_eol() - continue - self.tok.unget(token) - line_method(section) - - -def from_text(text): - """Convert the text format message into a message object. - - @param text: The text format message. - @type text: string - @raises UnknownHeaderField: - @raises dns.exception.SyntaxError: - @rtype: dns.message.Message object""" - - # 'text' can also be a file, but we don't publish that fact - # since it's an implementation detail. The official file - # interface is from_file(). - - m = Message() - - reader = _TextReader(text, m) - reader.read() - - return m - -def from_file(f): - """Read the next text format message from the specified file. - - @param f: file or string. If I{f} is a string, it is treated - as the name of a file to open. - @raises UnknownHeaderField: - @raises dns.exception.SyntaxError: - @rtype: dns.message.Message object""" - - if sys.hexversion >= 0x02030000: - # allow Unicode filenames; turn on universal newline support - str_type = basestring - opts = 'rU' - else: - str_type = str - opts = 'r' - if isinstance(f, str_type): - f = file(f, opts) - want_close = True - else: - want_close = False - - try: - m = from_text(f) - finally: - if want_close: - f.close() - return m - -def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None, - want_dnssec=False): - """Make a query message. - - The query name, type, and class may all be specified either - as objects of the appropriate type, or as strings. - - The query will have a randomly choosen query id, and its DNS flags - will be set to dns.flags.RD. - - @param qname: The query name. - @type qname: dns.name.Name object or string - @param rdtype: The desired rdata type. - @type rdtype: int - @param rdclass: The desired rdata class; the default is class IN. - @type rdclass: int - @param use_edns: The EDNS level to use; the default is None (no EDNS). - See the description of dns.message.Message.use_edns() for the possible - values for use_edns and their meanings. - @type use_edns: int or bool or None - @param want_dnssec: Should the query indicate that DNSSEC is desired? - @type want_dnssec: bool - @rtype: dns.message.Message object""" - - if isinstance(qname, (str, unicode)): - qname = dns.name.from_text(qname) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(rdclass, (str, unicode)): - rdclass = dns.rdataclass.from_text(rdclass) - m = Message() - m.flags |= dns.flags.RD - m.find_rrset(m.question, qname, rdclass, rdtype, create=True, - force_unique=True) - m.use_edns(use_edns) - m.want_dnssec(want_dnssec) - return m - -def make_response(query, recursion_available=False, our_payload=8192): - """Make a message which is a response for the specified query. - The message returned is really a response skeleton; it has all - of the infrastructure required of a response, but none of the - content. - - The response's question section is a shallow copy of the query's - question section, so the query's question RRsets should not be - changed. - - @param query: the query to respond to - @type query: dns.message.Message object - @param recursion_available: should RA be set in the response? - @type recursion_available: bool - @param our_payload: payload size to advertise in EDNS responses; default - is 8192. - @type our_payload: int - @rtype: dns.message.Message object""" - - if query.flags & dns.flags.QR: - raise dns.exception.FormError('specified query message is not a query') - response = dns.message.Message(query.id) - response.flags = dns.flags.QR | (query.flags & dns.flags.RD) - if recursion_available: - response.flags |= dns.flags.RA - response.set_opcode(query.opcode()) - response.question = list(query.question) - if query.edns >= 0: - response.use_edns(0, 0, our_payload, query.payload) - if not query.keyname is None: - response.keyname = query.keyname - response.keyring = query.keyring - response.request_mac = query.mac - return response diff --git a/lib/dnspython/dns/name.py b/lib/dnspython/dns/name.py deleted file mode 100644 index ed3ffeec57..0000000000 --- a/lib/dnspython/dns/name.py +++ /dev/null @@ -1,702 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Names. - -@var root: The DNS root name. -@type root: dns.name.Name object -@var empty: The empty DNS name. -@type empty: dns.name.Name object -""" - -import cStringIO -import struct -import sys - -if sys.hexversion >= 0x02030000: - import encodings.idna - -import dns.exception -import dns.wiredata - -NAMERELN_NONE = 0 -NAMERELN_SUPERDOMAIN = 1 -NAMERELN_SUBDOMAIN = 2 -NAMERELN_EQUAL = 3 -NAMERELN_COMMONANCESTOR = 4 - -class EmptyLabel(dns.exception.SyntaxError): - """Raised if a label is empty.""" - pass - -class BadEscape(dns.exception.SyntaxError): - """Raised if an escaped code in a text format name is invalid.""" - pass - -class BadPointer(dns.exception.FormError): - """Raised if a compression pointer points forward instead of backward.""" - pass - -class BadLabelType(dns.exception.FormError): - """Raised if the label type of a wire format name is unknown.""" - pass - -class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): - """Raised if an attempt is made to convert a non-absolute name to - wire when there is also a non-absolute (or missing) origin.""" - pass - -class NameTooLong(dns.exception.FormError): - """Raised if a name is > 255 octets long.""" - pass - -class LabelTooLong(dns.exception.SyntaxError): - """Raised if a label is > 63 octets long.""" - pass - -class AbsoluteConcatenation(dns.exception.DNSException): - """Raised if an attempt is made to append anything other than the - empty name to an absolute name.""" - pass - -class NoParent(dns.exception.DNSException): - """Raised if an attempt is made to get the parent of the root name - or the empty name.""" - pass - -_escaped = { - '"' : True, - '(' : True, - ')' : True, - '.' : True, - ';' : True, - '\\' : True, - '@' : True, - '$' : True - } - -def _escapify(label): - """Escape the characters in label which need it. - @returns: the escaped string - @rtype: string""" - text = '' - for c in label: - if c in _escaped: - text += '\\' + c - elif ord(c) > 0x20 and ord(c) < 0x7F: - text += c - else: - text += '\\%03d' % ord(c) - return text - -def _validate_labels(labels): - """Check for empty labels in the middle of a label sequence, - labels that are too long, and for too many labels. - @raises NameTooLong: the name as a whole is too long - @raises LabelTooLong: an individual label is too long - @raises EmptyLabel: a label is empty (i.e. the root label) and appears - in a position other than the end of the label sequence""" - - l = len(labels) - total = 0 - i = -1 - j = 0 - for label in labels: - ll = len(label) - total += ll + 1 - if ll > 63: - raise LabelTooLong - if i < 0 and label == '': - i = j - j += 1 - if total > 255: - raise NameTooLong - if i >= 0 and i != l - 1: - raise EmptyLabel - -class Name(object): - """A DNS name. - - The dns.name.Name class represents a DNS name as a tuple of labels. - Instances of the class are immutable. - - @ivar labels: The tuple of labels in the name. Each label is a string of - up to 63 octets.""" - - __slots__ = ['labels'] - - def __init__(self, labels): - """Initialize a domain name from a list of labels. - @param labels: the labels - @type labels: any iterable whose values are strings - """ - - super(Name, self).__setattr__('labels', tuple(labels)) - _validate_labels(self.labels) - - def __setattr__(self, name, value): - raise TypeError("object doesn't support attribute assignment") - - def is_absolute(self): - """Is the most significant label of this name the root label? - @rtype: bool - """ - - return len(self.labels) > 0 and self.labels[-1] == '' - - def is_wild(self): - """Is this name wild? (I.e. Is the least significant label '*'?) - @rtype: bool - """ - - return len(self.labels) > 0 and self.labels[0] == '*' - - def __hash__(self): - """Return a case-insensitive hash of the name. - @rtype: int - """ - - h = 0L - for label in self.labels: - for c in label: - h += ( h << 3 ) + ord(c.lower()) - return int(h % sys.maxint) - - def fullcompare(self, other): - """Compare two names, returning a 3-tuple (relation, order, nlabels). - - I{relation} describes the relation ship beween the names, - and is one of: dns.name.NAMERELN_NONE, - dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN, - dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR - - I{order} is < 0 if self < other, > 0 if self > other, and == - 0 if self == other. A relative name is always less than an - absolute name. If both names have the same relativity, then - the DNSSEC order relation is used to order them. - - I{nlabels} is the number of significant labels that the two names - have in common. - """ - - sabs = self.is_absolute() - oabs = other.is_absolute() - if sabs != oabs: - if sabs: - return (NAMERELN_NONE, 1, 0) - else: - return (NAMERELN_NONE, -1, 0) - l1 = len(self.labels) - l2 = len(other.labels) - ldiff = l1 - l2 - if ldiff < 0: - l = l1 - else: - l = l2 - - order = 0 - nlabels = 0 - namereln = NAMERELN_NONE - while l > 0: - l -= 1 - l1 -= 1 - l2 -= 1 - label1 = self.labels[l1].lower() - label2 = other.labels[l2].lower() - if label1 < label2: - order = -1 - if nlabels > 0: - namereln = NAMERELN_COMMONANCESTOR - return (namereln, order, nlabels) - elif label1 > label2: - order = 1 - if nlabels > 0: - namereln = NAMERELN_COMMONANCESTOR - return (namereln, order, nlabels) - nlabels += 1 - order = ldiff - if ldiff < 0: - namereln = NAMERELN_SUPERDOMAIN - elif ldiff > 0: - namereln = NAMERELN_SUBDOMAIN - else: - namereln = NAMERELN_EQUAL - return (namereln, order, nlabels) - - def is_subdomain(self, other): - """Is self a subdomain of other? - - The notion of subdomain includes equality. - @rtype: bool - """ - - (nr, o, nl) = self.fullcompare(other) - if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL: - return True - return False - - def is_superdomain(self, other): - """Is self a superdomain of other? - - The notion of subdomain includes equality. - @rtype: bool - """ - - (nr, o, nl) = self.fullcompare(other) - if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL: - return True - return False - - def canonicalize(self): - """Return a name which is equal to the current name, but is in - DNSSEC canonical form. - @rtype: dns.name.Name object - """ - - return Name([x.lower() for x in self.labels]) - - def __eq__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] != 0 - else: - return True - - def __lt__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] < 0 - else: - return NotImplemented - - def __le__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] <= 0 - else: - return NotImplemented - - def __ge__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] >= 0 - else: - return NotImplemented - - def __gt__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] > 0 - else: - return NotImplemented - - def __repr__(self): - return '' - - def __str__(self): - return self.to_text(False) - - def to_text(self, omit_final_dot = False): - """Convert name to text format. - @param omit_final_dot: If True, don't emit the final dot (denoting the - root label) for absolute names. The default is False. - @rtype: string - """ - - if len(self.labels) == 0: - return '@' - if len(self.labels) == 1 and self.labels[0] == '': - return '.' - if omit_final_dot and self.is_absolute(): - l = self.labels[:-1] - else: - l = self.labels - s = '.'.join(map(_escapify, l)) - return s - - def to_unicode(self, omit_final_dot = False): - """Convert name to Unicode text format. - - IDN ACE lables are converted to Unicode. - - @param omit_final_dot: If True, don't emit the final dot (denoting the - root label) for absolute names. The default is False. - @rtype: string - """ - - if len(self.labels) == 0: - return u'@' - if len(self.labels) == 1 and self.labels[0] == '': - return u'.' - if omit_final_dot and self.is_absolute(): - l = self.labels[:-1] - else: - l = self.labels - s = u'.'.join([encodings.idna.ToUnicode(_escapify(x)) for x in l]) - return s - - def to_digestable(self, origin=None): - """Convert name to a format suitable for digesting in hashes. - - The name is canonicalized and converted to uncompressed wire format. - - @param origin: If the name is relative and origin is not None, then - origin will be appended to it. - @type origin: dns.name.Name object - @raises NeedAbsoluteNameOrOrigin: All names in wire format are - absolute. If self is a relative name, then an origin must be supplied; - if it is missing, then this exception is raised - @rtype: string - """ - - if not self.is_absolute(): - if origin is None or not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - labels = list(self.labels) - labels.extend(list(origin.labels)) - else: - labels = self.labels - dlabels = ["%s%s" % (chr(len(x)), x.lower()) for x in labels] - return ''.join(dlabels) - - def to_wire(self, file = None, compress = None, origin = None): - """Convert name to wire format, possibly compressing it. - - @param file: the file where the name is emitted (typically - a cStringIO file). If None, a string containing the wire name - will be returned. - @type file: file or None - @param compress: The compression table. If None (the default) names - will not be compressed. - @type compress: dict - @param origin: If the name is relative and origin is not None, then - origin will be appended to it. - @type origin: dns.name.Name object - @raises NeedAbsoluteNameOrOrigin: All names in wire format are - absolute. If self is a relative name, then an origin must be supplied; - if it is missing, then this exception is raised - """ - - if file is None: - file = cStringIO.StringIO() - want_return = True - else: - want_return = False - - if not self.is_absolute(): - if origin is None or not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - labels = list(self.labels) - labels.extend(list(origin.labels)) - else: - labels = self.labels - i = 0 - for label in labels: - n = Name(labels[i:]) - i += 1 - if not compress is None: - pos = compress.get(n) - else: - pos = None - if not pos is None: - value = 0xc000 + pos - s = struct.pack('!H', value) - file.write(s) - break - else: - if not compress is None and len(n) > 1: - pos = file.tell() - if pos < 0xc000: - compress[n] = pos - l = len(label) - file.write(chr(l)) - if l > 0: - file.write(label) - if want_return: - return file.getvalue() - - def __len__(self): - """The length of the name (in labels). - @rtype: int - """ - - return len(self.labels) - - def __getitem__(self, index): - return self.labels[index] - - def __getslice__(self, start, stop): - return self.labels[start:stop] - - def __add__(self, other): - return self.concatenate(other) - - def __sub__(self, other): - return self.relativize(other) - - def split(self, depth): - """Split a name into a prefix and suffix at depth. - - @param depth: the number of labels in the suffix - @type depth: int - @raises ValueError: the depth was not >= 0 and <= the length of the - name. - @returns: the tuple (prefix, suffix) - @rtype: tuple - """ - - l = len(self.labels) - if depth == 0: - return (self, dns.name.empty) - elif depth == l: - return (dns.name.empty, self) - elif depth < 0 or depth > l: - raise ValueError('depth must be >= 0 and <= the length of the name') - return (Name(self[: -depth]), Name(self[-depth :])) - - def concatenate(self, other): - """Return a new name which is the concatenation of self and other. - @rtype: dns.name.Name object - @raises AbsoluteConcatenation: self is absolute and other is - not the empty name - """ - - if self.is_absolute() and len(other) > 0: - raise AbsoluteConcatenation - labels = list(self.labels) - labels.extend(list(other.labels)) - return Name(labels) - - def relativize(self, origin): - """If self is a subdomain of origin, return a new name which is self - relative to origin. Otherwise return self. - @rtype: dns.name.Name object - """ - - if not origin is None and self.is_subdomain(origin): - return Name(self[: -len(origin)]) - else: - return self - - def derelativize(self, origin): - """If self is a relative name, return a new name which is the - concatenation of self and origin. Otherwise return self. - @rtype: dns.name.Name object - """ - - if not self.is_absolute(): - return self.concatenate(origin) - else: - return self - - def choose_relativity(self, origin=None, relativize=True): - """Return a name with the relativity desired by the caller. If - origin is None, then self is returned. Otherwise, if - relativize is true the name is relativized, and if relativize is - false the name is derelativized. - @rtype: dns.name.Name object - """ - - if origin: - if relativize: - return self.relativize(origin) - else: - return self.derelativize(origin) - else: - return self - - def parent(self): - """Return the parent of the name. - @rtype: dns.name.Name object - @raises NoParent: the name is either the root name or the empty name, - and thus has no parent. - """ - if self == root or self == empty: - raise NoParent - return Name(self.labels[1:]) - -root = Name(['']) -empty = Name([]) - -def from_unicode(text, origin = root): - """Convert unicode text into a Name object. - - Lables are encoded in IDN ACE form. - - @rtype: dns.name.Name object - """ - - if not isinstance(text, unicode): - raise ValueError("input to from_unicode() must be a unicode string") - if not (origin is None or isinstance(origin, Name)): - raise ValueError("origin must be a Name or None") - labels = [] - label = u'' - escaping = False - edigits = 0 - total = 0 - if text == u'@': - text = u'' - if text: - if text == u'.': - return Name(['']) # no Unicode "u" on this constant! - for c in text: - if escaping: - if edigits == 0: - if c.isdigit(): - total = int(c) - edigits += 1 - else: - label += c - escaping = False - else: - if not c.isdigit(): - raise BadEscape - total *= 10 - total += int(c) - edigits += 1 - if edigits == 3: - escaping = False - label += chr(total) - elif c == u'.' or c == u'\u3002' or \ - c == u'\uff0e' or c == u'\uff61': - if len(label) == 0: - raise EmptyLabel - labels.append(encodings.idna.ToASCII(label)) - label = u'' - elif c == u'\\': - escaping = True - edigits = 0 - total = 0 - else: - label += c - if escaping: - raise BadEscape - if len(label) > 0: - labels.append(encodings.idna.ToASCII(label)) - else: - labels.append('') - if (len(labels) == 0 or labels[-1] != '') and not origin is None: - labels.extend(list(origin.labels)) - return Name(labels) - -def from_text(text, origin = root): - """Convert text into a Name object. - @rtype: dns.name.Name object - """ - - if not isinstance(text, str): - if isinstance(text, unicode) and sys.hexversion >= 0x02030000: - return from_unicode(text, origin) - else: - raise ValueError("input to from_text() must be a string") - if not (origin is None or isinstance(origin, Name)): - raise ValueError("origin must be a Name or None") - labels = [] - label = '' - escaping = False - edigits = 0 - total = 0 - if text == '@': - text = '' - if text: - if text == '.': - return Name(['']) - for c in text: - if escaping: - if edigits == 0: - if c.isdigit(): - total = int(c) - edigits += 1 - else: - label += c - escaping = False - else: - if not c.isdigit(): - raise BadEscape - total *= 10 - total += int(c) - edigits += 1 - if edigits == 3: - escaping = False - label += chr(total) - elif c == '.': - if len(label) == 0: - raise EmptyLabel - labels.append(label) - label = '' - elif c == '\\': - escaping = True - edigits = 0 - total = 0 - else: - label += c - if escaping: - raise BadEscape - if len(label) > 0: - labels.append(label) - else: - labels.append('') - if (len(labels) == 0 or labels[-1] != '') and not origin is None: - labels.extend(list(origin.labels)) - return Name(labels) - -def from_wire(message, current): - """Convert possibly compressed wire format into a Name. - @param message: the entire DNS message - @type message: string - @param current: the offset of the beginning of the name from the start - of the message - @type current: int - @raises dns.name.BadPointer: a compression pointer did not point backwards - in the message - @raises dns.name.BadLabelType: an invalid label type was encountered. - @returns: a tuple consisting of the name that was read and the number - of bytes of the wire format message which were consumed reading it - @rtype: (dns.name.Name object, int) tuple - """ - - if not isinstance(message, str): - raise ValueError("input to from_wire() must be a byte string") - message = dns.wiredata.maybe_wrap(message) - labels = [] - biggest_pointer = current - hops = 0 - count = ord(message[current]) - current += 1 - cused = 1 - while count != 0: - if count < 64: - labels.append(message[current : current + count].unwrap()) - current += count - if hops == 0: - cused += count - elif count >= 192: - current = (count & 0x3f) * 256 + ord(message[current]) - if hops == 0: - cused += 1 - if current >= biggest_pointer: - raise BadPointer - biggest_pointer = current - hops += 1 - else: - raise BadLabelType - count = ord(message[current]) - current += 1 - if hops == 0: - cused += 1 - labels.append('') - return (Name(labels), cused) diff --git a/lib/dnspython/dns/namedict.py b/lib/dnspython/dns/namedict.py deleted file mode 100644 index 9f5a0efb7c..0000000000 --- a/lib/dnspython/dns/namedict.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS name dictionary""" - -import dns.name - -class NameDict(dict): - - """A dictionary whose keys are dns.name.Name objects. - @ivar max_depth: the maximum depth of the keys that have ever been - added to the dictionary. - @type max_depth: int - """ - - def __init__(self, *args, **kwargs): - super(NameDict, self).__init__(*args, **kwargs) - self.max_depth = 0 - - def __setitem__(self, key, value): - if not isinstance(key, dns.name.Name): - raise ValueError('NameDict key must be a name') - depth = len(key) - if depth > self.max_depth: - self.max_depth = depth - super(NameDict, self).__setitem__(key, value) - - def get_deepest_match(self, name): - """Find the deepest match to I{name} in the dictionary. - - The deepest match is the longest name in the dictionary which is - a superdomain of I{name}. - - @param name: the name - @type name: dns.name.Name object - @rtype: (key, value) tuple - """ - - depth = len(name) - if depth > self.max_depth: - depth = self.max_depth - for i in xrange(-depth, 0): - n = dns.name.Name(name[i:]) - if self.has_key(n): - return (n, self[n]) - v = self[dns.name.empty] - return (dns.name.empty, v) diff --git a/lib/dnspython/dns/node.py b/lib/dnspython/dns/node.py deleted file mode 100644 index 7625c66d3a..0000000000 --- a/lib/dnspython/dns/node.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS nodes. A node is a set of rdatasets.""" - -import StringIO - -import dns.rdataset -import dns.rdatatype -import dns.renderer - -class Node(object): - """A DNS node. - - A node is a set of rdatasets - - @ivar rdatasets: the node's rdatasets - @type rdatasets: list of dns.rdataset.Rdataset objects""" - - __slots__ = ['rdatasets'] - - def __init__(self): - """Initialize a DNS node. - """ - - self.rdatasets = []; - - def to_text(self, name, **kw): - """Convert a node to text format. - - Each rdataset at the node is printed. Any keyword arguments - to this method are passed on to the rdataset's to_text() method. - @param name: the owner name of the rdatasets - @type name: dns.name.Name object - @rtype: string - """ - - s = StringIO.StringIO() - for rds in self.rdatasets: - print >> s, rds.to_text(name, **kw) - return s.getvalue()[:-1] - - def __repr__(self): - return '' - - def __eq__(self, other): - """Two nodes are equal if they have the same rdatasets. - - @rtype: bool - """ - # - # This is inefficient. Good thing we don't need to do it much. - # - for rd in self.rdatasets: - if rd not in other.rdatasets: - return False - for rd in other.rdatasets: - if rd not in self.rdatasets: - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.rdatasets) - - def __iter__(self): - return iter(self.rdatasets) - - def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE, - create=False): - """Find an rdataset matching the specified properties in the - current node. - - @param rdclass: The class of the rdataset - @type rdclass: int - @param rdtype: The type of the rdataset - @type rdtype: int - @param covers: The covered type. Usually this value is - dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or - dns.rdatatype.RRSIG, then the covers value will be the rdata - type the SIG/RRSIG covers. The library treats the SIG and RRSIG - types as if they were a family of - types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much - easier to work with than if RRSIGs covering different rdata - types were aggregated into a single RRSIG rdataset. - @type covers: int - @param create: If True, create the rdataset if it is not found. - @type create: bool - @raises KeyError: An rdataset of the desired type and class does - not exist and I{create} is not True. - @rtype: dns.rdataset.Rdataset object - """ - - for rds in self.rdatasets: - if rds.match(rdclass, rdtype, covers): - return rds - if not create: - raise KeyError - rds = dns.rdataset.Rdataset(rdclass, rdtype) - self.rdatasets.append(rds) - return rds - - def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE, - create=False): - """Get an rdataset matching the specified properties in the - current node. - - None is returned if an rdataset of the specified type and - class does not exist and I{create} is not True. - - @param rdclass: The class of the rdataset - @type rdclass: int - @param rdtype: The type of the rdataset - @type rdtype: int - @param covers: The covered type. - @type covers: int - @param create: If True, create the rdataset if it is not found. - @type create: bool - @rtype: dns.rdataset.Rdataset object or None - """ - - try: - rds = self.find_rdataset(rdclass, rdtype, covers, create) - except KeyError: - rds = None - return rds - - def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE): - """Delete the rdataset matching the specified properties in the - current node. - - If a matching rdataset does not exist, it is not an error. - - @param rdclass: The class of the rdataset - @type rdclass: int - @param rdtype: The type of the rdataset - @type rdtype: int - @param covers: The covered type. - @type covers: int - """ - - rds = self.get_rdataset(rdclass, rdtype, covers) - if not rds is None: - self.rdatasets.remove(rds) - - def replace_rdataset(self, replacement): - """Replace an rdataset. - - It is not an error if there is no rdataset matching I{replacement}. - - Ownership of the I{replacement} object is transferred to the node; - in other words, this method does not store a copy of I{replacement} - at the node, it stores I{replacement} itself. - """ - - self.delete_rdataset(replacement.rdclass, replacement.rdtype, - replacement.covers) - self.rdatasets.append(replacement) diff --git a/lib/dnspython/dns/opcode.py b/lib/dnspython/dns/opcode.py deleted file mode 100644 index 3258c34cd9..0000000000 --- a/lib/dnspython/dns/opcode.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Opcodes.""" - -import dns.exception - -QUERY = 0 -IQUERY = 1 -STATUS = 2 -NOTIFY = 4 -UPDATE = 5 - -_by_text = { - 'QUERY' : QUERY, - 'IQUERY' : IQUERY, - 'STATUS' : STATUS, - 'NOTIFY' : NOTIFY, - 'UPDATE' : UPDATE -} - -# We construct the inverse mapping programmatically to ensure that we -# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that -# would cause the mapping not to be true inverse. - -_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) - - -class UnknownOpcode(dns.exception.DNSException): - """Raised if an opcode is unknown.""" - pass - -def from_text(text): - """Convert text into an opcode. - - @param text: the textual opcode - @type text: string - @raises UnknownOpcode: the opcode is unknown - @rtype: int - """ - - if text.isdigit(): - value = int(text) - if value >= 0 and value <= 15: - return value - value = _by_text.get(text.upper()) - if value is None: - raise UnknownOpcode - return value - -def from_flags(flags): - """Extract an opcode from DNS message flags. - - @param flags: int - @rtype: int - """ - - return (flags & 0x7800) >> 11 - -def to_flags(value): - """Convert an opcode to a value suitable for ORing into DNS message - flags. - @rtype: int - """ - - return (value << 11) & 0x7800 - -def to_text(value): - """Convert an opcode to text. - - @param value: the opcdoe - @type value: int - @raises UnknownOpcode: the opcode is unknown - @rtype: string - """ - - text = _by_value.get(value) - if text is None: - text = str(value) - return text - -def is_update(flags): - """True if the opcode in flags is UPDATE. - - @param flags: DNS flags - @type flags: int - @rtype: bool - """ - - if (from_flags(flags) == UPDATE): - return True - return False diff --git a/lib/dnspython/dns/query.py b/lib/dnspython/dns/query.py deleted file mode 100644 index addee4e3f2..0000000000 --- a/lib/dnspython/dns/query.py +++ /dev/null @@ -1,492 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Talk to a DNS server.""" - -from __future__ import generators - -import errno -import select -import socket -import struct -import sys -import time - -import dns.exception -import dns.inet -import dns.name -import dns.message -import dns.rdataclass -import dns.rdatatype - -class UnexpectedSource(dns.exception.DNSException): - """Raised if a query response comes from an unexpected address or port.""" - pass - -class BadResponse(dns.exception.FormError): - """Raised if a query response does not respond to the question asked.""" - pass - -def _compute_expiration(timeout): - if timeout is None: - return None - else: - return time.time() + timeout - -def _poll_for(fd, readable, writable, error, timeout): - """ - @param fd: File descriptor (int). - @param readable: Whether to wait for readability (bool). - @param writable: Whether to wait for writability (bool). - @param expiration: Deadline timeout (expiration time, in seconds (float)). - - @return True on success, False on timeout - """ - event_mask = 0 - if readable: - event_mask |= select.POLLIN - if writable: - event_mask |= select.POLLOUT - if error: - event_mask |= select.POLLERR - - pollable = select.poll() - pollable.register(fd, event_mask) - - if timeout: - event_list = pollable.poll(long(timeout * 1000)) - else: - event_list = pollable.poll() - - return bool(event_list) - -def _select_for(fd, readable, writable, error, timeout): - """ - @param fd: File descriptor (int). - @param readable: Whether to wait for readability (bool). - @param writable: Whether to wait for writability (bool). - @param expiration: Deadline timeout (expiration time, in seconds (float)). - - @return True on success, False on timeout - """ - rset, wset, xset = [], [], [] - - if readable: - rset = [fd] - if writable: - wset = [fd] - if error: - xset = [fd] - - if timeout is None: - (rcount, wcount, xcount) = select.select(rset, wset, xset) - else: - (rcount, wcount, xcount) = select.select(rset, wset, xset, timeout) - - return bool((rcount or wcount or xcount)) - -def _wait_for(fd, readable, writable, error, expiration): - done = False - while not done: - if expiration is None: - timeout = None - else: - timeout = expiration - time.time() - if timeout <= 0.0: - raise dns.exception.Timeout - try: - if not _polling_backend(fd, readable, writable, error, timeout): - raise dns.exception.Timeout - except select.error, e: - if e.args[0] != errno.EINTR: - raise e - done = True - -def _set_polling_backend(fn): - """ - Internal API. Do not use. - """ - global _polling_backend - - _polling_backend = fn - -if hasattr(select, 'poll'): - # Prefer poll() on platforms that support it because it has no - # limits on the maximum value of a file descriptor (plus it will - # be more efficient for high values). - _polling_backend = _poll_for -else: - _polling_backend = _select_for - -def _wait_for_readable(s, expiration): - _wait_for(s, True, False, True, expiration) - -def _wait_for_writable(s, expiration): - _wait_for(s, False, True, True, expiration) - -def _addresses_equal(af, a1, a2): - # Convert the first value of the tuple, which is a textual format - # address into binary form, so that we are not confused by different - # textual representations of the same address - n1 = dns.inet.inet_pton(af, a1[0]) - n2 = dns.inet.inet_pton(af, a2[0]) - return n1 == n2 and a1[1:] == a2[1:] - -def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, - ignore_unexpected=False, one_rr_per_rrset=False): - """Return the response obtained after sending a query via UDP. - - @param q: the query - @type q: dns.message.Message - @param where: where to send the message - @type where: string containing an IPv4 or IPv6 address - @param timeout: The number of seconds to wait before the query times out. - If None, the default, wait forever. - @type timeout: float - @param port: The port to which to send the message. The default is 53. - @type port: int - @param af: the address family to use. The default is None, which - causes the address family to use to be inferred from the form of of where. - If the inference attempt fails, AF_INET is used. - @type af: int - @rtype: dns.message.Message object - @param source: source address. The default is the IPv4 wildcard address. - @type source: string - @param source_port: The port from which to send the message. - The default is 0. - @type source_port: int - @param ignore_unexpected: If True, ignore responses from unexpected - sources. The default is False. - @type ignore_unexpected: bool - @param one_rr_per_rrset: Put each RR into its own RRset - @type one_rr_per_rrset: bool - """ - - wire = q.to_wire() - if af is None: - try: - af = dns.inet.af_for_address(where) - except: - af = dns.inet.AF_INET - if af == dns.inet.AF_INET: - destination = (where, port) - if source is not None: - source = (source, source_port) - elif af == dns.inet.AF_INET6: - destination = (where, port, 0, 0) - if source is not None: - source = (source, source_port, 0, 0) - s = socket.socket(af, socket.SOCK_DGRAM, 0) - try: - expiration = _compute_expiration(timeout) - s.setblocking(0) - if source is not None: - s.bind(source) - _wait_for_writable(s, expiration) - s.sendto(wire, destination) - while 1: - _wait_for_readable(s, expiration) - (wire, from_address) = s.recvfrom(65535) - if _addresses_equal(af, from_address, destination) or \ - (dns.inet.is_multicast(where) and \ - from_address[1:] == destination[1:]): - break - if not ignore_unexpected: - raise UnexpectedSource('got a response from ' - '%s instead of %s' % (from_address, - destination)) - finally: - s.close() - r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, - one_rr_per_rrset=one_rr_per_rrset) - if not q.is_response(r): - raise BadResponse - return r - -def _net_read(sock, count, expiration): - """Read the specified number of bytes from sock. Keep trying until we - either get the desired amount, or we hit EOF. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - s = '' - while count > 0: - _wait_for_readable(sock, expiration) - n = sock.recv(count) - if n == '': - raise EOFError - count = count - len(n) - s = s + n - return s - -def _net_write(sock, data, expiration): - """Write the specified data to the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - current = 0 - l = len(data) - while current < l: - _wait_for_writable(sock, expiration) - current += sock.send(data[current:]) - -def _connect(s, address): - try: - s.connect(address) - except socket.error: - (ty, v) = sys.exc_info()[:2] - if v[0] != errno.EINPROGRESS and \ - v[0] != errno.EWOULDBLOCK and \ - v[0] != errno.EALREADY: - raise v - -def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, - one_rr_per_rrset=False): - """Return the response obtained after sending a query via TCP. - - @param q: the query - @type q: dns.message.Message object - @param where: where to send the message - @type where: string containing an IPv4 or IPv6 address - @param timeout: The number of seconds to wait before the query times out. - If None, the default, wait forever. - @type timeout: float - @param port: The port to which to send the message. The default is 53. - @type port: int - @param af: the address family to use. The default is None, which - causes the address family to use to be inferred from the form of of where. - If the inference attempt fails, AF_INET is used. - @type af: int - @rtype: dns.message.Message object - @param source: source address. The default is the IPv4 wildcard address. - @type source: string - @param source_port: The port from which to send the message. - The default is 0. - @type source_port: int - @param one_rr_per_rrset: Put each RR into its own RRset - @type one_rr_per_rrset: bool - """ - - wire = q.to_wire() - if af is None: - try: - af = dns.inet.af_for_address(where) - except: - af = dns.inet.AF_INET - if af == dns.inet.AF_INET: - destination = (where, port) - if source is not None: - source = (source, source_port) - elif af == dns.inet.AF_INET6: - destination = (where, port, 0, 0) - if source is not None: - source = (source, source_port, 0, 0) - s = socket.socket(af, socket.SOCK_STREAM, 0) - try: - expiration = _compute_expiration(timeout) - s.setblocking(0) - if source is not None: - s.bind(source) - _connect(s, destination) - - l = len(wire) - - # copying the wire into tcpmsg is inefficient, but lets us - # avoid writev() or doing a short write that would get pushed - # onto the net - tcpmsg = struct.pack("!H", l) + wire - _net_write(s, tcpmsg, expiration) - ldata = _net_read(s, 2, expiration) - (l,) = struct.unpack("!H", ldata) - wire = _net_read(s, l, expiration) - finally: - s.close() - r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, - one_rr_per_rrset=one_rr_per_rrset) - if not q.is_response(r): - raise BadResponse - return r - -def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, - timeout=None, port=53, keyring=None, keyname=None, relativize=True, - af=None, lifetime=None, source=None, source_port=0, serial=0, - use_udp=False, keyalgorithm=dns.tsig.default_algorithm): - """Return a generator for the responses to a zone transfer. - - @param where: where to send the message - @type where: string containing an IPv4 or IPv6 address - @param zone: The name of the zone to transfer - @type zone: dns.name.Name object or string - @param rdtype: The type of zone transfer. The default is - dns.rdatatype.AXFR. - @type rdtype: int or string - @param rdclass: The class of the zone transfer. The default is - dns.rdatatype.IN. - @type rdclass: int or string - @param timeout: The number of seconds to wait for each response message. - If None, the default, wait forever. - @type timeout: float - @param port: The port to which to send the message. The default is 53. - @type port: int - @param keyring: The TSIG keyring to use - @type keyring: dict - @param keyname: The name of the TSIG key to use - @type keyname: dns.name.Name object or string - @param relativize: If True, all names in the zone will be relativized to - the zone origin. It is essential that the relativize setting matches - the one specified to dns.zone.from_xfr(). - @type relativize: bool - @param af: the address family to use. The default is None, which - causes the address family to use to be inferred from the form of of where. - If the inference attempt fails, AF_INET is used. - @type af: int - @param lifetime: The total number of seconds to spend doing the transfer. - If None, the default, then there is no limit on the time the transfer may - take. - @type lifetime: float - @rtype: generator of dns.message.Message objects. - @param source: source address. The default is the IPv4 wildcard address. - @type source: string - @param source_port: The port from which to send the message. - The default is 0. - @type source_port: int - @param serial: The SOA serial number to use as the base for an IXFR diff - sequence (only meaningful if rdtype == dns.rdatatype.IXFR). - @type serial: int - @param use_udp: Use UDP (only meaningful for IXFR) - @type use_udp: bool - @param keyalgorithm: The TSIG algorithm to use; defaults to - dns.tsig.default_algorithm - @type keyalgorithm: string - """ - - if isinstance(zone, (str, unicode)): - zone = dns.name.from_text(zone) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - q = dns.message.make_query(zone, rdtype, rdclass) - if rdtype == dns.rdatatype.IXFR: - rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA', - '. . %u 0 0 0 0' % serial) - q.authority.append(rrset) - if not keyring is None: - q.use_tsig(keyring, keyname, algorithm=keyalgorithm) - wire = q.to_wire() - if af is None: - try: - af = dns.inet.af_for_address(where) - except: - af = dns.inet.AF_INET - if af == dns.inet.AF_INET: - destination = (where, port) - if source is not None: - source = (source, source_port) - elif af == dns.inet.AF_INET6: - destination = (where, port, 0, 0) - if source is not None: - source = (source, source_port, 0, 0) - if use_udp: - if rdtype != dns.rdatatype.IXFR: - raise ValueError('cannot do a UDP AXFR') - s = socket.socket(af, socket.SOCK_DGRAM, 0) - else: - s = socket.socket(af, socket.SOCK_STREAM, 0) - s.setblocking(0) - if source is not None: - s.bind(source) - expiration = _compute_expiration(lifetime) - _connect(s, destination) - l = len(wire) - if use_udp: - _wait_for_writable(s, expiration) - s.send(wire) - else: - tcpmsg = struct.pack("!H", l) + wire - _net_write(s, tcpmsg, expiration) - done = False - soa_rrset = None - soa_count = 0 - if relativize: - origin = zone - oname = dns.name.empty - else: - origin = None - oname = zone - tsig_ctx = None - first = True - while not done: - mexpiration = _compute_expiration(timeout) - if mexpiration is None or mexpiration > expiration: - mexpiration = expiration - if use_udp: - _wait_for_readable(s, expiration) - (wire, from_address) = s.recvfrom(65535) - else: - ldata = _net_read(s, 2, mexpiration) - (l,) = struct.unpack("!H", ldata) - wire = _net_read(s, l, mexpiration) - r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, - xfr=True, origin=origin, tsig_ctx=tsig_ctx, - multi=True, first=first, - one_rr_per_rrset=(rdtype==dns.rdatatype.IXFR)) - tsig_ctx = r.tsig_ctx - first = False - answer_index = 0 - delete_mode = False - expecting_SOA = False - if soa_rrset is None: - if not r.answer or r.answer[0].name != oname: - raise dns.exception.FormError - rrset = r.answer[0] - if rrset.rdtype != dns.rdatatype.SOA: - raise dns.exception.FormError("first RRset is not an SOA") - answer_index = 1 - soa_rrset = rrset.copy() - if rdtype == dns.rdatatype.IXFR: - if soa_rrset[0].serial == serial: - # - # We're already up-to-date. - # - done = True - else: - expecting_SOA = True - # - # Process SOAs in the answer section (other than the initial - # SOA in the first message). - # - for rrset in r.answer[answer_index:]: - if done: - raise dns.exception.FormError("answers after final SOA") - if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: - if expecting_SOA: - if rrset[0].serial != serial: - raise dns.exception.FormError("IXFR base serial mismatch") - expecting_SOA = False - elif rdtype == dns.rdatatype.IXFR: - delete_mode = not delete_mode - if rrset == soa_rrset and not delete_mode: - done = True - elif expecting_SOA: - # - # We made an IXFR request and are expecting another - # SOA RR, but saw something else, so this must be an - # AXFR response. - # - rdtype = dns.rdatatype.AXFR - expecting_SOA = False - if done and q.keyring and not r.had_tsig: - raise dns.exception.FormError("missing TSIG") - yield r - s.close() diff --git a/lib/dnspython/dns/rcode.py b/lib/dnspython/dns/rcode.py deleted file mode 100644 index 7807782aa4..0000000000 --- a/lib/dnspython/dns/rcode.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Result Codes.""" - -import dns.exception - -NOERROR = 0 -FORMERR = 1 -SERVFAIL = 2 -NXDOMAIN = 3 -NOTIMP = 4 -REFUSED = 5 -YXDOMAIN = 6 -YXRRSET = 7 -NXRRSET = 8 -NOTAUTH = 9 -NOTZONE = 10 -BADVERS = 16 - -_by_text = { - 'NOERROR' : NOERROR, - 'FORMERR' : FORMERR, - 'SERVFAIL' : SERVFAIL, - 'NXDOMAIN' : NXDOMAIN, - 'NOTIMP' : NOTIMP, - 'REFUSED' : REFUSED, - 'YXDOMAIN' : YXDOMAIN, - 'YXRRSET' : YXRRSET, - 'NXRRSET' : NXRRSET, - 'NOTAUTH' : NOTAUTH, - 'NOTZONE' : NOTZONE, - 'BADVERS' : BADVERS -} - -# We construct the inverse mapping programmatically to ensure that we -# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that -# would cause the mapping not to be a true inverse. - -_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) - - -class UnknownRcode(dns.exception.DNSException): - """Raised if an rcode is unknown.""" - pass - -def from_text(text): - """Convert text into an rcode. - - @param text: the texual rcode - @type text: string - @raises UnknownRcode: the rcode is unknown - @rtype: int - """ - - if text.isdigit(): - v = int(text) - if v >= 0 and v <= 4095: - return v - v = _by_text.get(text.upper()) - if v is None: - raise UnknownRcode - return v - -def from_flags(flags, ednsflags): - """Return the rcode value encoded by flags and ednsflags. - - @param flags: the DNS flags - @type flags: int - @param ednsflags: the EDNS flags - @type ednsflags: int - @raises ValueError: rcode is < 0 or > 4095 - @rtype: int - """ - - value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0) - if value < 0 or value > 4095: - raise ValueError('rcode must be >= 0 and <= 4095') - return value - -def to_flags(value): - """Return a (flags, ednsflags) tuple which encodes the rcode. - - @param value: the rcode - @type value: int - @raises ValueError: rcode is < 0 or > 4095 - @rtype: (int, int) tuple - """ - - if value < 0 or value > 4095: - raise ValueError('rcode must be >= 0 and <= 4095') - v = value & 0xf - ev = long(value & 0xff0) << 20 - return (v, ev) - -def to_text(value): - """Convert rcode into text. - - @param value: the rcode - @type value: int - @rtype: string - """ - - text = _by_value.get(value) - if text is None: - text = str(value) - return text diff --git a/lib/dnspython/dns/rdata.py b/lib/dnspython/dns/rdata.py deleted file mode 100644 index 350bf790b8..0000000000 --- a/lib/dnspython/dns/rdata.py +++ /dev/null @@ -1,478 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdata. - -@var _rdata_modules: A dictionary mapping a (rdclass, rdtype) tuple to -the module which implements that type. -@type _rdata_modules: dict -@var _module_prefix: The prefix to use when forming modules names. The -default is 'dns.rdtypes'. Changing this value will break the library. -@type _module_prefix: string -@var _hex_chunk: At most this many octets that will be represented in each -chunk of hexstring that _hexify() produces before whitespace occurs. -@type _hex_chunk: int""" - -import cStringIO - -import dns.exception -import dns.name -import dns.rdataclass -import dns.rdatatype -import dns.tokenizer -import dns.wiredata - -_hex_chunksize = 32 - -def _hexify(data, chunksize=None): - """Convert a binary string into its hex encoding, broken up into chunks - of I{chunksize} characters separated by a space. - - @param data: the binary string - @type data: string - @param chunksize: the chunk size. Default is L{dns.rdata._hex_chunksize} - @rtype: string - """ - - if chunksize is None: - chunksize = _hex_chunksize - hex = data.encode('hex_codec') - l = len(hex) - if l > chunksize: - chunks = [] - i = 0 - while i < l: - chunks.append(hex[i : i + chunksize]) - i += chunksize - hex = ' '.join(chunks) - return hex - -_base64_chunksize = 32 - -def _base64ify(data, chunksize=None): - """Convert a binary string into its base64 encoding, broken up into chunks - of I{chunksize} characters separated by a space. - - @param data: the binary string - @type data: string - @param chunksize: the chunk size. Default is - L{dns.rdata._base64_chunksize} - @rtype: string - """ - - if chunksize is None: - chunksize = _base64_chunksize - b64 = data.encode('base64_codec') - b64 = b64.replace('\n', '') - l = len(b64) - if l > chunksize: - chunks = [] - i = 0 - while i < l: - chunks.append(b64[i : i + chunksize]) - i += chunksize - b64 = ' '.join(chunks) - return b64 - -__escaped = { - '"' : True, - '\\' : True, - } - -def _escapify(qstring): - """Escape the characters in a quoted string which need it. - - @param qstring: the string - @type qstring: string - @returns: the escaped string - @rtype: string - """ - - text = '' - for c in qstring: - if c in __escaped: - text += '\\' + c - elif ord(c) >= 0x20 and ord(c) < 0x7F: - text += c - else: - text += '\\%03d' % ord(c) - return text - -def _truncate_bitmap(what): - """Determine the index of greatest byte that isn't all zeros, and - return the bitmap that contains all the bytes less than that index. - - @param what: a string of octets representing a bitmap. - @type what: string - @rtype: string - """ - - for i in xrange(len(what) - 1, -1, -1): - if what[i] != '\x00': - break - return ''.join(what[0 : i + 1]) - -class Rdata(object): - """Base class for all DNS rdata types. - """ - - __slots__ = ['rdclass', 'rdtype'] - - def __init__(self, rdclass, rdtype): - """Initialize an rdata. - @param rdclass: The rdata class - @type rdclass: int - @param rdtype: The rdata type - @type rdtype: int - """ - - self.rdclass = rdclass - self.rdtype = rdtype - - def covers(self): - """DNS SIG/RRSIG rdatas apply to a specific type; this type is - returned by the covers() function. If the rdata type is not - SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when - creating rdatasets, allowing the rdataset to contain only RRSIGs - of a particular type, e.g. RRSIG(NS). - @rtype: int - """ - - return dns.rdatatype.NONE - - def extended_rdatatype(self): - """Return a 32-bit type value, the least significant 16 bits of - which are the ordinary DNS type, and the upper 16 bits of which are - the "covered" type, if any. - @rtype: int - """ - - return self.covers() << 16 | self.rdtype - - def to_text(self, origin=None, relativize=True, **kw): - """Convert an rdata to text format. - @rtype: string - """ - raise NotImplementedError - - def to_wire(self, file, compress = None, origin = None): - """Convert an rdata to wire format. - @rtype: string - """ - - raise NotImplementedError - - def to_digestable(self, origin = None): - """Convert rdata to a format suitable for digesting in hashes. This - is also the DNSSEC canonical form.""" - f = cStringIO.StringIO() - self.to_wire(f, None, origin) - return f.getvalue() - - def validate(self): - """Check that the current contents of the rdata's fields are - valid. If you change an rdata by assigning to its fields, - it is a good idea to call validate() when you are done making - changes. - """ - dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text()) - - def __repr__(self): - covers = self.covers() - if covers == dns.rdatatype.NONE: - ctext = '' - else: - ctext = '(' + dns.rdatatype.to_text(covers) + ')' - return '' - - def __str__(self): - return self.to_text() - - def _cmp(self, other): - """Compare an rdata with another rdata of the same rdtype and - rdclass. Return < 0 if self < other in the DNSSEC ordering, - 0 if self == other, and > 0 if self > other. - """ - - raise NotImplementedError - - def __eq__(self, other): - if not isinstance(other, Rdata): - return False - if self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype: - return False - return self._cmp(other) == 0 - - def __ne__(self, other): - if not isinstance(other, Rdata): - return True - if self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype: - return True - return self._cmp(other) != 0 - - def __lt__(self, other): - if not isinstance(other, Rdata) or \ - self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype: - return NotImplemented - return self._cmp(other) < 0 - - def __le__(self, other): - if not isinstance(other, Rdata) or \ - self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype: - return NotImplemented - return self._cmp(other) <= 0 - - def __ge__(self, other): - if not isinstance(other, Rdata) or \ - self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype: - return NotImplemented - return self._cmp(other) >= 0 - - def __gt__(self, other): - if not isinstance(other, Rdata) or \ - self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype: - return NotImplemented - return self._cmp(other) > 0 - - def __hash__(self): - return hash(self.to_digestable(dns.name.root)) - - def _wire_cmp(self, other): - # A number of types compare rdata in wire form, so we provide - # the method here instead of duplicating it. - # - # We specifiy an arbitrary origin of '.' when doing the - # comparison, since the rdata may have relative names and we - # can't convert a relative name to wire without an origin. - b1 = cStringIO.StringIO() - self.to_wire(b1, None, dns.name.root) - b2 = cStringIO.StringIO() - other.to_wire(b2, None, dns.name.root) - return cmp(b1.getvalue(), b2.getvalue()) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - """Build an rdata object from text format. - - @param rdclass: The rdata class - @type rdclass: int - @param rdtype: The rdata type - @type rdtype: int - @param tok: The tokenizer - @type tok: dns.tokenizer.Tokenizer - @param origin: The origin to use for relative names - @type origin: dns.name.Name - @param relativize: should names be relativized? - @type relativize: bool - @rtype: dns.rdata.Rdata instance - """ - - raise NotImplementedError - - from_text = classmethod(from_text) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - """Build an rdata object from wire format - - @param rdclass: The rdata class - @type rdclass: int - @param rdtype: The rdata type - @type rdtype: int - @param wire: The wire-format message - @type wire: string - @param current: The offet in wire of the beginning of the rdata. - @type current: int - @param rdlen: The length of the wire-format rdata - @type rdlen: int - @param origin: The origin to use for relative names - @type origin: dns.name.Name - @rtype: dns.rdata.Rdata instance - """ - - raise NotImplementedError - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - """Convert any domain names in the rdata to the specified - relativization. - """ - - pass - - -class GenericRdata(Rdata): - """Generate Rdata Class - - This class is used for rdata types for which we have no better - implementation. It implements the DNS "unknown RRs" scheme. - """ - - __slots__ = ['data'] - - def __init__(self, rdclass, rdtype, data): - super(GenericRdata, self).__init__(rdclass, rdtype) - self.data = data - - def to_text(self, origin=None, relativize=True, **kw): - return r'\# %d ' % len(self.data) + _hexify(self.data) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - token = tok.get() - if not token.is_identifier() or token.value != '\#': - raise dns.exception.SyntaxError(r'generic rdata does not start with \#') - length = tok.get_int() - chunks = [] - while 1: - token = tok.get() - if token.is_eol_or_eof(): - break - chunks.append(token.value) - hex = ''.join(chunks) - data = hex.decode('hex_codec') - if len(data) != length: - raise dns.exception.SyntaxError('generic rdata hex data has wrong length') - return cls(rdclass, rdtype, data) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - file.write(self.data) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - return cls(rdclass, rdtype, wire[current : current + rdlen]) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - return cmp(self.data, other.data) - -_rdata_modules = {} -_module_prefix = 'dns.rdtypes' - -def get_rdata_class(rdclass, rdtype): - - def import_module(name): - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - - mod = _rdata_modules.get((rdclass, rdtype)) - rdclass_text = dns.rdataclass.to_text(rdclass) - rdtype_text = dns.rdatatype.to_text(rdtype) - rdtype_text = rdtype_text.replace('-', '_') - if not mod: - mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype)) - if not mod: - try: - mod = import_module('.'.join([_module_prefix, - rdclass_text, rdtype_text])) - _rdata_modules[(rdclass, rdtype)] = mod - except ImportError: - try: - mod = import_module('.'.join([_module_prefix, - 'ANY', rdtype_text])) - _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod - except ImportError: - mod = None - if mod: - cls = getattr(mod, rdtype_text) - else: - cls = GenericRdata - return cls - -def from_text(rdclass, rdtype, tok, origin = None, relativize = True): - """Build an rdata object from text format. - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_text() class method is called - with the parameters to this function. - - If I{tok} is a string, then a tokenizer is created and the string - is used as its input. - - @param rdclass: The rdata class - @type rdclass: int - @param rdtype: The rdata type - @type rdtype: int - @param tok: The tokenizer or input text - @type tok: dns.tokenizer.Tokenizer or string - @param origin: The origin to use for relative names - @type origin: dns.name.Name - @param relativize: Should names be relativized? - @type relativize: bool - @rtype: dns.rdata.Rdata instance""" - - if isinstance(tok, str): - tok = dns.tokenizer.Tokenizer(tok) - cls = get_rdata_class(rdclass, rdtype) - if cls != GenericRdata: - # peek at first token - token = tok.get() - tok.unget(token) - if token.is_identifier() and \ - token.value == r'\#': - # - # Known type using the generic syntax. Extract the - # wire form from the generic syntax, and then run - # from_wire on it. - # - rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin, - relativize) - return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data), - origin) - return cls.from_text(rdclass, rdtype, tok, origin, relativize) - -def from_wire(rdclass, rdtype, wire, current, rdlen, origin = None): - """Build an rdata object from wire format - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_wire() class method is called - with the parameters to this function. - - @param rdclass: The rdata class - @type rdclass: int - @param rdtype: The rdata type - @type rdtype: int - @param wire: The wire-format message - @type wire: string - @param current: The offet in wire of the beginning of the rdata. - @type current: int - @param rdlen: The length of the wire-format rdata - @type rdlen: int - @param origin: The origin to use for relative names - @type origin: dns.name.Name - @rtype: dns.rdata.Rdata instance""" - - wire = dns.wiredata.maybe_wrap(wire) - cls = get_rdata_class(rdclass, rdtype) - return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin) diff --git a/lib/dnspython/dns/rdataclass.py b/lib/dnspython/dns/rdataclass.py deleted file mode 100644 index 7601e70c3a..0000000000 --- a/lib/dnspython/dns/rdataclass.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Rdata Classes. - -@var _by_text: The rdata class textual name to value mapping -@type _by_text: dict -@var _by_value: The rdata class value to textual name mapping -@type _by_value: dict -@var _metaclasses: If an rdataclass is a metaclass, there will be a mapping -whose key is the rdatatype value and whose value is True in this dictionary. -@type _metaclasses: dict""" - -import re - -import dns.exception - -RESERVED0 = 0 -IN = 1 -CH = 3 -HS = 4 -NONE = 254 -ANY = 255 - -_by_text = { - 'RESERVED0' : RESERVED0, - 'IN' : IN, - 'CH' : CH, - 'HS' : HS, - 'NONE' : NONE, - 'ANY' : ANY - } - -# We construct the inverse mapping programmatically to ensure that we -# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that -# would cause the mapping not to be true inverse. - -_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) - -# Now that we've built the inverse map, we can add class aliases to -# the _by_text mapping. - -_by_text.update({ - 'INTERNET' : IN, - 'CHAOS' : CH, - 'HESIOD' : HS - }) - -_metaclasses = { - NONE : True, - ANY : True - } - -_unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I); - -class UnknownRdataclass(dns.exception.DNSException): - """Raised when a class is unknown.""" - pass - -def from_text(text): - """Convert text into a DNS rdata class value. - @param text: the text - @type text: string - @rtype: int - @raises dns.rdataclass.UnknownRdataclass: the class is unknown - @raises ValueError: the rdata class value is not >= 0 and <= 65535 - """ - - value = _by_text.get(text.upper()) - if value is None: - match = _unknown_class_pattern.match(text) - if match == None: - raise UnknownRdataclass - value = int(match.group(1)) - if value < 0 or value > 65535: - raise ValueError("class must be between >= 0 and <= 65535") - return value - -def to_text(value): - """Convert a DNS rdata class to text. - @param value: the rdata class value - @type value: int - @rtype: string - @raises ValueError: the rdata class value is not >= 0 and <= 65535 - """ - - if value < 0 or value > 65535: - raise ValueError("class must be between >= 0 and <= 65535") - text = _by_value.get(value) - if text is None: - text = 'CLASS' + `value` - return text - -def is_metaclass(rdclass): - """True if the class is a metaclass. - @param rdclass: the rdata class - @type rdclass: int - @rtype: bool""" - - if _metaclasses.has_key(rdclass): - return True - return False diff --git a/lib/dnspython/dns/rdataset.py b/lib/dnspython/dns/rdataset.py deleted file mode 100644 index dcd2b40191..0000000000 --- a/lib/dnspython/dns/rdataset.py +++ /dev/null @@ -1,329 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" - -import random -import StringIO -import struct - -import dns.exception -import dns.rdatatype -import dns.rdataclass -import dns.rdata -import dns.set - -# define SimpleSet here for backwards compatibility -SimpleSet = dns.set.Set - -class DifferingCovers(dns.exception.DNSException): - """Raised if an attempt is made to add a SIG/RRSIG whose covered type - is not the same as that of the other rdatas in the rdataset.""" - pass - -class IncompatibleTypes(dns.exception.DNSException): - """Raised if an attempt is made to add rdata of an incompatible type.""" - pass - -class Rdataset(dns.set.Set): - """A DNS rdataset. - - @ivar rdclass: The class of the rdataset - @type rdclass: int - @ivar rdtype: The type of the rdataset - @type rdtype: int - @ivar covers: The covered type. Usually this value is - dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or - dns.rdatatype.RRSIG, then the covers value will be the rdata - type the SIG/RRSIG covers. The library treats the SIG and RRSIG - types as if they were a family of - types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much - easier to work with than if RRSIGs covering different rdata - types were aggregated into a single RRSIG rdataset. - @type covers: int - @ivar ttl: The DNS TTL (Time To Live) value - @type ttl: int - """ - - __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl'] - - def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE): - """Create a new rdataset of the specified class and type. - - @see: the description of the class instance variables for the - meaning of I{rdclass} and I{rdtype}""" - - super(Rdataset, self).__init__() - self.rdclass = rdclass - self.rdtype = rdtype - self.covers = covers - self.ttl = 0 - - def _clone(self): - obj = super(Rdataset, self)._clone() - obj.rdclass = self.rdclass - obj.rdtype = self.rdtype - obj.covers = self.covers - obj.ttl = self.ttl - return obj - - def update_ttl(self, ttl): - """Set the TTL of the rdataset to be the lesser of the set's current - TTL or the specified TTL. If the set contains no rdatas, set the TTL - to the specified TTL. - @param ttl: The TTL - @type ttl: int""" - - if len(self) == 0: - self.ttl = ttl - elif ttl < self.ttl: - self.ttl = ttl - - def add(self, rd, ttl=None): - """Add the specified rdata to the rdataset. - - If the optional I{ttl} parameter is supplied, then - self.update_ttl(ttl) will be called prior to adding the rdata. - - @param rd: The rdata - @type rd: dns.rdata.Rdata object - @param ttl: The TTL - @type ttl: int""" - - # - # If we're adding a signature, do some special handling to - # check that the signature covers the same type as the - # other rdatas in this rdataset. If this is the first rdata - # in the set, initialize the covers field. - # - if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: - raise IncompatibleTypes - if not ttl is None: - self.update_ttl(ttl) - if self.rdtype == dns.rdatatype.RRSIG or \ - self.rdtype == dns.rdatatype.SIG: - covers = rd.covers() - if len(self) == 0 and self.covers == dns.rdatatype.NONE: - self.covers = covers - elif self.covers != covers: - raise DifferingCovers - if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: - self.clear() - super(Rdataset, self).add(rd) - - def union_update(self, other): - self.update_ttl(other.ttl) - super(Rdataset, self).union_update(other) - - def intersection_update(self, other): - self.update_ttl(other.ttl) - super(Rdataset, self).intersection_update(other) - - def update(self, other): - """Add all rdatas in other to self. - - @param other: The rdataset from which to update - @type other: dns.rdataset.Rdataset object""" - - self.update_ttl(other.ttl) - super(Rdataset, self).update(other) - - def __repr__(self): - if self.covers == 0: - ctext = '' - else: - ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' - return '' - - def __str__(self): - return self.to_text() - - def __eq__(self, other): - """Two rdatasets are equal if they have the same class, type, and - covers, and contain the same rdata. - @rtype: bool""" - - if not isinstance(other, Rdataset): - return False - if self.rdclass != other.rdclass or \ - self.rdtype != other.rdtype or \ - self.covers != other.covers: - return False - return super(Rdataset, self).__eq__(other) - - def __ne__(self, other): - return not self.__eq__(other) - - def to_text(self, name=None, origin=None, relativize=True, - override_rdclass=None, **kw): - """Convert the rdataset into DNS master file format. - - @see: L{dns.name.Name.choose_relativity} for more information - on how I{origin} and I{relativize} determine the way names - are emitted. - - Any additional keyword arguments are passed on to the rdata - to_text() method. - - @param name: If name is not None, emit a RRs with I{name} as - the owner name. - @type name: dns.name.Name object - @param origin: The origin for relative names, or None. - @type origin: dns.name.Name object - @param relativize: True if names should names be relativized - @type relativize: bool""" - if not name is None: - name = name.choose_relativity(origin, relativize) - ntext = str(name) - pad = ' ' - else: - ntext = '' - pad = '' - s = StringIO.StringIO() - if not override_rdclass is None: - rdclass = override_rdclass - else: - rdclass = self.rdclass - if len(self) == 0: - # - # Empty rdatasets are used for the question section, and in - # some dynamic updates, so we don't need to print out the TTL - # (which is meaningless anyway). - # - print >> s, '%s%s%s %s' % (ntext, pad, - dns.rdataclass.to_text(rdclass), - dns.rdatatype.to_text(self.rdtype)) - else: - for rd in self: - print >> s, '%s%s%d %s %s %s' % \ - (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass), - dns.rdatatype.to_text(self.rdtype), - rd.to_text(origin=origin, relativize=relativize, **kw)) - # - # We strip off the final \n for the caller's convenience in printing - # - return s.getvalue()[:-1] - - def to_wire(self, name, file, compress=None, origin=None, - override_rdclass=None, want_shuffle=True): - """Convert the rdataset to wire format. - - @param name: The owner name of the RRset that will be emitted - @type name: dns.name.Name object - @param file: The file to which the wire format data will be appended - @type file: file - @param compress: The compression table to use; the default is None. - @type compress: dict - @param origin: The origin to be appended to any relative names when - they are emitted. The default is None. - @returns: the number of records emitted - @rtype: int - """ - - if not override_rdclass is None: - rdclass = override_rdclass - want_shuffle = False - else: - rdclass = self.rdclass - file.seek(0, 2) - if len(self) == 0: - name.to_wire(file, compress, origin) - stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0) - file.write(stuff) - return 1 - else: - if want_shuffle: - l = list(self) - random.shuffle(l) - else: - l = self - for rd in l: - name.to_wire(file, compress, origin) - stuff = struct.pack("!HHIH", self.rdtype, rdclass, - self.ttl, 0) - file.write(stuff) - start = file.tell() - rd.to_wire(file, compress, origin) - end = file.tell() - assert end - start < 65536 - file.seek(start - 2) - stuff = struct.pack("!H", end - start) - file.write(stuff) - file.seek(0, 2) - return len(self) - - def match(self, rdclass, rdtype, covers): - """Returns True if this rdataset matches the specified class, type, - and covers""" - if self.rdclass == rdclass and \ - self.rdtype == rdtype and \ - self.covers == covers: - return True - return False - -def from_text_list(rdclass, rdtype, ttl, text_rdatas): - """Create an rdataset with the specified class, type, and TTL, and with - the specified list of rdatas in text format. - - @rtype: dns.rdataset.Rdataset object - """ - - if isinstance(rdclass, (str, unicode)): - rdclass = dns.rdataclass.from_text(rdclass) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - r = Rdataset(rdclass, rdtype) - r.update_ttl(ttl) - for t in text_rdatas: - rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) - r.add(rd) - return r - -def from_text(rdclass, rdtype, ttl, *text_rdatas): - """Create an rdataset with the specified class, type, and TTL, and with - the specified rdatas in text format. - - @rtype: dns.rdataset.Rdataset object - """ - - return from_text_list(rdclass, rdtype, ttl, text_rdatas) - -def from_rdata_list(ttl, rdatas): - """Create an rdataset with the specified TTL, and with - the specified list of rdata objects. - - @rtype: dns.rdataset.Rdataset object - """ - - if len(rdatas) == 0: - raise ValueError("rdata list must not be empty") - r = None - for rd in rdatas: - if r is None: - r = Rdataset(rd.rdclass, rd.rdtype) - r.update_ttl(ttl) - first_time = False - r.add(rd) - return r - -def from_rdata(ttl, *rdatas): - """Create an rdataset with the specified TTL, and with - the specified rdata objects. - - @rtype: dns.rdataset.Rdataset object - """ - - return from_rdata_list(ttl, rdatas) diff --git a/lib/dnspython/dns/rdatatype.py b/lib/dnspython/dns/rdatatype.py deleted file mode 100644 index 380cfcd0d3..0000000000 --- a/lib/dnspython/dns/rdatatype.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Rdata Types. - -@var _by_text: The rdata type textual name to value mapping -@type _by_text: dict -@var _by_value: The rdata type value to textual name mapping -@type _by_value: dict -@var _metatypes: If an rdatatype is a metatype, there will be a mapping -whose key is the rdatatype value and whose value is True in this dictionary. -@type _metatypes: dict -@var _singletons: If an rdatatype is a singleton, there will be a mapping -whose key is the rdatatype value and whose value is True in this dictionary. -@type _singletons: dict""" - -import re - -import dns.exception - -NONE = 0 -A = 1 -NS = 2 -MD = 3 -MF = 4 -CNAME = 5 -SOA = 6 -MB = 7 -MG = 8 -MR = 9 -NULL = 10 -WKS = 11 -PTR = 12 -HINFO = 13 -MINFO = 14 -MX = 15 -TXT = 16 -RP = 17 -AFSDB = 18 -X25 = 19 -ISDN = 20 -RT = 21 -NSAP = 22 -NSAP_PTR = 23 -SIG = 24 -KEY = 25 -PX = 26 -GPOS = 27 -AAAA = 28 -LOC = 29 -NXT = 30 -SRV = 33 -NAPTR = 35 -KX = 36 -CERT = 37 -A6 = 38 -DNAME = 39 -OPT = 41 -APL = 42 -DS = 43 -SSHFP = 44 -IPSECKEY = 45 -RRSIG = 46 -NSEC = 47 -DNSKEY = 48 -DHCID = 49 -NSEC3 = 50 -NSEC3PARAM = 51 -HIP = 55 -SPF = 99 -UNSPEC = 103 -TKEY = 249 -TSIG = 250 -IXFR = 251 -AXFR = 252 -MAILB = 253 -MAILA = 254 -ANY = 255 -TA = 32768 -DLV = 32769 - -_by_text = { - 'NONE' : NONE, - 'A' : A, - 'NS' : NS, - 'MD' : MD, - 'MF' : MF, - 'CNAME' : CNAME, - 'SOA' : SOA, - 'MB' : MB, - 'MG' : MG, - 'MR' : MR, - 'NULL' : NULL, - 'WKS' : WKS, - 'PTR' : PTR, - 'HINFO' : HINFO, - 'MINFO' : MINFO, - 'MX' : MX, - 'TXT' : TXT, - 'RP' : RP, - 'AFSDB' : AFSDB, - 'X25' : X25, - 'ISDN' : ISDN, - 'RT' : RT, - 'NSAP' : NSAP, - 'NSAP-PTR' : NSAP_PTR, - 'SIG' : SIG, - 'KEY' : KEY, - 'PX' : PX, - 'GPOS' : GPOS, - 'AAAA' : AAAA, - 'LOC' : LOC, - 'NXT' : NXT, - 'SRV' : SRV, - 'NAPTR' : NAPTR, - 'KX' : KX, - 'CERT' : CERT, - 'A6' : A6, - 'DNAME' : DNAME, - 'OPT' : OPT, - 'APL' : APL, - 'DS' : DS, - 'SSHFP' : SSHFP, - 'IPSECKEY' : IPSECKEY, - 'RRSIG' : RRSIG, - 'NSEC' : NSEC, - 'DNSKEY' : DNSKEY, - 'DHCID' : DHCID, - 'NSEC3' : NSEC3, - 'NSEC3PARAM' : NSEC3PARAM, - 'HIP' : HIP, - 'SPF' : SPF, - 'UNSPEC' : UNSPEC, - 'TKEY' : TKEY, - 'TSIG' : TSIG, - 'IXFR' : IXFR, - 'AXFR' : AXFR, - 'MAILB' : MAILB, - 'MAILA' : MAILA, - 'ANY' : ANY, - 'TA' : TA, - 'DLV' : DLV, - } - -# We construct the inverse mapping programmatically to ensure that we -# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that -# would cause the mapping not to be true inverse. - -_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) - - -_metatypes = { - OPT : True - } - -_singletons = { - SOA : True, - NXT : True, - DNAME : True, - NSEC : True, - # CNAME is technically a singleton, but we allow multiple CNAMEs. - } - -_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I); - -class UnknownRdatatype(dns.exception.DNSException): - """Raised if a type is unknown.""" - pass - -def from_text(text): - """Convert text into a DNS rdata type value. - @param text: the text - @type text: string - @raises dns.rdatatype.UnknownRdatatype: the type is unknown - @raises ValueError: the rdata type value is not >= 0 and <= 65535 - @rtype: int""" - - value = _by_text.get(text.upper()) - if value is None: - match = _unknown_type_pattern.match(text) - if match == None: - raise UnknownRdatatype - value = int(match.group(1)) - if value < 0 or value > 65535: - raise ValueError("type must be between >= 0 and <= 65535") - return value - -def to_text(value): - """Convert a DNS rdata type to text. - @param value: the rdata type value - @type value: int - @raises ValueError: the rdata type value is not >= 0 and <= 65535 - @rtype: string""" - - if value < 0 or value > 65535: - raise ValueError("type must be between >= 0 and <= 65535") - text = _by_value.get(value) - if text is None: - text = 'TYPE' + `value` - return text - -def is_metatype(rdtype): - """True if the type is a metatype. - @param rdtype: the type - @type rdtype: int - @rtype: bool""" - - if rdtype >= TKEY and rdtype <= ANY or _metatypes.has_key(rdtype): - return True - return False - -def is_singleton(rdtype): - """True if the type is a singleton. - @param rdtype: the type - @type rdtype: int - @rtype: bool""" - - if _singletons.has_key(rdtype): - return True - return False diff --git a/lib/dnspython/dns/rdtypes/ANY/AFSDB.py b/lib/dnspython/dns/rdtypes/ANY/AFSDB.py deleted file mode 100644 index c729789afa..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/AFSDB.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.mxbase - -class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """AFSDB record - - @ivar subtype: the subtype value - @type subtype: int - @ivar hostname: the hostname name - @type hostname: dns.name.Name object""" - - # Use the property mechanism to make "subtype" an alias for the - # "preference" attribute, and "hostname" an alias for the "exchange" - # attribute. - # - # This lets us inherit the UncompressedMX implementation but lets - # the caller use appropriate attribute names for the rdata type. - # - # We probably lose some performance vs. a cut-and-paste - # implementation, but this way we don't copy code, and that's - # good. - - def get_subtype(self): - return self.preference - - def set_subtype(self, subtype): - self.preference = subtype - - subtype = property(get_subtype, set_subtype) - - def get_hostname(self): - return self.exchange - - def set_hostname(self, hostname): - self.exchange = hostname - - hostname = property(get_hostname, set_hostname) diff --git a/lib/dnspython/dns/rdtypes/ANY/CERT.py b/lib/dnspython/dns/rdtypes/ANY/CERT.py deleted file mode 100644 index c102521a14..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/CERT.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import struct - -import dns.exception -import dns.dnssec -import dns.rdata -import dns.tokenizer - -_ctype_by_value = { - 1 : 'PKIX', - 2 : 'SPKI', - 3 : 'PGP', - 253 : 'URI', - 254 : 'OID', - } - -_ctype_by_name = { - 'PKIX' : 1, - 'SPKI' : 2, - 'PGP' : 3, - 'URI' : 253, - 'OID' : 254, - } - -def _ctype_from_text(what): - v = _ctype_by_name.get(what) - if not v is None: - return v - return int(what) - -def _ctype_to_text(what): - v = _ctype_by_value.get(what) - if not v is None: - return v - return str(what) - -class CERT(dns.rdata.Rdata): - """CERT record - - @ivar certificate_type: certificate type - @type certificate_type: int - @ivar key_tag: key tag - @type key_tag: int - @ivar algorithm: algorithm - @type algorithm: int - @ivar certificate: the certificate or CRL - @type certificate: string - @see: RFC 2538""" - - __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate'] - - def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm, - certificate): - super(CERT, self).__init__(rdclass, rdtype) - self.certificate_type = certificate_type - self.key_tag = key_tag - self.algorithm = algorithm - self.certificate = certificate - - def to_text(self, origin=None, relativize=True, **kw): - certificate_type = _ctype_to_text(self.certificate_type) - return "%s %d %s %s" % (certificate_type, self.key_tag, - dns.dnssec.algorithm_to_text(self.algorithm), - dns.rdata._base64ify(self.certificate)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - certificate_type = _ctype_from_text(tok.get_string()) - key_tag = tok.get_uint16() - algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) - if algorithm < 0 or algorithm > 255: - raise dns.exception.SyntaxError("bad algorithm type") - chunks = [] - while 1: - t = tok.get().unescape() - if t.is_eol_or_eof(): - break - if not t.is_identifier(): - raise dns.exception.SyntaxError - chunks.append(t.value) - b64 = ''.join(chunks) - certificate = b64.decode('base64_codec') - return cls(rdclass, rdtype, certificate_type, key_tag, - algorithm, certificate) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - prefix = struct.pack("!HHB", self.certificate_type, self.key_tag, - self.algorithm) - file.write(prefix) - file.write(self.certificate) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - prefix = wire[current : current + 5].unwrap() - current += 5 - rdlen -= 5 - if rdlen < 0: - raise dns.exception.FormError - (certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix) - certificate = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, - certificate) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - f = cStringIO.StringIO() - self.to_wire(f) - wire1 = f.getvalue() - f.seek(0) - f.truncate() - other.to_wire(f) - wire2 = f.getvalue() - f.close() - - return cmp(wire1, wire2) diff --git a/lib/dnspython/dns/rdtypes/ANY/CNAME.py b/lib/dnspython/dns/rdtypes/ANY/CNAME.py deleted file mode 100644 index fb8e9be49e..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/CNAME.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.nsbase - -class CNAME(dns.rdtypes.nsbase.NSBase): - """CNAME record - - Note: although CNAME is officially a singleton type, dnspython allows - non-singleton CNAME rdatasets because such sets have been commonly - used by BIND and other nameservers for load balancing.""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/DLV.py b/lib/dnspython/dns/rdtypes/ANY/DLV.py deleted file mode 100644 index 8bd79793d1..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/DLV.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.dsbase - -class DLV(dns.rdtypes.dsbase.DSBase): - """DLV record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/DNAME.py b/lib/dnspython/dns/rdtypes/ANY/DNAME.py deleted file mode 100644 index d864001190..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/DNAME.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.nsbase - -class DNAME(dns.rdtypes.nsbase.UncompressedNS): - """DNAME record""" - def to_digestable(self, origin = None): - return self.target.to_digestable(origin) diff --git a/lib/dnspython/dns/rdtypes/ANY/DNSKEY.py b/lib/dnspython/dns/rdtypes/ANY/DNSKEY.py deleted file mode 100644 index 1d678d2f2a..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/DNSKEY.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -import struct - -import dns.exception -import dns.dnssec -import dns.rdata - -# flag constants -SEP = 0x0001 -REVOKE = 0x0080 -ZONE = 0x0100 - -class DNSKEY(dns.rdata.Rdata): - """DNSKEY record - - @ivar flags: the key flags - @type flags: int - @ivar protocol: the protocol for which this key may be used - @type protocol: int - @ivar algorithm: the algorithm used for the key - @type algorithm: int - @ivar key: the public key - @type key: string""" - - __slots__ = ['flags', 'protocol', 'algorithm', 'key'] - - def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): - super(DNSKEY, self).__init__(rdclass, rdtype) - self.flags = flags - self.protocol = protocol - self.algorithm = algorithm - self.key = key - - def to_text(self, origin=None, relativize=True, **kw): - return '%d %d %d %s' % (self.flags, self.protocol, self.algorithm, - dns.rdata._base64ify(self.key)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - flags = tok.get_uint16() - protocol = tok.get_uint8() - algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) - chunks = [] - while 1: - t = tok.get().unescape() - if t.is_eol_or_eof(): - break - if not t.is_identifier(): - raise dns.exception.SyntaxError - chunks.append(t.value) - b64 = ''.join(chunks) - key = b64.decode('base64_codec') - return cls(rdclass, rdtype, flags, protocol, algorithm, key) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) - file.write(header) - file.write(self.key) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - if rdlen < 4: - raise dns.exception.FormError - header = struct.unpack('!HBB', wire[current : current + 4]) - current += 4 - rdlen -= 4 - key = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, header[0], header[1], header[2], - key) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - hs = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) - ho = struct.pack("!HBB", other.flags, other.protocol, other.algorithm) - v = cmp(hs, ho) - if v == 0: - v = cmp(self.key, other.key) - return v diff --git a/lib/dnspython/dns/rdtypes/ANY/DS.py b/lib/dnspython/dns/rdtypes/ANY/DS.py deleted file mode 100644 index 56b6332de2..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/DS.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.dsbase - -class DS(dns.rdtypes.dsbase.DSBase): - """DS record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/GPOS.py b/lib/dnspython/dns/rdtypes/ANY/GPOS.py deleted file mode 100644 index 38d1d88b28..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/GPOS.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.rdata -import dns.tokenizer - -def _validate_float_string(what): - if what[0] == '-' or what[0] == '+': - what = what[1:] - if what.isdigit(): - return - (left, right) = what.split('.') - if left == '' and right == '': - raise dns.exception.FormError - if not left == '' and not left.isdigit(): - raise dns.exception.FormError - if not right == '' and not right.isdigit(): - raise dns.exception.FormError - -class GPOS(dns.rdata.Rdata): - """GPOS record - - @ivar latitude: latitude - @type latitude: string - @ivar longitude: longitude - @type longitude: string - @ivar altitude: altitude - @type altitude: string - @see: RFC 1712""" - - __slots__ = ['latitude', 'longitude', 'altitude'] - - def __init__(self, rdclass, rdtype, latitude, longitude, altitude): - super(GPOS, self).__init__(rdclass, rdtype) - if isinstance(latitude, float) or \ - isinstance(latitude, int) or \ - isinstance(latitude, long): - latitude = str(latitude) - if isinstance(longitude, float) or \ - isinstance(longitude, int) or \ - isinstance(longitude, long): - longitude = str(longitude) - if isinstance(altitude, float) or \ - isinstance(altitude, int) or \ - isinstance(altitude, long): - altitude = str(altitude) - _validate_float_string(latitude) - _validate_float_string(longitude) - _validate_float_string(altitude) - self.latitude = latitude - self.longitude = longitude - self.altitude = altitude - - def to_text(self, origin=None, relativize=True, **kw): - return '%s %s %s' % (self.latitude, self.longitude, self.altitude) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - latitude = tok.get_string() - longitude = tok.get_string() - altitude = tok.get_string() - tok.get_eol() - return cls(rdclass, rdtype, latitude, longitude, altitude) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - l = len(self.latitude) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.latitude) - l = len(self.longitude) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.longitude) - l = len(self.altitude) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.altitude) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l > rdlen: - raise dns.exception.FormError - latitude = wire[current : current + l].unwrap() - current += l - rdlen -= l - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l > rdlen: - raise dns.exception.FormError - longitude = wire[current : current + l].unwrap() - current += l - rdlen -= l - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l != rdlen: - raise dns.exception.FormError - altitude = wire[current : current + l].unwrap() - return cls(rdclass, rdtype, latitude, longitude, altitude) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - v = cmp(self.latitude, other.latitude) - if v == 0: - v = cmp(self.longitude, other.longitude) - if v == 0: - v = cmp(self.altitude, other.altitude) - return v - - def _get_float_latitude(self): - return float(self.latitude) - - def _set_float_latitude(self, value): - self.latitude = str(value) - - float_latitude = property(_get_float_latitude, _set_float_latitude, - doc="latitude as a floating point value") - - def _get_float_longitude(self): - return float(self.longitude) - - def _set_float_longitude(self, value): - self.longitude = str(value) - - float_longitude = property(_get_float_longitude, _set_float_longitude, - doc="longitude as a floating point value") - - def _get_float_altitude(self): - return float(self.altitude) - - def _set_float_altitude(self, value): - self.altitude = str(value) - - float_altitude = property(_get_float_altitude, _set_float_altitude, - doc="altitude as a floating point value") diff --git a/lib/dnspython/dns/rdtypes/ANY/HINFO.py b/lib/dnspython/dns/rdtypes/ANY/HINFO.py deleted file mode 100644 index 15fd54e69e..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/HINFO.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.rdata -import dns.tokenizer - -class HINFO(dns.rdata.Rdata): - """HINFO record - - @ivar cpu: the CPU type - @type cpu: string - @ivar os: the OS type - @type os: string - @see: RFC 1035""" - - __slots__ = ['cpu', 'os'] - - def __init__(self, rdclass, rdtype, cpu, os): - super(HINFO, self).__init__(rdclass, rdtype) - self.cpu = cpu - self.os = os - - def to_text(self, origin=None, relativize=True, **kw): - return '"%s" "%s"' % (dns.rdata._escapify(self.cpu), - dns.rdata._escapify(self.os)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - cpu = tok.get_string() - os = tok.get_string() - tok.get_eol() - return cls(rdclass, rdtype, cpu, os) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - l = len(self.cpu) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.cpu) - l = len(self.os) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.os) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l > rdlen: - raise dns.exception.FormError - cpu = wire[current : current + l].unwrap() - current += l - rdlen -= l - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l != rdlen: - raise dns.exception.FormError - os = wire[current : current + l].unwrap() - return cls(rdclass, rdtype, cpu, os) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - v = cmp(self.cpu, other.cpu) - if v == 0: - v = cmp(self.os, other.os) - return v diff --git a/lib/dnspython/dns/rdtypes/ANY/HIP.py b/lib/dnspython/dns/rdtypes/ANY/HIP.py deleted file mode 100644 index 968b36f5e2..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/HIP.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (C) 2010, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import string -import struct - -import dns.exception -import dns.rdata -import dns.rdatatype - -class HIP(dns.rdata.Rdata): - """HIP record - - @ivar hit: the host identity tag - @type hit: string - @ivar algorithm: the public key cryptographic algorithm - @type algorithm: int - @ivar key: the public key - @type key: string - @ivar servers: the rendezvous servers - @type servers: list of dns.name.Name objects - @see: RFC 5205""" - - __slots__ = ['hit', 'algorithm', 'key', 'servers'] - - def __init__(self, rdclass, rdtype, hit, algorithm, key, servers): - super(HIP, self).__init__(rdclass, rdtype) - self.hit = hit - self.algorithm = algorithm - self.key = key - self.servers = servers - - def to_text(self, origin=None, relativize=True, **kw): - hit = self.hit.encode('hex-codec') - key = self.key.encode('base64-codec').replace('\n', '') - text = '' - servers = [] - for server in self.servers: - servers.append(str(server.choose_relativity(origin, relativize))) - if len(servers) > 0: - text += (' ' + ' '.join(servers)) - return '%u %s %s%s' % (self.algorithm, hit, key, text) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - algorithm = tok.get_uint8() - hit = tok.get_string().decode('hex-codec') - if len(hit) > 255: - raise dns.exception.SyntaxError("HIT too long") - key = tok.get_string().decode('base64-codec') - servers = [] - while 1: - token = tok.get() - if token.is_eol_or_eof(): - break - server = dns.name.from_text(token.value, origin) - server.choose_relativity(origin, relativize) - servers.append(server) - return cls(rdclass, rdtype, hit, algorithm, key, servers) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - lh = len(self.hit) - lk = len(self.key) - file.write(struct.pack("!BBH", lh, self.algorithm, lk)) - file.write(self.hit) - file.write(self.key) - for server in self.servers: - server.to_wire(file, None, origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (lh, algorithm, lk) = struct.unpack('!BBH', - wire[current : current + 4]) - current += 4 - rdlen -= 4 - hit = wire[current : current + lh].unwrap() - current += lh - rdlen -= lh - key = wire[current : current + lk].unwrap() - current += lk - rdlen -= lk - servers = [] - while rdlen > 0: - (server, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - current += cused - rdlen -= cused - if not origin is None: - server = server.relativize(origin) - servers.append(server) - return cls(rdclass, rdtype, hit, algorithm, key, servers) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - servers = [] - for server in self.servers: - server = server.choose_relativity(origin, relativize) - servers.append(server) - self.servers = servers - - def _cmp(self, other): - b1 = cStringIO.StringIO() - lh = len(self.hit) - lk = len(self.key) - b1.write(struct.pack("!BBH", lh, self.algorithm, lk)) - b1.write(self.hit) - b1.write(self.key) - b2 = cStringIO.StringIO() - lh = len(other.hit) - lk = len(other.key) - b2.write(struct.pack("!BBH", lh, other.algorithm, lk)) - b2.write(other.hit) - b2.write(other.key) - v = cmp(b1.getvalue(), b2.getvalue()) - if v != 0: - return v - ls = len(self.servers) - lo = len(other.servers) - count = min(ls, lo) - i = 0 - while i < count: - v = cmp(self.servers[i], other.servers[i]) - if v != 0: - return v - i += 1 - return ls - lo diff --git a/lib/dnspython/dns/rdtypes/ANY/ISDN.py b/lib/dnspython/dns/rdtypes/ANY/ISDN.py deleted file mode 100644 index 0c2d3cd035..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/ISDN.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.rdata -import dns.tokenizer - -class ISDN(dns.rdata.Rdata): - """ISDN record - - @ivar address: the ISDN address - @type address: string - @ivar subaddress: the ISDN subaddress (or '' if not present) - @type subaddress: string - @see: RFC 1183""" - - __slots__ = ['address', 'subaddress'] - - def __init__(self, rdclass, rdtype, address, subaddress): - super(ISDN, self).__init__(rdclass, rdtype) - self.address = address - self.subaddress = subaddress - - def to_text(self, origin=None, relativize=True, **kw): - if self.subaddress: - return '"%s" "%s"' % (dns.rdata._escapify(self.address), - dns.rdata._escapify(self.subaddress)) - else: - return '"%s"' % dns.rdata._escapify(self.address) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - address = tok.get_string() - t = tok.get() - if not t.is_eol_or_eof(): - tok.unget(t) - subaddress = tok.get_string() - else: - tok.unget(t) - subaddress = '' - tok.get_eol() - return cls(rdclass, rdtype, address, subaddress) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - l = len(self.address) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.address) - l = len(self.subaddress) - if l > 0: - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.subaddress) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l > rdlen: - raise dns.exception.FormError - address = wire[current : current + l].unwrap() - current += l - rdlen -= l - if rdlen > 0: - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l != rdlen: - raise dns.exception.FormError - subaddress = wire[current : current + l].unwrap() - else: - subaddress = '' - return cls(rdclass, rdtype, address, subaddress) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - v = cmp(self.address, other.address) - if v == 0: - v = cmp(self.subaddress, other.subaddress) - return v diff --git a/lib/dnspython/dns/rdtypes/ANY/LOC.py b/lib/dnspython/dns/rdtypes/ANY/LOC.py deleted file mode 100644 index 154546d79d..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/LOC.py +++ /dev/null @@ -1,334 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import struct - -import dns.exception -import dns.rdata - -_pows = (1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, - 100000000L, 1000000000L, 10000000000L) - -def _exponent_of(what, desc): - exp = None - for i in xrange(len(_pows)): - if what // _pows[i] == 0L: - exp = i - 1 - break - if exp is None or exp < 0: - raise dns.exception.SyntaxError("%s value out of bounds" % desc) - return exp - -def _float_to_tuple(what): - if what < 0: - sign = -1 - what *= -1 - else: - sign = 1 - what = long(round(what * 3600000)) - degrees = int(what // 3600000) - what -= degrees * 3600000 - minutes = int(what // 60000) - what -= minutes * 60000 - seconds = int(what // 1000) - what -= int(seconds * 1000) - what = int(what) - return (degrees * sign, minutes, seconds, what) - -def _tuple_to_float(what): - if what[0] < 0: - sign = -1 - value = float(what[0]) * -1 - else: - sign = 1 - value = float(what[0]) - value += float(what[1]) / 60.0 - value += float(what[2]) / 3600.0 - value += float(what[3]) / 3600000.0 - return sign * value - -def _encode_size(what, desc): - what = long(what); - exponent = _exponent_of(what, desc) & 0xF - base = what // pow(10, exponent) & 0xF - return base * 16 + exponent - -def _decode_size(what, desc): - exponent = what & 0x0F - if exponent > 9: - raise dns.exception.SyntaxError("bad %s exponent" % desc) - base = (what & 0xF0) >> 4 - if base > 9: - raise dns.exception.SyntaxError("bad %s base" % desc) - return long(base) * pow(10, exponent) - -class LOC(dns.rdata.Rdata): - """LOC record - - @ivar latitude: latitude - @type latitude: (int, int, int, int) tuple specifying the degrees, minutes, - seconds, and milliseconds of the coordinate. - @ivar longitude: longitude - @type longitude: (int, int, int, int) tuple specifying the degrees, - minutes, seconds, and milliseconds of the coordinate. - @ivar altitude: altitude - @type altitude: float - @ivar size: size of the sphere - @type size: float - @ivar horizontal_precision: horizontal precision - @type horizontal_precision: float - @ivar vertical_precision: vertical precision - @type vertical_precision: float - @see: RFC 1876""" - - __slots__ = ['latitude', 'longitude', 'altitude', 'size', - 'horizontal_precision', 'vertical_precision'] - - def __init__(self, rdclass, rdtype, latitude, longitude, altitude, - size=1.0, hprec=10000.0, vprec=10.0): - """Initialize a LOC record instance. - - The parameters I{latitude} and I{longitude} may be either a 4-tuple - of integers specifying (degrees, minutes, seconds, milliseconds), - or they may be floating point values specifying the number of - degrees. The other parameters are floats.""" - - super(LOC, self).__init__(rdclass, rdtype) - if isinstance(latitude, int) or isinstance(latitude, long): - latitude = float(latitude) - if isinstance(latitude, float): - latitude = _float_to_tuple(latitude) - self.latitude = latitude - if isinstance(longitude, int) or isinstance(longitude, long): - longitude = float(longitude) - if isinstance(longitude, float): - longitude = _float_to_tuple(longitude) - self.longitude = longitude - self.altitude = float(altitude) - self.size = float(size) - self.horizontal_precision = float(hprec) - self.vertical_precision = float(vprec) - - def to_text(self, origin=None, relativize=True, **kw): - if self.latitude[0] > 0: - lat_hemisphere = 'N' - lat_degrees = self.latitude[0] - else: - lat_hemisphere = 'S' - lat_degrees = -1 * self.latitude[0] - if self.longitude[0] > 0: - long_hemisphere = 'E' - long_degrees = self.longitude[0] - else: - long_hemisphere = 'W' - long_degrees = -1 * self.longitude[0] - text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( - lat_degrees, self.latitude[1], self.latitude[2], self.latitude[3], - lat_hemisphere, long_degrees, self.longitude[1], self.longitude[2], - self.longitude[3], long_hemisphere, self.altitude / 100.0 - ) - - if self.size != 1.0 or self.horizontal_precision != 10000.0 or \ - self.vertical_precision != 10.0: - text += " %0.2fm %0.2fm %0.2fm" % ( - self.size / 100.0, self.horizontal_precision / 100.0, - self.vertical_precision / 100.0 - ) - return text - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - latitude = [0, 0, 0, 0] - longitude = [0, 0, 0, 0] - size = 1.0 - hprec = 10000.0 - vprec = 10.0 - - latitude[0] = tok.get_int() - t = tok.get_string() - if t.isdigit(): - latitude[1] = int(t) - t = tok.get_string() - if '.' in t: - (seconds, milliseconds) = t.split('.') - if not seconds.isdigit(): - raise dns.exception.SyntaxError('bad latitude seconds value') - latitude[2] = int(seconds) - if latitude[2] >= 60: - raise dns.exception.SyntaxError('latitude seconds >= 60') - l = len(milliseconds) - if l == 0 or l > 3 or not milliseconds.isdigit(): - raise dns.exception.SyntaxError('bad latitude milliseconds value') - if l == 1: - m = 100 - elif l == 2: - m = 10 - else: - m = 1 - latitude[3] = m * int(milliseconds) - t = tok.get_string() - elif t.isdigit(): - latitude[2] = int(t) - t = tok.get_string() - if t == 'S': - latitude[0] *= -1 - elif t != 'N': - raise dns.exception.SyntaxError('bad latitude hemisphere value') - - longitude[0] = tok.get_int() - t = tok.get_string() - if t.isdigit(): - longitude[1] = int(t) - t = tok.get_string() - if '.' in t: - (seconds, milliseconds) = t.split('.') - if not seconds.isdigit(): - raise dns.exception.SyntaxError('bad longitude seconds value') - longitude[2] = int(seconds) - if longitude[2] >= 60: - raise dns.exception.SyntaxError('longitude seconds >= 60') - l = len(milliseconds) - if l == 0 or l > 3 or not milliseconds.isdigit(): - raise dns.exception.SyntaxError('bad longitude milliseconds value') - if l == 1: - m = 100 - elif l == 2: - m = 10 - else: - m = 1 - longitude[3] = m * int(milliseconds) - t = tok.get_string() - elif t.isdigit(): - longitude[2] = int(t) - t = tok.get_string() - if t == 'W': - longitude[0] *= -1 - elif t != 'E': - raise dns.exception.SyntaxError('bad longitude hemisphere value') - - t = tok.get_string() - if t[-1] == 'm': - t = t[0 : -1] - altitude = float(t) * 100.0 # m -> cm - - token = tok.get().unescape() - if not token.is_eol_or_eof(): - value = token.value - if value[-1] == 'm': - value = value[0 : -1] - size = float(value) * 100.0 # m -> cm - token = tok.get().unescape() - if not token.is_eol_or_eof(): - value = token.value - if value[-1] == 'm': - value = value[0 : -1] - hprec = float(value) * 100.0 # m -> cm - token = tok.get().unescape() - if not token.is_eol_or_eof(): - value = token.value - if value[-1] == 'm': - value = value[0 : -1] - vprec = float(value) * 100.0 # m -> cm - tok.get_eol() - - return cls(rdclass, rdtype, latitude, longitude, altitude, - size, hprec, vprec) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - if self.latitude[0] < 0: - sign = -1 - degrees = long(-1 * self.latitude[0]) - else: - sign = 1 - degrees = long(self.latitude[0]) - milliseconds = (degrees * 3600000 + - self.latitude[1] * 60000 + - self.latitude[2] * 1000 + - self.latitude[3]) * sign - latitude = 0x80000000L + milliseconds - if self.longitude[0] < 0: - sign = -1 - degrees = long(-1 * self.longitude[0]) - else: - sign = 1 - degrees = long(self.longitude[0]) - milliseconds = (degrees * 3600000 + - self.longitude[1] * 60000 + - self.longitude[2] * 1000 + - self.longitude[3]) * sign - longitude = 0x80000000L + milliseconds - altitude = long(self.altitude) + 10000000L - size = _encode_size(self.size, "size") - hprec = _encode_size(self.horizontal_precision, "horizontal precision") - vprec = _encode_size(self.vertical_precision, "vertical precision") - wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude, - longitude, altitude) - file.write(wire) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (version, size, hprec, vprec, latitude, longitude, altitude) = \ - struct.unpack("!BBBBIII", wire[current : current + rdlen]) - if latitude > 0x80000000L: - latitude = float(latitude - 0x80000000L) / 3600000 - else: - latitude = -1 * float(0x80000000L - latitude) / 3600000 - if latitude < -90.0 or latitude > 90.0: - raise dns.exception.FormError("bad latitude") - if longitude > 0x80000000L: - longitude = float(longitude - 0x80000000L) / 3600000 - else: - longitude = -1 * float(0x80000000L - longitude) / 3600000 - if longitude < -180.0 or longitude > 180.0: - raise dns.exception.FormError("bad longitude") - altitude = float(altitude) - 10000000.0 - size = _decode_size(size, "size") - hprec = _decode_size(hprec, "horizontal precision") - vprec = _decode_size(vprec, "vertical precision") - return cls(rdclass, rdtype, latitude, longitude, altitude, - size, hprec, vprec) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - f = cStringIO.StringIO() - self.to_wire(f) - wire1 = f.getvalue() - f.seek(0) - f.truncate() - other.to_wire(f) - wire2 = f.getvalue() - f.close() - - return cmp(wire1, wire2) - - def _get_float_latitude(self): - return _tuple_to_float(self.latitude) - - def _set_float_latitude(self, value): - self.latitude = _float_to_tuple(value) - - float_latitude = property(_get_float_latitude, _set_float_latitude, - doc="latitude as a floating point value") - - def _get_float_longitude(self): - return _tuple_to_float(self.longitude) - - def _set_float_longitude(self, value): - self.longitude = _float_to_tuple(value) - - float_longitude = property(_get_float_longitude, _set_float_longitude, - doc="longitude as a floating point value") diff --git a/lib/dnspython/dns/rdtypes/ANY/MX.py b/lib/dnspython/dns/rdtypes/ANY/MX.py deleted file mode 100644 index 92f41532a7..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/MX.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.mxbase - -class MX(dns.rdtypes.mxbase.MXBase): - """MX record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/NS.py b/lib/dnspython/dns/rdtypes/ANY/NS.py deleted file mode 100644 index 6b45d4d4b6..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/NS.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.nsbase - -class NS(dns.rdtypes.nsbase.NSBase): - """NS record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/NSEC.py b/lib/dnspython/dns/rdtypes/ANY/NSEC.py deleted file mode 100644 index ad113a4b59..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/NSEC.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO - -import dns.exception -import dns.rdata -import dns.rdatatype -import dns.name - -class NSEC(dns.rdata.Rdata): - """NSEC record - - @ivar next: the next name - @type next: dns.name.Name object - @ivar windows: the windowed bitmap list - @type windows: list of (window number, string) tuples""" - - __slots__ = ['next', 'windows'] - - def __init__(self, rdclass, rdtype, next, windows): - super(NSEC, self).__init__(rdclass, rdtype) - self.next = next - self.windows = windows - - def to_text(self, origin=None, relativize=True, **kw): - next = self.next.choose_relativity(origin, relativize) - text = '' - for (window, bitmap) in self.windows: - bits = [] - for i in xrange(0, len(bitmap)): - byte = ord(bitmap[i]) - for j in xrange(0, 8): - if byte & (0x80 >> j): - bits.append(dns.rdatatype.to_text(window * 256 + \ - i * 8 + j)) - text += (' ' + ' '.join(bits)) - return '%s%s' % (next, text) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - next = tok.get_name() - next = next.choose_relativity(origin, relativize) - rdtypes = [] - while 1: - token = tok.get().unescape() - if token.is_eol_or_eof(): - break - nrdtype = dns.rdatatype.from_text(token.value) - if nrdtype == 0: - raise dns.exception.SyntaxError("NSEC with bit 0") - if nrdtype > 65535: - raise dns.exception.SyntaxError("NSEC with bit > 65535") - rdtypes.append(nrdtype) - rdtypes.sort() - window = 0 - octets = 0 - prior_rdtype = 0 - bitmap = ['\0'] * 32 - windows = [] - for nrdtype in rdtypes: - if nrdtype == prior_rdtype: - continue - prior_rdtype = nrdtype - new_window = nrdtype // 256 - if new_window != window: - windows.append((window, ''.join(bitmap[0:octets]))) - bitmap = ['\0'] * 32 - window = new_window - offset = nrdtype % 256 - byte = offset // 8 - bit = offset % 8 - octets = byte + 1 - bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) - windows.append((window, ''.join(bitmap[0:octets]))) - return cls(rdclass, rdtype, next, windows) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - self.next.to_wire(file, None, origin) - for (window, bitmap) in self.windows: - file.write(chr(window)) - file.write(chr(len(bitmap))) - file.write(bitmap) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (next, cused) = dns.name.from_wire(wire[: current + rdlen], current) - current += cused - rdlen -= cused - windows = [] - while rdlen > 0: - if rdlen < 3: - raise dns.exception.FormError("NSEC too short") - window = ord(wire[current]) - octets = ord(wire[current + 1]) - if octets == 0 or octets > 32: - raise dns.exception.FormError("bad NSEC octets") - current += 2 - rdlen -= 2 - if rdlen < octets: - raise dns.exception.FormError("bad NSEC bitmap length") - bitmap = wire[current : current + octets].unwrap() - current += octets - rdlen -= octets - windows.append((window, bitmap)) - if not origin is None: - next = next.relativize(origin) - return cls(rdclass, rdtype, next, windows) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.next = self.next.choose_relativity(origin, relativize) - - def _cmp(self, other): - return self._wire_cmp(other) diff --git a/lib/dnspython/dns/rdtypes/ANY/NSEC3.py b/lib/dnspython/dns/rdtypes/ANY/NSEC3.py deleted file mode 100644 index c7ac7375b4..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/NSEC3.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import cStringIO -import string -import struct - -import dns.exception -import dns.rdata -import dns.rdatatype - -b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV', - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') -b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', - '0123456789ABCDEFGHIJKLMNOPQRSTUV') - -# hash algorithm constants -SHA1 = 1 - -# flag constants -OPTOUT = 1 - -class NSEC3(dns.rdata.Rdata): - """NSEC3 record - - @ivar algorithm: the hash algorithm number - @type algorithm: int - @ivar flags: the flags - @type flags: int - @ivar iterations: the number of iterations - @type iterations: int - @ivar salt: the salt - @type salt: string - @ivar next: the next name hash - @type next: string - @ivar windows: the windowed bitmap list - @type windows: list of (window number, string) tuples""" - - __slots__ = ['algorithm', 'flags', 'iterations', 'salt', 'next', 'windows'] - - def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt, - next, windows): - super(NSEC3, self).__init__(rdclass, rdtype) - self.algorithm = algorithm - self.flags = flags - self.iterations = iterations - self.salt = salt - self.next = next - self.windows = windows - - def to_text(self, origin=None, relativize=True, **kw): - next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower() - if self.salt == '': - salt = '-' - else: - salt = self.salt.encode('hex-codec') - text = '' - for (window, bitmap) in self.windows: - bits = [] - for i in xrange(0, len(bitmap)): - byte = ord(bitmap[i]) - for j in xrange(0, 8): - if byte & (0x80 >> j): - bits.append(dns.rdatatype.to_text(window * 256 + \ - i * 8 + j)) - text += (' ' + ' '.join(bits)) - return '%u %u %u %s %s%s' % (self.algorithm, self.flags, self.iterations, - salt, next, text) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - algorithm = tok.get_uint8() - flags = tok.get_uint8() - iterations = tok.get_uint16() - salt = tok.get_string() - if salt == '-': - salt = '' - else: - salt = salt.decode('hex-codec') - next = tok.get_string().upper().translate(b32_hex_to_normal) - next = base64.b32decode(next) - rdtypes = [] - while 1: - token = tok.get().unescape() - if token.is_eol_or_eof(): - break - nrdtype = dns.rdatatype.from_text(token.value) - if nrdtype == 0: - raise dns.exception.SyntaxError("NSEC3 with bit 0") - if nrdtype > 65535: - raise dns.exception.SyntaxError("NSEC3 with bit > 65535") - rdtypes.append(nrdtype) - rdtypes.sort() - window = 0 - octets = 0 - prior_rdtype = 0 - bitmap = ['\0'] * 32 - windows = [] - for nrdtype in rdtypes: - if nrdtype == prior_rdtype: - continue - prior_rdtype = nrdtype - new_window = nrdtype // 256 - if new_window != window: - windows.append((window, ''.join(bitmap[0:octets]))) - bitmap = ['\0'] * 32 - window = new_window - offset = nrdtype % 256 - byte = offset // 8 - bit = offset % 8 - octets = byte + 1 - bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) - windows.append((window, ''.join(bitmap[0:octets]))) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - l = len(self.salt) - file.write(struct.pack("!BBHB", self.algorithm, self.flags, - self.iterations, l)) - file.write(self.salt) - l = len(self.next) - file.write(struct.pack("!B", l)) - file.write(self.next) - for (window, bitmap) in self.windows: - file.write(chr(window)) - file.write(chr(len(bitmap))) - file.write(bitmap) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (algorithm, flags, iterations, slen) = struct.unpack('!BBHB', - wire[current : current + 5]) - current += 5 - rdlen -= 5 - salt = wire[current : current + slen].unwrap() - current += slen - rdlen -= slen - (nlen, ) = struct.unpack('!B', wire[current]) - current += 1 - rdlen -= 1 - next = wire[current : current + nlen].unwrap() - current += nlen - rdlen -= nlen - windows = [] - while rdlen > 0: - if rdlen < 3: - raise dns.exception.FormError("NSEC3 too short") - window = ord(wire[current]) - octets = ord(wire[current + 1]) - if octets == 0 or octets > 32: - raise dns.exception.FormError("bad NSEC3 octets") - current += 2 - rdlen -= 2 - if rdlen < octets: - raise dns.exception.FormError("bad NSEC3 bitmap length") - bitmap = wire[current : current + octets].unwrap() - current += octets - rdlen -= octets - windows.append((window, bitmap)) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - b1 = cStringIO.StringIO() - self.to_wire(b1) - b2 = cStringIO.StringIO() - other.to_wire(b2) - return cmp(b1.getvalue(), b2.getvalue()) diff --git a/lib/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py b/lib/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py deleted file mode 100644 index 4e687825ad..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import struct - -import dns.exception -import dns.rdata - -class NSEC3PARAM(dns.rdata.Rdata): - """NSEC3PARAM record - - @ivar algorithm: the hash algorithm number - @type algorithm: int - @ivar flags: the flags - @type flags: int - @ivar iterations: the number of iterations - @type iterations: int - @ivar salt: the salt - @type salt: string""" - - __slots__ = ['algorithm', 'flags', 'iterations', 'salt'] - - def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt): - super(NSEC3PARAM, self).__init__(rdclass, rdtype) - self.algorithm = algorithm - self.flags = flags - self.iterations = iterations - self.salt = salt - - def to_text(self, origin=None, relativize=True, **kw): - if self.salt == '': - salt = '-' - else: - salt = self.salt.encode('hex-codec') - return '%u %u %u %s' % (self.algorithm, self.flags, self.iterations, salt) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - algorithm = tok.get_uint8() - flags = tok.get_uint8() - iterations = tok.get_uint16() - salt = tok.get_string() - if salt == '-': - salt = '' - else: - salt = salt.decode('hex-codec') - return cls(rdclass, rdtype, algorithm, flags, iterations, salt) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - l = len(self.salt) - file.write(struct.pack("!BBHB", self.algorithm, self.flags, - self.iterations, l)) - file.write(self.salt) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (algorithm, flags, iterations, slen) = struct.unpack('!BBHB', - wire[current : current + 5]) - current += 5 - rdlen -= 5 - salt = wire[current : current + slen].unwrap() - current += slen - rdlen -= slen - if rdlen != 0: - raise dns.exception.FormError - return cls(rdclass, rdtype, algorithm, flags, iterations, salt) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - b1 = cStringIO.StringIO() - self.to_wire(b1) - b2 = cStringIO.StringIO() - other.to_wire(b2) - return cmp(b1.getvalue(), b2.getvalue()) diff --git a/lib/dnspython/dns/rdtypes/ANY/PTR.py b/lib/dnspython/dns/rdtypes/ANY/PTR.py deleted file mode 100644 index 4a037537b6..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/PTR.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.nsbase - -class PTR(dns.rdtypes.nsbase.NSBase): - """PTR record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/RP.py b/lib/dnspython/dns/rdtypes/ANY/RP.py deleted file mode 100644 index 26c55314e1..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/RP.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.rdata -import dns.name - -class RP(dns.rdata.Rdata): - """RP record - - @ivar mbox: The responsible person's mailbox - @type mbox: dns.name.Name object - @ivar txt: The owner name of a node with TXT records, or the root name - if no TXT records are associated with this RP. - @type txt: dns.name.Name object - @see: RFC 1183""" - - __slots__ = ['mbox', 'txt'] - - def __init__(self, rdclass, rdtype, mbox, txt): - super(RP, self).__init__(rdclass, rdtype) - self.mbox = mbox - self.txt = txt - - def to_text(self, origin=None, relativize=True, **kw): - mbox = self.mbox.choose_relativity(origin, relativize) - txt = self.txt.choose_relativity(origin, relativize) - return "%s %s" % (str(mbox), str(txt)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - mbox = tok.get_name() - txt = tok.get_name() - mbox = mbox.choose_relativity(origin, relativize) - txt = txt.choose_relativity(origin, relativize) - tok.get_eol() - return cls(rdclass, rdtype, mbox, txt) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - self.mbox.to_wire(file, None, origin) - self.txt.to_wire(file, None, origin) - - def to_digestable(self, origin = None): - return self.mbox.to_digestable(origin) + \ - self.txt.to_digestable(origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (mbox, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - current += cused - rdlen -= cused - if rdlen <= 0: - raise dns.exception.FormError - (txt, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused != rdlen: - raise dns.exception.FormError - if not origin is None: - mbox = mbox.relativize(origin) - txt = txt.relativize(origin) - return cls(rdclass, rdtype, mbox, txt) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.mbox = self.mbox.choose_relativity(origin, relativize) - self.txt = self.txt.choose_relativity(origin, relativize) - - def _cmp(self, other): - v = cmp(self.mbox, other.mbox) - if v == 0: - v = cmp(self.txt, other.txt) - return v diff --git a/lib/dnspython/dns/rdtypes/ANY/RRSIG.py b/lib/dnspython/dns/rdtypes/ANY/RRSIG.py deleted file mode 100644 index 63d389cd7a..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/RRSIG.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import calendar -import struct -import time - -import dns.dnssec -import dns.exception -import dns.rdata -import dns.rdatatype - -class BadSigTime(dns.exception.DNSException): - """Raised when a SIG or RRSIG RR's time cannot be parsed.""" - pass - -def sigtime_to_posixtime(what): - if len(what) != 14: - raise BadSigTime - year = int(what[0:4]) - month = int(what[4:6]) - day = int(what[6:8]) - hour = int(what[8:10]) - minute = int(what[10:12]) - second = int(what[12:14]) - return calendar.timegm((year, month, day, hour, minute, second, - 0, 0, 0)) - -def posixtime_to_sigtime(what): - return time.strftime('%Y%m%d%H%M%S', time.gmtime(what)) - -class RRSIG(dns.rdata.Rdata): - """RRSIG record - - @ivar type_covered: the rdata type this signature covers - @type type_covered: int - @ivar algorithm: the algorithm used for the sig - @type algorithm: int - @ivar labels: number of labels - @type labels: int - @ivar original_ttl: the original TTL - @type original_ttl: long - @ivar expiration: signature expiration time - @type expiration: long - @ivar inception: signature inception time - @type inception: long - @ivar key_tag: the key tag - @type key_tag: int - @ivar signer: the signer - @type signer: dns.name.Name object - @ivar signature: the signature - @type signature: string""" - - __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl', - 'expiration', 'inception', 'key_tag', 'signer', - 'signature'] - - def __init__(self, rdclass, rdtype, type_covered, algorithm, labels, - original_ttl, expiration, inception, key_tag, signer, - signature): - super(RRSIG, self).__init__(rdclass, rdtype) - self.type_covered = type_covered - self.algorithm = algorithm - self.labels = labels - self.original_ttl = original_ttl - self.expiration = expiration - self.inception = inception - self.key_tag = key_tag - self.signer = signer - self.signature = signature - - def covers(self): - return self.type_covered - - def to_text(self, origin=None, relativize=True, **kw): - return '%s %d %d %d %s %s %d %s %s' % ( - dns.rdatatype.to_text(self.type_covered), - self.algorithm, - self.labels, - self.original_ttl, - posixtime_to_sigtime(self.expiration), - posixtime_to_sigtime(self.inception), - self.key_tag, - self.signer, - dns.rdata._base64ify(self.signature) - ) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - type_covered = dns.rdatatype.from_text(tok.get_string()) - algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) - labels = tok.get_int() - original_ttl = tok.get_ttl() - expiration = sigtime_to_posixtime(tok.get_string()) - inception = sigtime_to_posixtime(tok.get_string()) - key_tag = tok.get_int() - signer = tok.get_name() - signer = signer.choose_relativity(origin, relativize) - chunks = [] - while 1: - t = tok.get().unescape() - if t.is_eol_or_eof(): - break - if not t.is_identifier(): - raise dns.exception.SyntaxError - chunks.append(t.value) - b64 = ''.join(chunks) - signature = b64.decode('base64_codec') - return cls(rdclass, rdtype, type_covered, algorithm, labels, - original_ttl, expiration, inception, key_tag, signer, - signature) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - header = struct.pack('!HBBIIIH', self.type_covered, - self.algorithm, self.labels, - self.original_ttl, self.expiration, - self.inception, self.key_tag) - file.write(header) - self.signer.to_wire(file, None, origin) - file.write(self.signature) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - header = struct.unpack('!HBBIIIH', wire[current : current + 18]) - current += 18 - rdlen -= 18 - (signer, cused) = dns.name.from_wire(wire[: current + rdlen], current) - current += cused - rdlen -= cused - if not origin is None: - signer = signer.relativize(origin) - signature = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, header[0], header[1], header[2], - header[3], header[4], header[5], header[6], signer, - signature) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.signer = self.signer.choose_relativity(origin, relativize) - - def _cmp(self, other): - return self._wire_cmp(other) diff --git a/lib/dnspython/dns/rdtypes/ANY/RT.py b/lib/dnspython/dns/rdtypes/ANY/RT.py deleted file mode 100644 index f9653fdb34..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/RT.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.mxbase - -class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """RT record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/SOA.py b/lib/dnspython/dns/rdtypes/ANY/SOA.py deleted file mode 100644 index 2d6f21b56b..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/SOA.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.rdata -import dns.name - -class SOA(dns.rdata.Rdata): - """SOA record - - @ivar mname: the SOA MNAME (master name) field - @type mname: dns.name.Name object - @ivar rname: the SOA RNAME (responsible name) field - @type rname: dns.name.Name object - @ivar serial: The zone's serial number - @type serial: int - @ivar refresh: The zone's refresh value (in seconds) - @type refresh: int - @ivar retry: The zone's retry value (in seconds) - @type retry: int - @ivar expire: The zone's expiration value (in seconds) - @type expire: int - @ivar minimum: The zone's negative caching time (in seconds, called - "minimum" for historical reasons) - @type minimum: int - @see: RFC 1035""" - - __slots__ = ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire', - 'minimum'] - - def __init__(self, rdclass, rdtype, mname, rname, serial, refresh, retry, - expire, minimum): - super(SOA, self).__init__(rdclass, rdtype) - self.mname = mname - self.rname = rname - self.serial = serial - self.refresh = refresh - self.retry = retry - self.expire = expire - self.minimum = minimum - - def to_text(self, origin=None, relativize=True, **kw): - mname = self.mname.choose_relativity(origin, relativize) - rname = self.rname.choose_relativity(origin, relativize) - return '%s %s %d %d %d %d %d' % ( - mname, rname, self.serial, self.refresh, self.retry, - self.expire, self.minimum ) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - mname = tok.get_name() - rname = tok.get_name() - mname = mname.choose_relativity(origin, relativize) - rname = rname.choose_relativity(origin, relativize) - serial = tok.get_uint32() - refresh = tok.get_ttl() - retry = tok.get_ttl() - expire = tok.get_ttl() - minimum = tok.get_ttl() - tok.get_eol() - return cls(rdclass, rdtype, mname, rname, serial, refresh, retry, - expire, minimum ) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - self.mname.to_wire(file, compress, origin) - self.rname.to_wire(file, compress, origin) - five_ints = struct.pack('!IIIII', self.serial, self.refresh, - self.retry, self.expire, self.minimum) - file.write(five_ints) - - def to_digestable(self, origin = None): - return self.mname.to_digestable(origin) + \ - self.rname.to_digestable(origin) + \ - struct.pack('!IIIII', self.serial, self.refresh, - self.retry, self.expire, self.minimum) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (mname, cused) = dns.name.from_wire(wire[: current + rdlen], current) - current += cused - rdlen -= cused - (rname, cused) = dns.name.from_wire(wire[: current + rdlen], current) - current += cused - rdlen -= cused - if rdlen != 20: - raise dns.exception.FormError - five_ints = struct.unpack('!IIIII', - wire[current : current + rdlen]) - if not origin is None: - mname = mname.relativize(origin) - rname = rname.relativize(origin) - return cls(rdclass, rdtype, mname, rname, - five_ints[0], five_ints[1], five_ints[2], five_ints[3], - five_ints[4]) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.mname = self.mname.choose_relativity(origin, relativize) - self.rname = self.rname.choose_relativity(origin, relativize) - - def _cmp(self, other): - v = cmp(self.mname, other.mname) - if v == 0: - v = cmp(self.rname, other.rname) - if v == 0: - self_ints = struct.pack('!IIIII', self.serial, self.refresh, - self.retry, self.expire, self.minimum) - other_ints = struct.pack('!IIIII', other.serial, other.refresh, - other.retry, other.expire, - other.minimum) - v = cmp(self_ints, other_ints) - return v diff --git a/lib/dnspython/dns/rdtypes/ANY/SPF.py b/lib/dnspython/dns/rdtypes/ANY/SPF.py deleted file mode 100644 index 8860dd72bb..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/SPF.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.txtbase - -class SPF(dns.rdtypes.txtbase.TXTBase): - """SPF record - - @see: RFC 4408""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/SSHFP.py b/lib/dnspython/dns/rdtypes/ANY/SSHFP.py deleted file mode 100644 index cec650a570..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/SSHFP.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.rdata -import dns.rdatatype - -class SSHFP(dns.rdata.Rdata): - """SSHFP record - - @ivar algorithm: the algorithm - @type algorithm: int - @ivar fp_type: the digest type - @type fp_type: int - @ivar fingerprint: the fingerprint - @type fingerprint: string - @see: draft-ietf-secsh-dns-05.txt""" - - __slots__ = ['algorithm', 'fp_type', 'fingerprint'] - - def __init__(self, rdclass, rdtype, algorithm, fp_type, - fingerprint): - super(SSHFP, self).__init__(rdclass, rdtype) - self.algorithm = algorithm - self.fp_type = fp_type - self.fingerprint = fingerprint - - def to_text(self, origin=None, relativize=True, **kw): - return '%d %d %s' % (self.algorithm, - self.fp_type, - dns.rdata._hexify(self.fingerprint, - chunksize=128)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - algorithm = tok.get_uint8() - fp_type = tok.get_uint8() - fingerprint = tok.get_string() - fingerprint = fingerprint.decode('hex_codec') - tok.get_eol() - return cls(rdclass, rdtype, algorithm, fp_type, fingerprint) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - header = struct.pack("!BB", self.algorithm, self.fp_type) - file.write(header) - file.write(self.fingerprint) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - header = struct.unpack("!BB", wire[current : current + 2]) - current += 2 - rdlen -= 2 - fingerprint = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, header[0], header[1], fingerprint) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - hs = struct.pack("!BB", self.algorithm, self.fp_type) - ho = struct.pack("!BB", other.algorithm, other.fp_type) - v = cmp(hs, ho) - if v == 0: - v = cmp(self.fingerprint, other.fingerprint) - return v diff --git a/lib/dnspython/dns/rdtypes/ANY/TXT.py b/lib/dnspython/dns/rdtypes/ANY/TXT.py deleted file mode 100644 index 604fd0f4de..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/TXT.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.txtbase - -class TXT(dns.rdtypes.txtbase.TXTBase): - """TXT record""" - pass diff --git a/lib/dnspython/dns/rdtypes/ANY/X25.py b/lib/dnspython/dns/rdtypes/ANY/X25.py deleted file mode 100644 index ae91295f8c..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/X25.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.rdata -import dns.tokenizer - -class X25(dns.rdata.Rdata): - """X25 record - - @ivar address: the PSDN address - @type address: string - @see: RFC 1183""" - - __slots__ = ['address'] - - def __init__(self, rdclass, rdtype, address): - super(X25, self).__init__(rdclass, rdtype) - self.address = address - - def to_text(self, origin=None, relativize=True, **kw): - return '"%s"' % dns.rdata._escapify(self.address) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - address = tok.get_string() - tok.get_eol() - return cls(rdclass, rdtype, address) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - l = len(self.address) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(self.address) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l != rdlen: - raise dns.exception.FormError - address = wire[current : current + l].unwrap() - return cls(rdclass, rdtype, address) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - return cmp(self.address, other.address) diff --git a/lib/dnspython/dns/rdtypes/ANY/__init__.py b/lib/dnspython/dns/rdtypes/ANY/__init__.py deleted file mode 100644 index 721e9dd60b..0000000000 --- a/lib/dnspython/dns/rdtypes/ANY/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class ANY (generic) rdata type classes.""" - -__all__ = [ - 'AFSDB', - 'CERT', - 'CNAME', - 'DLV', - 'DNAME', - 'DNSKEY', - 'DS', - 'GPOS', - 'HINFO', - 'HIP', - 'ISDN', - 'LOC', - 'MX', - 'NS', - 'NSEC', - 'NSEC3', - 'NSEC3PARAM', - 'PTR', - 'RP', - 'RRSIG', - 'RT', - 'SOA', - 'SPF', - 'SSHFP', - 'TXT', - 'X25', -] diff --git a/lib/dnspython/dns/rdtypes/IN/A.py b/lib/dnspython/dns/rdtypes/IN/A.py deleted file mode 100644 index 372d333226..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/A.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.ipv4 -import dns.rdata -import dns.tokenizer - -class A(dns.rdata.Rdata): - """A record. - - @ivar address: an IPv4 address - @type address: string (in the standard "dotted quad" format)""" - - __slots__ = ['address'] - - def __init__(self, rdclass, rdtype, address): - super(A, self).__init__(rdclass, rdtype) - # check that it's OK - junk = dns.ipv4.inet_aton(address) - self.address = address - - def to_text(self, origin=None, relativize=True, **kw): - return self.address - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - address = tok.get_identifier() - tok.get_eol() - return cls(rdclass, rdtype, address) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - file.write(dns.ipv4.inet_aton(self.address)) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - address = dns.ipv4.inet_ntoa(wire[current : current + rdlen]) - return cls(rdclass, rdtype, address) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - sa = dns.ipv4.inet_aton(self.address) - oa = dns.ipv4.inet_aton(other.address) - return cmp(sa, oa) diff --git a/lib/dnspython/dns/rdtypes/IN/AAAA.py b/lib/dnspython/dns/rdtypes/IN/AAAA.py deleted file mode 100644 index e131bd5070..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/AAAA.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.inet -import dns.rdata -import dns.tokenizer - -class AAAA(dns.rdata.Rdata): - """AAAA record. - - @ivar address: an IPv6 address - @type address: string (in the standard IPv6 format)""" - - __slots__ = ['address'] - - def __init__(self, rdclass, rdtype, address): - super(AAAA, self).__init__(rdclass, rdtype) - # check that it's OK - junk = dns.inet.inet_pton(dns.inet.AF_INET6, address) - self.address = address - - def to_text(self, origin=None, relativize=True, **kw): - return self.address - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - address = tok.get_identifier() - tok.get_eol() - return cls(rdclass, rdtype, address) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address)) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - address = dns.inet.inet_ntop(dns.inet.AF_INET6, - wire[current : current + rdlen]) - return cls(rdclass, rdtype, address) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - sa = dns.inet.inet_pton(dns.inet.AF_INET6, self.address) - oa = dns.inet.inet_pton(dns.inet.AF_INET6, other.address) - return cmp(sa, oa) diff --git a/lib/dnspython/dns/rdtypes/IN/APL.py b/lib/dnspython/dns/rdtypes/IN/APL.py deleted file mode 100644 index 260fd6f39f..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/APL.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import struct - -import dns.exception -import dns.inet -import dns.rdata -import dns.tokenizer - -class APLItem(object): - """An APL list item. - - @ivar family: the address family (IANA address family registry) - @type family: int - @ivar negation: is this item negated? - @type negation: bool - @ivar address: the address - @type address: string - @ivar prefix: the prefix length - @type prefix: int - """ - - __slots__ = ['family', 'negation', 'address', 'prefix'] - - def __init__(self, family, negation, address, prefix): - self.family = family - self.negation = negation - self.address = address - self.prefix = prefix - - def __str__(self): - if self.negation: - return "!%d:%s/%s" % (self.family, self.address, self.prefix) - else: - return "%d:%s/%s" % (self.family, self.address, self.prefix) - - def to_wire(self, file): - if self.family == 1: - address = dns.inet.inet_pton(dns.inet.AF_INET, self.address) - elif self.family == 2: - address = dns.inet.inet_pton(dns.inet.AF_INET6, self.address) - else: - address = self.address.decode('hex_codec') - # - # Truncate least significant zero bytes. - # - last = 0 - for i in xrange(len(address) - 1, -1, -1): - if address[i] != chr(0): - last = i + 1 - break - address = address[0 : last] - l = len(address) - assert l < 128 - if self.negation: - l |= 0x80 - header = struct.pack('!HBB', self.family, self.prefix, l) - file.write(header) - file.write(address) - -class APL(dns.rdata.Rdata): - """APL record. - - @ivar items: a list of APL items - @type items: list of APL_Item - @see: RFC 3123""" - - __slots__ = ['items'] - - def __init__(self, rdclass, rdtype, items): - super(APL, self).__init__(rdclass, rdtype) - self.items = items - - def to_text(self, origin=None, relativize=True, **kw): - return ' '.join(map(lambda x: str(x), self.items)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - items = [] - while 1: - token = tok.get().unescape() - if token.is_eol_or_eof(): - break - item = token.value - if item[0] == '!': - negation = True - item = item[1:] - else: - negation = False - (family, rest) = item.split(':', 1) - family = int(family) - (address, prefix) = rest.split('/', 1) - prefix = int(prefix) - item = APLItem(family, negation, address, prefix) - items.append(item) - - return cls(rdclass, rdtype, items) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - for item in self.items: - item.to_wire(file) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - items = [] - while 1: - if rdlen < 4: - raise dns.exception.FormError - header = struct.unpack('!HBB', wire[current : current + 4]) - afdlen = header[2] - if afdlen > 127: - negation = True - afdlen -= 128 - else: - negation = False - current += 4 - rdlen -= 4 - if rdlen < afdlen: - raise dns.exception.FormError - address = wire[current : current + afdlen].unwrap() - l = len(address) - if header[0] == 1: - if l < 4: - address += '\x00' * (4 - l) - address = dns.inet.inet_ntop(dns.inet.AF_INET, address) - elif header[0] == 2: - if l < 16: - address += '\x00' * (16 - l) - address = dns.inet.inet_ntop(dns.inet.AF_INET6, address) - else: - # - # This isn't really right according to the RFC, but it - # seems better than throwing an exception - # - address = address.encode('hex_codec') - current += afdlen - rdlen -= afdlen - item = APLItem(header[0], negation, address, header[1]) - items.append(item) - if rdlen == 0: - break - return cls(rdclass, rdtype, items) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - f = cStringIO.StringIO() - self.to_wire(f) - wire1 = f.getvalue() - f.seek(0) - f.truncate() - other.to_wire(f) - wire2 = f.getvalue() - f.close() - - return cmp(wire1, wire2) diff --git a/lib/dnspython/dns/rdtypes/IN/DHCID.py b/lib/dnspython/dns/rdtypes/IN/DHCID.py deleted file mode 100644 index 5524beadd1..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/DHCID.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception - -class DHCID(dns.rdata.Rdata): - """DHCID record - - @ivar data: the data (the content of the RR is opaque as far as the - DNS is concerned) - @type data: string - @see: RFC 4701""" - - __slots__ = ['data'] - - def __init__(self, rdclass, rdtype, data): - super(DHCID, self).__init__(rdclass, rdtype) - self.data = data - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._base64ify(self.data) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - chunks = [] - while 1: - t = tok.get().unescape() - if t.is_eol_or_eof(): - break - if not t.is_identifier(): - raise dns.exception.SyntaxError - chunks.append(t.value) - b64 = ''.join(chunks) - data = b64.decode('base64_codec') - return cls(rdclass, rdtype, data) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - file.write(self.data) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - data = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, data) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - return cmp(self.data, other.data) diff --git a/lib/dnspython/dns/rdtypes/IN/IPSECKEY.py b/lib/dnspython/dns/rdtypes/IN/IPSECKEY.py deleted file mode 100644 index d85b6fe993..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/IPSECKEY.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import struct - -import dns.exception -import dns.inet -import dns.name - -class IPSECKEY(dns.rdata.Rdata): - """IPSECKEY record - - @ivar precedence: the precedence for this key data - @type precedence: int - @ivar gateway_type: the gateway type - @type gateway_type: int - @ivar algorithm: the algorithm to use - @type algorithm: int - @ivar gateway: the public key - @type gateway: None, IPv4 address, IPV6 address, or domain name - @ivar key: the public key - @type key: string - @see: RFC 4025""" - - __slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key'] - - def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm, - gateway, key): - super(IPSECKEY, self).__init__(rdclass, rdtype) - if gateway_type == 0: - if gateway != '.' and not gateway is None: - raise SyntaxError('invalid gateway for gateway type 0') - gateway = None - elif gateway_type == 1: - # check that it's OK - junk = dns.inet.inet_pton(dns.inet.AF_INET, gateway) - elif gateway_type == 2: - # check that it's OK - junk = dns.inet.inet_pton(dns.inet.AF_INET6, gateway) - elif gateway_type == 3: - pass - else: - raise SyntaxError('invalid IPSECKEY gateway type: %d' % gateway_type) - self.precedence = precedence - self.gateway_type = gateway_type - self.algorithm = algorithm - self.gateway = gateway - self.key = key - - def to_text(self, origin=None, relativize=True, **kw): - if self.gateway_type == 0: - gateway = '.' - elif self.gateway_type == 1: - gateway = self.gateway - elif self.gateway_type == 2: - gateway = self.gateway - elif self.gateway_type == 3: - gateway = str(self.gateway.choose_relativity(origin, relativize)) - else: - raise ValueError('invalid gateway type') - return '%d %d %d %s %s' % (self.precedence, self.gateway_type, - self.algorithm, gateway, - dns.rdata._base64ify(self.key)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - precedence = tok.get_uint8() - gateway_type = tok.get_uint8() - algorithm = tok.get_uint8() - if gateway_type == 3: - gateway = tok.get_name().choose_relativity(origin, relativize) - else: - gateway = tok.get_string() - chunks = [] - while 1: - t = tok.get().unescape() - if t.is_eol_or_eof(): - break - if not t.is_identifier(): - raise dns.exception.SyntaxError - chunks.append(t.value) - b64 = ''.join(chunks) - key = b64.decode('base64_codec') - return cls(rdclass, rdtype, precedence, gateway_type, algorithm, - gateway, key) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - header = struct.pack("!BBB", self.precedence, self.gateway_type, - self.algorithm) - file.write(header) - if self.gateway_type == 0: - pass - elif self.gateway_type == 1: - file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway)) - elif self.gateway_type == 2: - file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway)) - elif self.gateway_type == 3: - self.gateway.to_wire(file, None, origin) - else: - raise ValueError('invalid gateway type') - file.write(self.key) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - if rdlen < 3: - raise dns.exception.FormError - header = struct.unpack('!BBB', wire[current : current + 3]) - gateway_type = header[1] - current += 3 - rdlen -= 3 - if gateway_type == 0: - gateway = None - elif gateway_type == 1: - gateway = dns.inet.inet_ntop(dns.inet.AF_INET, - wire[current : current + 4]) - current += 4 - rdlen -= 4 - elif gateway_type == 2: - gateway = dns.inet.inet_ntop(dns.inet.AF_INET6, - wire[current : current + 16]) - current += 16 - rdlen -= 16 - elif gateway_type == 3: - (gateway, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - current += cused - rdlen -= cused - else: - raise dns.exception.FormError('invalid IPSECKEY gateway type') - key = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, header[0], gateway_type, header[2], - gateway, key) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - f = cStringIO.StringIO() - self.to_wire(f) - wire1 = f.getvalue() - f.seek(0) - f.truncate() - other.to_wire(f) - wire2 = f.getvalue() - f.close() - - return cmp(wire1, wire2) diff --git a/lib/dnspython/dns/rdtypes/IN/KX.py b/lib/dnspython/dns/rdtypes/IN/KX.py deleted file mode 100644 index c7bd5bbe65..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/KX.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.mxbase - -class KX(dns.rdtypes.mxbase.UncompressedMX): - """KX record""" - pass diff --git a/lib/dnspython/dns/rdtypes/IN/NAPTR.py b/lib/dnspython/dns/rdtypes/IN/NAPTR.py deleted file mode 100644 index 7fe043082b..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/NAPTR.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.name -import dns.rdata - -def _write_string(file, s): - l = len(s) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(s) - -class NAPTR(dns.rdata.Rdata): - """NAPTR record - - @ivar order: order - @type order: int - @ivar preference: preference - @type preference: int - @ivar flags: flags - @type flags: string - @ivar service: service - @type service: string - @ivar regexp: regular expression - @type regexp: string - @ivar replacement: replacement name - @type replacement: dns.name.Name object - @see: RFC 3403""" - - __slots__ = ['order', 'preference', 'flags', 'service', 'regexp', - 'replacement'] - - def __init__(self, rdclass, rdtype, order, preference, flags, service, - regexp, replacement): - super(NAPTR, self).__init__(rdclass, rdtype) - self.order = order - self.preference = preference - self.flags = flags - self.service = service - self.regexp = regexp - self.replacement = replacement - - def to_text(self, origin=None, relativize=True, **kw): - replacement = self.replacement.choose_relativity(origin, relativize) - return '%d %d "%s" "%s" "%s" %s' % \ - (self.order, self.preference, - dns.rdata._escapify(self.flags), - dns.rdata._escapify(self.service), - dns.rdata._escapify(self.regexp), - self.replacement) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - order = tok.get_uint16() - preference = tok.get_uint16() - flags = tok.get_string() - service = tok.get_string() - regexp = tok.get_string() - replacement = tok.get_name() - replacement = replacement.choose_relativity(origin, relativize) - tok.get_eol() - return cls(rdclass, rdtype, order, preference, flags, service, - regexp, replacement) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - two_ints = struct.pack("!HH", self.order, self.preference) - file.write(two_ints) - _write_string(file, self.flags) - _write_string(file, self.service) - _write_string(file, self.regexp) - self.replacement.to_wire(file, compress, origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (order, preference) = struct.unpack('!HH', wire[current : current + 4]) - current += 4 - rdlen -= 4 - strings = [] - for i in xrange(3): - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l > rdlen or rdlen < 0: - raise dns.exception.FormError - s = wire[current : current + l].unwrap() - current += l - rdlen -= l - strings.append(s) - (replacement, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused != rdlen: - raise dns.exception.FormError - if not origin is None: - replacement = replacement.relativize(origin) - return cls(rdclass, rdtype, order, preference, strings[0], strings[1], - strings[2], replacement) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.replacement = self.replacement.choose_relativity(origin, - relativize) - - def _cmp(self, other): - sp = struct.pack("!HH", self.order, self.preference) - op = struct.pack("!HH", other.order, other.preference) - v = cmp(sp, op) - if v == 0: - v = cmp(self.flags, other.flags) - if v == 0: - v = cmp(self.service, other.service) - if v == 0: - v = cmp(self.regexp, other.regexp) - if v == 0: - v = cmp(self.replacement, other.replacement) - return v diff --git a/lib/dnspython/dns/rdtypes/IN/NSAP.py b/lib/dnspython/dns/rdtypes/IN/NSAP.py deleted file mode 100644 index 216cb0a81a..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/NSAP.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.rdata -import dns.tokenizer - -class NSAP(dns.rdata.Rdata): - """NSAP record. - - @ivar address: a NASP - @type address: string - @see: RFC 1706""" - - __slots__ = ['address'] - - def __init__(self, rdclass, rdtype, address): - super(NSAP, self).__init__(rdclass, rdtype) - self.address = address - - def to_text(self, origin=None, relativize=True, **kw): - return "0x%s" % self.address.encode('hex_codec') - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - address = tok.get_string() - t = tok.get_eol() - if address[0:2] != '0x': - raise dns.exception.SyntaxError('string does not start with 0x') - address = address[2:].replace('.', '') - if len(address) % 2 != 0: - raise dns.exception.SyntaxError('hexstring has odd length') - address = address.decode('hex_codec') - return cls(rdclass, rdtype, address) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - file.write(self.address) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - address = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, address) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - return cmp(self.address, other.address) diff --git a/lib/dnspython/dns/rdtypes/IN/NSAP_PTR.py b/lib/dnspython/dns/rdtypes/IN/NSAP_PTR.py deleted file mode 100644 index df5b989ac8..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/NSAP_PTR.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.rdtypes.nsbase - -class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS): - """NSAP-PTR record""" - pass diff --git a/lib/dnspython/dns/rdtypes/IN/PX.py b/lib/dnspython/dns/rdtypes/IN/PX.py deleted file mode 100644 index 1422b83487..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/PX.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.rdata -import dns.name - -class PX(dns.rdata.Rdata): - """PX record. - - @ivar preference: the preference value - @type preference: int - @ivar map822: the map822 name - @type map822: dns.name.Name object - @ivar mapx400: the mapx400 name - @type mapx400: dns.name.Name object - @see: RFC 2163""" - - __slots__ = ['preference', 'map822', 'mapx400'] - - def __init__(self, rdclass, rdtype, preference, map822, mapx400): - super(PX, self).__init__(rdclass, rdtype) - self.preference = preference - self.map822 = map822 - self.mapx400 = mapx400 - - def to_text(self, origin=None, relativize=True, **kw): - map822 = self.map822.choose_relativity(origin, relativize) - mapx400 = self.mapx400.choose_relativity(origin, relativize) - return '%d %s %s' % (self.preference, map822, mapx400) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - preference = tok.get_uint16() - map822 = tok.get_name() - map822 = map822.choose_relativity(origin, relativize) - mapx400 = tok.get_name(None) - mapx400 = mapx400.choose_relativity(origin, relativize) - tok.get_eol() - return cls(rdclass, rdtype, preference, map822, mapx400) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - pref = struct.pack("!H", self.preference) - file.write(pref) - self.map822.to_wire(file, None, origin) - self.mapx400.to_wire(file, None, origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (preference, ) = struct.unpack('!H', wire[current : current + 2]) - current += 2 - rdlen -= 2 - (map822, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused > rdlen: - raise dns.exception.FormError - current += cused - rdlen -= cused - if not origin is None: - map822 = map822.relativize(origin) - (mapx400, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused != rdlen: - raise dns.exception.FormError - if not origin is None: - mapx400 = mapx400.relativize(origin) - return cls(rdclass, rdtype, preference, map822, mapx400) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.map822 = self.map822.choose_relativity(origin, relativize) - self.mapx400 = self.mapx400.choose_relativity(origin, relativize) - - def _cmp(self, other): - sp = struct.pack("!H", self.preference) - op = struct.pack("!H", other.preference) - v = cmp(sp, op) - if v == 0: - v = cmp(self.map822, other.map822) - if v == 0: - v = cmp(self.mapx400, other.mapx400) - return v diff --git a/lib/dnspython/dns/rdtypes/IN/SRV.py b/lib/dnspython/dns/rdtypes/IN/SRV.py deleted file mode 100644 index e101b26bea..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/SRV.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.rdata -import dns.name - -class SRV(dns.rdata.Rdata): - """SRV record - - @ivar priority: the priority - @type priority: int - @ivar weight: the weight - @type weight: int - @ivar port: the port of the service - @type port: int - @ivar target: the target host - @type target: dns.name.Name object - @see: RFC 2782""" - - __slots__ = ['priority', 'weight', 'port', 'target'] - - def __init__(self, rdclass, rdtype, priority, weight, port, target): - super(SRV, self).__init__(rdclass, rdtype) - self.priority = priority - self.weight = weight - self.port = port - self.target = target - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - return '%d %d %d %s' % (self.priority, self.weight, self.port, - target) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - priority = tok.get_uint16() - weight = tok.get_uint16() - port = tok.get_uint16() - target = tok.get_name(None) - target = target.choose_relativity(origin, relativize) - tok.get_eol() - return cls(rdclass, rdtype, priority, weight, port, target) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) - file.write(three_ints) - self.target.to_wire(file, compress, origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (priority, weight, port) = struct.unpack('!HHH', - wire[current : current + 6]) - current += 6 - rdlen -= 6 - (target, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused != rdlen: - raise dns.exception.FormError - if not origin is None: - target = target.relativize(origin) - return cls(rdclass, rdtype, priority, weight, port, target) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.target = self.target.choose_relativity(origin, relativize) - - def _cmp(self, other): - sp = struct.pack("!HHH", self.priority, self.weight, self.port) - op = struct.pack("!HHH", other.priority, other.weight, other.port) - v = cmp(sp, op) - if v == 0: - v = cmp(self.target, other.target) - return v diff --git a/lib/dnspython/dns/rdtypes/IN/WKS.py b/lib/dnspython/dns/rdtypes/IN/WKS.py deleted file mode 100644 index 04c3054e44..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/WKS.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import socket -import struct - -import dns.ipv4 -import dns.rdata - -_proto_tcp = socket.getprotobyname('tcp') -_proto_udp = socket.getprotobyname('udp') - -class WKS(dns.rdata.Rdata): - """WKS record - - @ivar address: the address - @type address: string - @ivar protocol: the protocol - @type protocol: int - @ivar bitmap: the bitmap - @type bitmap: string - @see: RFC 1035""" - - __slots__ = ['address', 'protocol', 'bitmap'] - - def __init__(self, rdclass, rdtype, address, protocol, bitmap): - super(WKS, self).__init__(rdclass, rdtype) - self.address = address - self.protocol = protocol - self.bitmap = bitmap - - def to_text(self, origin=None, relativize=True, **kw): - bits = [] - for i in xrange(0, len(self.bitmap)): - byte = ord(self.bitmap[i]) - for j in xrange(0, 8): - if byte & (0x80 >> j): - bits.append(str(i * 8 + j)) - text = ' '.join(bits) - return '%s %d %s' % (self.address, self.protocol, text) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - address = tok.get_string() - protocol = tok.get_string() - if protocol.isdigit(): - protocol = int(protocol) - else: - protocol = socket.getprotobyname(protocol) - bitmap = [] - while 1: - token = tok.get().unescape() - if token.is_eol_or_eof(): - break - if token.value.isdigit(): - serv = int(token.value) - else: - if protocol != _proto_udp and protocol != _proto_tcp: - raise NotImplementedError("protocol must be TCP or UDP") - if protocol == _proto_udp: - protocol_text = "udp" - else: - protocol_text = "tcp" - serv = socket.getservbyname(token.value, protocol_text) - i = serv // 8 - l = len(bitmap) - if l < i + 1: - for j in xrange(l, i + 1): - bitmap.append('\x00') - bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (serv % 8))) - bitmap = dns.rdata._truncate_bitmap(bitmap) - return cls(rdclass, rdtype, address, protocol, bitmap) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - file.write(dns.ipv4.inet_aton(self.address)) - protocol = struct.pack('!B', self.protocol) - file.write(protocol) - file.write(self.bitmap) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - address = dns.ipv4.inet_ntoa(wire[current : current + 4]) - protocol, = struct.unpack('!B', wire[current + 4 : current + 5]) - current += 5 - rdlen -= 5 - bitmap = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, address, protocol, bitmap) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - sa = dns.ipv4.inet_aton(self.address) - oa = dns.ipv4.inet_aton(other.address) - v = cmp(sa, oa) - if v == 0: - sp = struct.pack('!B', self.protocol) - op = struct.pack('!B', other.protocol) - v = cmp(sp, op) - if v == 0: - v = cmp(self.bitmap, other.bitmap) - return v diff --git a/lib/dnspython/dns/rdtypes/IN/__init__.py b/lib/dnspython/dns/rdtypes/IN/__init__.py deleted file mode 100644 index 24cf1ece4e..0000000000 --- a/lib/dnspython/dns/rdtypes/IN/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class IN rdata type classes.""" - -__all__ = [ - 'A', - 'AAAA', - 'APL', - 'DHCID', - 'KX', - 'NAPTR', - 'NSAP', - 'NSAP_PTR', - 'PX', - 'SRV', - 'WKS', -] diff --git a/lib/dnspython/dns/rdtypes/__init__.py b/lib/dnspython/dns/rdtypes/__init__.py deleted file mode 100644 index 49db5a3776..0000000000 --- a/lib/dnspython/dns/rdtypes/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdata type classes""" - -__all__ = [ - 'ANY', - 'IN', - 'mxbase', - 'nsbase', -] diff --git a/lib/dnspython/dns/rdtypes/dsbase.py b/lib/dnspython/dns/rdtypes/dsbase.py deleted file mode 100644 index 6f5559a7e4..0000000000 --- a/lib/dnspython/dns/rdtypes/dsbase.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) 2010, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.rdata -import dns.rdatatype - -class DSBase(dns.rdata.Rdata): - """Base class for rdata that is like a DS record - - @ivar key_tag: the key tag - @type key_tag: int - @ivar algorithm: the algorithm - @type algorithm: int - @ivar digest_type: the digest type - @type digest_type: int - @ivar digest: the digest - @type digest: int - @see: draft-ietf-dnsext-delegation-signer-14.txt""" - - __slots__ = ['key_tag', 'algorithm', 'digest_type', 'digest'] - - def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, - digest): - super(DSBase, self).__init__(rdclass, rdtype) - self.key_tag = key_tag - self.algorithm = algorithm - self.digest_type = digest_type - self.digest = digest - - def to_text(self, origin=None, relativize=True, **kw): - return '%d %d %d %s' % (self.key_tag, self.algorithm, - self.digest_type, - dns.rdata._hexify(self.digest, - chunksize=128)) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - key_tag = tok.get_uint16() - algorithm = tok.get_uint8() - digest_type = tok.get_uint8() - chunks = [] - while 1: - t = tok.get().unescape() - if t.is_eol_or_eof(): - break - if not t.is_identifier(): - raise dns.exception.SyntaxError - chunks.append(t.value) - digest = ''.join(chunks) - digest = digest.decode('hex_codec') - return cls(rdclass, rdtype, key_tag, algorithm, digest_type, - digest) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - header = struct.pack("!HBB", self.key_tag, self.algorithm, - self.digest_type) - file.write(header) - file.write(self.digest) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - header = struct.unpack("!HBB", wire[current : current + 4]) - current += 4 - rdlen -= 4 - digest = wire[current : current + rdlen].unwrap() - return cls(rdclass, rdtype, header[0], header[1], header[2], digest) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - hs = struct.pack("!HBB", self.key_tag, self.algorithm, - self.digest_type) - ho = struct.pack("!HBB", other.key_tag, other.algorithm, - other.digest_type) - v = cmp(hs, ho) - if v == 0: - v = cmp(self.digest, other.digest) - return v diff --git a/lib/dnspython/dns/rdtypes/mxbase.py b/lib/dnspython/dns/rdtypes/mxbase.py deleted file mode 100644 index abc6a9ed5b..0000000000 --- a/lib/dnspython/dns/rdtypes/mxbase.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""MX-like base classes.""" - -import cStringIO -import struct - -import dns.exception -import dns.rdata -import dns.name - -class MXBase(dns.rdata.Rdata): - """Base class for rdata that is like an MX record. - - @ivar preference: the preference value - @type preference: int - @ivar exchange: the exchange name - @type exchange: dns.name.Name object""" - - __slots__ = ['preference', 'exchange'] - - def __init__(self, rdclass, rdtype, preference, exchange): - super(MXBase, self).__init__(rdclass, rdtype) - self.preference = preference - self.exchange = exchange - - def to_text(self, origin=None, relativize=True, **kw): - exchange = self.exchange.choose_relativity(origin, relativize) - return '%d %s' % (self.preference, exchange) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - preference = tok.get_uint16() - exchange = tok.get_name() - exchange = exchange.choose_relativity(origin, relativize) - tok.get_eol() - return cls(rdclass, rdtype, preference, exchange) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - pref = struct.pack("!H", self.preference) - file.write(pref) - self.exchange.to_wire(file, compress, origin) - - def to_digestable(self, origin = None): - return struct.pack("!H", self.preference) + \ - self.exchange.to_digestable(origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (preference, ) = struct.unpack('!H', wire[current : current + 2]) - current += 2 - rdlen -= 2 - (exchange, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused != rdlen: - raise dns.exception.FormError - if not origin is None: - exchange = exchange.relativize(origin) - return cls(rdclass, rdtype, preference, exchange) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.exchange = self.exchange.choose_relativity(origin, relativize) - - def _cmp(self, other): - sp = struct.pack("!H", self.preference) - op = struct.pack("!H", other.preference) - v = cmp(sp, op) - if v == 0: - v = cmp(self.exchange, other.exchange) - return v - -class UncompressedMX(MXBase): - """Base class for rdata that is like an MX record, but whose name - is not compressed when converted to DNS wire format, and whose - digestable form is not downcased.""" - - def to_wire(self, file, compress = None, origin = None): - super(UncompressedMX, self).to_wire(file, None, origin) - - def to_digestable(self, origin = None): - f = cStringIO.StringIO() - self.to_wire(f, None, origin) - return f.getvalue() - -class UncompressedDowncasingMX(MXBase): - """Base class for rdata that is like an MX record, but whose name - is not compressed when convert to DNS wire format.""" - - def to_wire(self, file, compress = None, origin = None): - super(UncompressedDowncasingMX, self).to_wire(file, None, origin) diff --git a/lib/dnspython/dns/rdtypes/nsbase.py b/lib/dnspython/dns/rdtypes/nsbase.py deleted file mode 100644 index fbd5ef1e51..0000000000 --- a/lib/dnspython/dns/rdtypes/nsbase.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""NS-like base classes.""" - -import cStringIO - -import dns.exception -import dns.rdata -import dns.name - -class NSBase(dns.rdata.Rdata): - """Base class for rdata that is like an NS record. - - @ivar target: the target name of the rdata - @type target: dns.name.Name object""" - - __slots__ = ['target'] - - def __init__(self, rdclass, rdtype, target): - super(NSBase, self).__init__(rdclass, rdtype) - self.target = target - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - return str(target) - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - target = tok.get_name() - target = target.choose_relativity(origin, relativize) - tok.get_eol() - return cls(rdclass, rdtype, target) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - self.target.to_wire(file, compress, origin) - - def to_digestable(self, origin = None): - return self.target.to_digestable(origin) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - (target, cused) = dns.name.from_wire(wire[: current + rdlen], - current) - if cused != rdlen: - raise dns.exception.FormError - if not origin is None: - target = target.relativize(origin) - return cls(rdclass, rdtype, target) - - from_wire = classmethod(from_wire) - - def choose_relativity(self, origin = None, relativize = True): - self.target = self.target.choose_relativity(origin, relativize) - - def _cmp(self, other): - return cmp(self.target, other.target) - -class UncompressedNS(NSBase): - """Base class for rdata that is like an NS record, but whose name - is not compressed when convert to DNS wire format, and whose - digestable form is not downcased.""" - - def to_wire(self, file, compress = None, origin = None): - super(UncompressedNS, self).to_wire(file, None, origin) - - def to_digestable(self, origin = None): - f = cStringIO.StringIO() - self.to_wire(f, None, origin) - return f.getvalue() diff --git a/lib/dnspython/dns/rdtypes/txtbase.py b/lib/dnspython/dns/rdtypes/txtbase.py deleted file mode 100644 index 580f056ea0..0000000000 --- a/lib/dnspython/dns/rdtypes/txtbase.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""TXT-like base class.""" - -import dns.exception -import dns.rdata -import dns.tokenizer - -class TXTBase(dns.rdata.Rdata): - """Base class for rdata that is like a TXT record - - @ivar strings: the text strings - @type strings: list of string - @see: RFC 1035""" - - __slots__ = ['strings'] - - def __init__(self, rdclass, rdtype, strings): - super(TXTBase, self).__init__(rdclass, rdtype) - if isinstance(strings, str): - strings = [ strings ] - self.strings = strings[:] - - def to_text(self, origin=None, relativize=True, **kw): - txt = '' - prefix = '' - for s in self.strings: - txt += '%s"%s"' % (prefix, dns.rdata._escapify(s)) - prefix = ' ' - return txt - - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): - strings = [] - while 1: - token = tok.get().unescape() - if token.is_eol_or_eof(): - break - if not (token.is_quoted_string() or token.is_identifier()): - raise dns.exception.SyntaxError("expected a string") - if len(token.value) > 255: - raise dns.exception.SyntaxError("string too long") - strings.append(token.value) - if len(strings) == 0: - raise dns.exception.UnexpectedEnd - return cls(rdclass, rdtype, strings) - - from_text = classmethod(from_text) - - def to_wire(self, file, compress = None, origin = None): - for s in self.strings: - l = len(s) - assert l < 256 - byte = chr(l) - file.write(byte) - file.write(s) - - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): - strings = [] - while rdlen > 0: - l = ord(wire[current]) - current += 1 - rdlen -= 1 - if l > rdlen: - raise dns.exception.FormError - s = wire[current : current + l].unwrap() - current += l - rdlen -= l - strings.append(s) - return cls(rdclass, rdtype, strings) - - from_wire = classmethod(from_wire) - - def _cmp(self, other): - return cmp(self.strings, other.strings) diff --git a/lib/dnspython/dns/renderer.py b/lib/dnspython/dns/renderer.py deleted file mode 100644 index ad3f83d450..0000000000 --- a/lib/dnspython/dns/renderer.py +++ /dev/null @@ -1,325 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Help for building DNS wire format messages""" - -import cStringIO -import struct -import random -import time - -import dns.exception -import dns.tsig - -QUESTION = 0 -ANSWER = 1 -AUTHORITY = 2 -ADDITIONAL = 3 - -class Renderer(object): - """Helper class for building DNS wire-format messages. - - Most applications can use the higher-level L{dns.message.Message} - class and its to_wire() method to generate wire-format messages. - This class is for those applications which need finer control - over the generation of messages. - - Typical use:: - - r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512) - r.add_question(qname, qtype, qclass) - r.add_rrset(dns.renderer.ANSWER, rrset_1) - r.add_rrset(dns.renderer.ANSWER, rrset_2) - r.add_rrset(dns.renderer.AUTHORITY, ns_rrset) - r.add_edns(0, 0, 4096) - r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_1) - r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_2) - r.write_header() - r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) - wire = r.get_wire() - - @ivar output: where rendering is written - @type output: cStringIO.StringIO object - @ivar id: the message id - @type id: int - @ivar flags: the message flags - @type flags: int - @ivar max_size: the maximum size of the message - @type max_size: int - @ivar origin: the origin to use when rendering relative names - @type origin: dns.name.Name object - @ivar compress: the compression table - @type compress: dict - @ivar section: the section currently being rendered - @type section: int (dns.renderer.QUESTION, dns.renderer.ANSWER, - dns.renderer.AUTHORITY, or dns.renderer.ADDITIONAL) - @ivar counts: list of the number of RRs in each section - @type counts: int list of length 4 - @ivar mac: the MAC of the rendered message (if TSIG was used) - @type mac: string - """ - - def __init__(self, id=None, flags=0, max_size=65535, origin=None): - """Initialize a new renderer. - - @param id: the message id - @type id: int - @param flags: the DNS message flags - @type flags: int - @param max_size: the maximum message size; the default is 65535. - If rendering results in a message greater than I{max_size}, - then L{dns.exception.TooBig} will be raised. - @type max_size: int - @param origin: the origin to use when rendering relative names - @type origin: dns.name.Namem or None. - """ - - self.output = cStringIO.StringIO() - if id is None: - self.id = random.randint(0, 65535) - else: - self.id = id - self.flags = flags - self.max_size = max_size - self.origin = origin - self.compress = {} - self.section = QUESTION - self.counts = [0, 0, 0, 0] - self.output.write('\x00' * 12) - self.mac = '' - - def _rollback(self, where): - """Truncate the output buffer at offset I{where}, and remove any - compression table entries that pointed beyond the truncation - point. - - @param where: the offset - @type where: int - """ - - self.output.seek(where) - self.output.truncate() - keys_to_delete = [] - for k, v in self.compress.iteritems(): - if v >= where: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.compress[k] - - def _set_section(self, section): - """Set the renderer's current section. - - Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, - ADDITIONAL. Sections may be empty. - - @param section: the section - @type section: int - @raises dns.exception.FormError: an attempt was made to set - a section value less than the current section. - """ - - if self.section != section: - if self.section > section: - raise dns.exception.FormError - self.section = section - - def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): - """Add a question to the message. - - @param qname: the question name - @type qname: dns.name.Name - @param rdtype: the question rdata type - @type rdtype: int - @param rdclass: the question rdata class - @type rdclass: int - """ - - self._set_section(QUESTION) - before = self.output.tell() - qname.to_wire(self.output, self.compress, self.origin) - self.output.write(struct.pack("!HH", rdtype, rdclass)) - after = self.output.tell() - if after >= self.max_size: - self._rollback(before) - raise dns.exception.TooBig - self.counts[QUESTION] += 1 - - def add_rrset(self, section, rrset, **kw): - """Add the rrset to the specified section. - - Any keyword arguments are passed on to the rdataset's to_wire() - routine. - - @param section: the section - @type section: int - @param rrset: the rrset - @type rrset: dns.rrset.RRset object - """ - - self._set_section(section) - before = self.output.tell() - n = rrset.to_wire(self.output, self.compress, self.origin, **kw) - after = self.output.tell() - if after >= self.max_size: - self._rollback(before) - raise dns.exception.TooBig - self.counts[section] += n - - def add_rdataset(self, section, name, rdataset, **kw): - """Add the rdataset to the specified section, using the specified - name as the owner name. - - Any keyword arguments are passed on to the rdataset's to_wire() - routine. - - @param section: the section - @type section: int - @param name: the owner name - @type name: dns.name.Name object - @param rdataset: the rdataset - @type rdataset: dns.rdataset.Rdataset object - """ - - self._set_section(section) - before = self.output.tell() - n = rdataset.to_wire(name, self.output, self.compress, self.origin, - **kw) - after = self.output.tell() - if after >= self.max_size: - self._rollback(before) - raise dns.exception.TooBig - self.counts[section] += n - - def add_edns(self, edns, ednsflags, payload, options=None): - """Add an EDNS OPT record to the message. - - @param edns: The EDNS level to use. - @type edns: int - @param ednsflags: EDNS flag values. - @type ednsflags: int - @param payload: The EDNS sender's payload field, which is the maximum - size of UDP datagram the sender can handle. - @type payload: int - @param options: The EDNS options list - @type options: list of dns.edns.Option instances - @see: RFC 2671 - """ - - # make sure the EDNS version in ednsflags agrees with edns - ednsflags &= 0xFF00FFFFL - ednsflags |= (edns << 16) - self._set_section(ADDITIONAL) - before = self.output.tell() - self.output.write(struct.pack('!BHHIH', 0, dns.rdatatype.OPT, payload, - ednsflags, 0)) - if not options is None: - lstart = self.output.tell() - for opt in options: - stuff = struct.pack("!HH", opt.otype, 0) - self.output.write(stuff) - start = self.output.tell() - opt.to_wire(self.output) - end = self.output.tell() - assert end - start < 65536 - self.output.seek(start - 2) - stuff = struct.pack("!H", end - start) - self.output.write(stuff) - self.output.seek(0, 2) - lend = self.output.tell() - assert lend - lstart < 65536 - self.output.seek(lstart - 2) - stuff = struct.pack("!H", lend - lstart) - self.output.write(stuff) - self.output.seek(0, 2) - after = self.output.tell() - if after >= self.max_size: - self._rollback(before) - raise dns.exception.TooBig - self.counts[ADDITIONAL] += 1 - - def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data, - request_mac, algorithm=dns.tsig.default_algorithm): - """Add a TSIG signature to the message. - - @param keyname: the TSIG key name - @type keyname: dns.name.Name object - @param secret: the secret to use - @type secret: string - @param fudge: TSIG time fudge - @type fudge: int - @param id: the message id to encode in the tsig signature - @type id: int - @param tsig_error: TSIG error code; default is 0. - @type tsig_error: int - @param other_data: TSIG other data. - @type other_data: string - @param request_mac: This message is a response to the request which - had the specified MAC. - @type request_mac: string - @param algorithm: the TSIG algorithm to use - @type algorithm: dns.name.Name object - """ - - self._set_section(ADDITIONAL) - before = self.output.tell() - s = self.output.getvalue() - (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s, - keyname, - secret, - int(time.time()), - fudge, - id, - tsig_error, - other_data, - request_mac, - algorithm=algorithm) - keyname.to_wire(self.output, self.compress, self.origin) - self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG, - dns.rdataclass.ANY, 0, 0)) - rdata_start = self.output.tell() - self.output.write(tsig_rdata) - after = self.output.tell() - assert after - rdata_start < 65536 - if after >= self.max_size: - self._rollback(before) - raise dns.exception.TooBig - self.output.seek(rdata_start - 2) - self.output.write(struct.pack('!H', after - rdata_start)) - self.counts[ADDITIONAL] += 1 - self.output.seek(10) - self.output.write(struct.pack('!H', self.counts[ADDITIONAL])) - self.output.seek(0, 2) - - def write_header(self): - """Write the DNS message header. - - Writing the DNS message header is done asfter all sections - have been rendered, but before the optional TSIG signature - is added. - """ - - self.output.seek(0) - self.output.write(struct.pack('!HHHHHH', self.id, self.flags, - self.counts[0], self.counts[1], - self.counts[2], self.counts[3])) - self.output.seek(0, 2) - - def get_wire(self): - """Return the wire format message. - - @rtype: string - """ - - return self.output.getvalue() diff --git a/lib/dnspython/dns/resolver.py b/lib/dnspython/dns/resolver.py deleted file mode 100644 index 90f95e8ed0..0000000000 --- a/lib/dnspython/dns/resolver.py +++ /dev/null @@ -1,1161 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS stub resolver. - -@var default_resolver: The default resolver object -@type default_resolver: dns.resolver.Resolver object""" - -import socket -import sys -import time - -import dns.exception -import dns.ipv4 -import dns.ipv6 -import dns.message -import dns.name -import dns.query -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.reversename - -if sys.platform == 'win32': - import _winreg - -class NXDOMAIN(dns.exception.DNSException): - """The query name does not exist.""" - pass - -# The definition of the Timeout exception has moved from here to the -# dns.exception module. We keep dns.resolver.Timeout defined for -# backwards compatibility. - -Timeout = dns.exception.Timeout - -class NoAnswer(dns.exception.DNSException): - """The response did not contain an answer to the question.""" - pass - -class NoNameservers(dns.exception.DNSException): - """No non-broken nameservers are available to answer the query.""" - pass - -class NotAbsolute(dns.exception.DNSException): - """Raised if an absolute domain name is required but a relative name - was provided.""" - pass - -class NoRootSOA(dns.exception.DNSException): - """Raised if for some reason there is no SOA at the root name. - This should never happen!""" - pass - -class NoMetaqueries(dns.exception.DNSException): - """Metaqueries are not allowed.""" - pass - - -class Answer(object): - """DNS stub resolver answer - - Instances of this class bundle up the result of a successful DNS - resolution. - - For convenience, the answer object implements much of the sequence - protocol, forwarding to its rrset. E.g. "for a in answer" is - equivalent to "for a in answer.rrset", "answer[i]" is equivalent - to "answer.rrset[i]", and "answer[i:j]" is equivalent to - "answer.rrset[i:j]". - - Note that CNAMEs or DNAMEs in the response may mean that answer - node's name might not be the query name. - - @ivar qname: The query name - @type qname: dns.name.Name object - @ivar rdtype: The query type - @type rdtype: int - @ivar rdclass: The query class - @type rdclass: int - @ivar response: The response message - @type response: dns.message.Message object - @ivar rrset: The answer - @type rrset: dns.rrset.RRset object - @ivar expiration: The time when the answer expires - @type expiration: float (seconds since the epoch) - @ivar canonical_name: The canonical name of the query name - @type canonical_name: dns.name.Name object - """ - def __init__(self, qname, rdtype, rdclass, response, - raise_on_no_answer=True): - self.qname = qname - self.rdtype = rdtype - self.rdclass = rdclass - self.response = response - min_ttl = -1 - rrset = None - for count in xrange(0, 15): - try: - rrset = response.find_rrset(response.answer, qname, - rdclass, rdtype) - if min_ttl == -1 or rrset.ttl < min_ttl: - min_ttl = rrset.ttl - break - except KeyError: - if rdtype != dns.rdatatype.CNAME: - try: - crrset = response.find_rrset(response.answer, - qname, - rdclass, - dns.rdatatype.CNAME) - if min_ttl == -1 or crrset.ttl < min_ttl: - min_ttl = crrset.ttl - for rd in crrset: - qname = rd.target - break - continue - except KeyError: - if raise_on_no_answer: - raise NoAnswer - if raise_on_no_answer: - raise NoAnswer - if rrset is None and raise_on_no_answer: - raise NoAnswer - self.canonical_name = qname - self.rrset = rrset - if rrset is None: - while 1: - # Look for a SOA RR whose owner name is a superdomain - # of qname. - try: - srrset = response.find_rrset(response.authority, qname, - rdclass, dns.rdatatype.SOA) - if min_ttl == -1 or srrset.ttl < min_ttl: - min_ttl = srrset.ttl - if srrset[0].minimum < min_ttl: - min_ttl = srrset[0].minimum - break - except KeyError: - try: - qname = qname.parent() - except dns.name.NoParent: - break - self.expiration = time.time() + min_ttl - - def __getattr__(self, attr): - if attr == 'name': - return self.rrset.name - elif attr == 'ttl': - return self.rrset.ttl - elif attr == 'covers': - return self.rrset.covers - elif attr == 'rdclass': - return self.rrset.rdclass - elif attr == 'rdtype': - return self.rrset.rdtype - else: - raise AttributeError(attr) - - def __len__(self): - return len(self.rrset) - - def __iter__(self): - return iter(self.rrset) - - def __getitem__(self, i): - return self.rrset[i] - - def __delitem__(self, i): - del self.rrset[i] - - def __getslice__(self, i, j): - return self.rrset[i:j] - - def __delslice__(self, i, j): - del self.rrset[i:j] - -class Cache(object): - """Simple DNS answer cache. - - @ivar data: A dictionary of cached data - @type data: dict - @ivar cleaning_interval: The number of seconds between cleanings. The - default is 300 (5 minutes). - @type cleaning_interval: float - @ivar next_cleaning: The time the cache should next be cleaned (in seconds - since the epoch.) - @type next_cleaning: float - """ - - def __init__(self, cleaning_interval=300.0): - """Initialize a DNS cache. - - @param cleaning_interval: the number of seconds between periodic - cleanings. The default is 300.0 - @type cleaning_interval: float. - """ - - self.data = {} - self.cleaning_interval = cleaning_interval - self.next_cleaning = time.time() + self.cleaning_interval - - def maybe_clean(self): - """Clean the cache if it's time to do so.""" - - now = time.time() - if self.next_cleaning <= now: - keys_to_delete = [] - for (k, v) in self.data.iteritems(): - if v.expiration <= now: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.data[k] - now = time.time() - self.next_cleaning = now + self.cleaning_interval - - def get(self, key): - """Get the answer associated with I{key}. Returns None if - no answer is cached for the key. - @param key: the key - @type key: (dns.name.Name, int, int) tuple whose values are the - query name, rdtype, and rdclass. - @rtype: dns.resolver.Answer object or None - """ - - self.maybe_clean() - v = self.data.get(key) - if v is None or v.expiration <= time.time(): - return None - return v - - def put(self, key, value): - """Associate key and value in the cache. - @param key: the key - @type key: (dns.name.Name, int, int) tuple whose values are the - query name, rdtype, and rdclass. - @param value: The answer being cached - @type value: dns.resolver.Answer object - """ - - self.maybe_clean() - self.data[key] = value - - def flush(self, key=None): - """Flush the cache. - - If I{key} is specified, only that item is flushed. Otherwise - the entire cache is flushed. - - @param key: the key to flush - @type key: (dns.name.Name, int, int) tuple or None - """ - - if not key is None: - if self.data.has_key(key): - del self.data[key] - else: - self.data = {} - self.next_cleaning = time.time() + self.cleaning_interval - -class LRUCacheNode(object): - """LRUCache node. - """ - def __init__(self, key, value): - self.key = key - self.value = value - self.prev = self - self.next = self - - def link_before(self, node): - self.prev = node.prev - self.next = node - node.prev.next = self - node.prev = self - - def link_after(self, node): - self.prev = node - self.next = node.next - node.next.prev = self - node.next = self - - def unlink(self): - self.next.prev = self.prev - self.prev.next = self.next - -class LRUCache(object): - """Bounded least-recently-used DNS answer cache. - - This cache is better than the simple cache (above) if you're - running a web crawler or other process that does a lot of - resolutions. The LRUCache has a maximum number of nodes, and when - it is full, the least-recently used node is removed to make space - for a new one. - - @ivar data: A dictionary of cached data - @type data: dict - @ivar sentinel: sentinel node for circular doubly linked list of nodes - @type sentinel: LRUCacheNode object - @ivar max_size: The maximum number of nodes - @type max_size: int - """ - - def __init__(self, max_size=100000): - """Initialize a DNS cache. - - @param max_size: The maximum number of nodes to cache; the default is - 100000. Must be > 1. - @type max_size: int - """ - self.data = {} - self.set_max_size(max_size) - self.sentinel = LRUCacheNode(None, None) - - def set_max_size(self, max_size): - if max_size < 1: - max_size = 1 - self.max_size = max_size - - def get(self, key): - """Get the answer associated with I{key}. Returns None if - no answer is cached for the key. - @param key: the key - @type key: (dns.name.Name, int, int) tuple whose values are the - query name, rdtype, and rdclass. - @rtype: dns.resolver.Answer object or None - """ - node = self.data.get(key) - if node is None: - return None - # Unlink because we're either going to move the node to the front - # of the LRU list or we're going to free it. - node.unlink() - if node.value.expiration <= time.time(): - del self.data[node.key] - return None - node.link_after(self.sentinel) - return node.value - - def put(self, key, value): - """Associate key and value in the cache. - @param key: the key - @type key: (dns.name.Name, int, int) tuple whose values are the - query name, rdtype, and rdclass. - @param value: The answer being cached - @type value: dns.resolver.Answer object - """ - node = self.data.get(key) - if not node is None: - node.unlink() - del self.data[node.key] - while len(self.data) >= self.max_size: - node = self.sentinel.prev - node.unlink() - del self.data[node.key] - node = LRUCacheNode(key, value) - node.link_after(self.sentinel) - self.data[key] = node - - def flush(self, key=None): - """Flush the cache. - - If I{key} is specified, only that item is flushed. Otherwise - the entire cache is flushed. - - @param key: the key to flush - @type key: (dns.name.Name, int, int) tuple or None - """ - if not key is None: - node = self.data.get(key) - if not node is None: - node.unlink() - del self.data[node.key] - else: - node = self.sentinel.next - while node != self.sentinel: - next = node.next - node.prev = None - node.next = None - node = next - self.data = {} - -class Resolver(object): - """DNS stub resolver - - @ivar domain: The domain of this host - @type domain: dns.name.Name object - @ivar nameservers: A list of nameservers to query. Each nameserver is - a string which contains the IP address of a nameserver. - @type nameservers: list of strings - @ivar search: The search list. If the query name is a relative name, - the resolver will construct an absolute query name by appending the search - names one by one to the query name. - @type search: list of dns.name.Name objects - @ivar port: The port to which to send queries. The default is 53. - @type port: int - @ivar timeout: The number of seconds to wait for a response from a - server, before timing out. - @type timeout: float - @ivar lifetime: The total number of seconds to spend trying to get an - answer to the question. If the lifetime expires, a Timeout exception - will occur. - @type lifetime: float - @ivar keyring: The TSIG keyring to use. The default is None. - @type keyring: dict - @ivar keyname: The TSIG keyname to use. The default is None. - @type keyname: dns.name.Name object - @ivar keyalgorithm: The TSIG key algorithm to use. The default is - dns.tsig.default_algorithm. - @type keyalgorithm: string - @ivar edns: The EDNS level to use. The default is -1, no Edns. - @type edns: int - @ivar ednsflags: The EDNS flags - @type ednsflags: int - @ivar payload: The EDNS payload size. The default is 0. - @type payload: int - @ivar cache: The cache to use. The default is None. - @type cache: dns.resolver.Cache object - """ - def __init__(self, filename='/etc/resolv.conf', configure=True): - """Initialize a resolver instance. - - @param filename: The filename of a configuration file in - standard /etc/resolv.conf format. This parameter is meaningful - only when I{configure} is true and the platform is POSIX. - @type filename: string or file object - @param configure: If True (the default), the resolver instance - is configured in the normal fashion for the operating system - the resolver is running on. (I.e. a /etc/resolv.conf file on - POSIX systems and from the registry on Windows systems.) - @type configure: bool""" - - self.reset() - if configure: - if sys.platform == 'win32': - self.read_registry() - elif filename: - self.read_resolv_conf(filename) - - def reset(self): - """Reset all resolver configuration to the defaults.""" - self.domain = \ - dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) - if len(self.domain) == 0: - self.domain = dns.name.root - self.nameservers = [] - self.search = [] - self.port = 53 - self.timeout = 2.0 - self.lifetime = 30.0 - self.keyring = None - self.keyname = None - self.keyalgorithm = dns.tsig.default_algorithm - self.edns = -1 - self.ednsflags = 0 - self.payload = 0 - self.cache = None - - def read_resolv_conf(self, f): - """Process f as a file in the /etc/resolv.conf format. If f is - a string, it is used as the name of the file to open; otherwise it - is treated as the file itself.""" - if isinstance(f, str) or isinstance(f, unicode): - try: - f = open(f, 'r') - except IOError: - # /etc/resolv.conf doesn't exist, can't be read, etc. - # We'll just use the default resolver configuration. - self.nameservers = ['127.0.0.1'] - return - want_close = True - else: - want_close = False - try: - for l in f: - if len(l) == 0 or l[0] == '#' or l[0] == ';': - continue - tokens = l.split() - if len(tokens) == 0: - continue - if tokens[0] == 'nameserver': - self.nameservers.append(tokens[1]) - elif tokens[0] == 'domain': - self.domain = dns.name.from_text(tokens[1]) - elif tokens[0] == 'search': - for suffix in tokens[1:]: - self.search.append(dns.name.from_text(suffix)) - finally: - if want_close: - f.close() - if len(self.nameservers) == 0: - self.nameservers.append('127.0.0.1') - - def _determine_split_char(self, entry): - # - # The windows registry irritatingly changes the list element - # delimiter in between ' ' and ',' (and vice-versa) in various - # versions of windows. - # - if entry.find(' ') >= 0: - split_char = ' ' - elif entry.find(',') >= 0: - split_char = ',' - else: - # probably a singleton; treat as a space-separated list. - split_char = ' ' - return split_char - - def _config_win32_nameservers(self, nameservers): - """Configure a NameServer registry entry.""" - # we call str() on nameservers to convert it from unicode to ascii - nameservers = str(nameservers) - split_char = self._determine_split_char(nameservers) - ns_list = nameservers.split(split_char) - for ns in ns_list: - if not ns in self.nameservers: - self.nameservers.append(ns) - - def _config_win32_domain(self, domain): - """Configure a Domain registry entry.""" - # we call str() on domain to convert it from unicode to ascii - self.domain = dns.name.from_text(str(domain)) - - def _config_win32_search(self, search): - """Configure a Search registry entry.""" - # we call str() on search to convert it from unicode to ascii - search = str(search) - split_char = self._determine_split_char(search) - search_list = search.split(split_char) - for s in search_list: - if not s in self.search: - self.search.append(dns.name.from_text(s)) - - def _config_win32_fromkey(self, key): - """Extract DNS info from a registry key.""" - try: - servers, rtype = _winreg.QueryValueEx(key, 'NameServer') - except WindowsError: - servers = None - if servers: - self._config_win32_nameservers(servers) - try: - dom, rtype = _winreg.QueryValueEx(key, 'Domain') - if dom: - self._config_win32_domain(dom) - except WindowsError: - pass - else: - try: - servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer') - except WindowsError: - servers = None - if servers: - self._config_win32_nameservers(servers) - try: - dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') - if dom: - self._config_win32_domain(dom) - except WindowsError: - pass - try: - search, rtype = _winreg.QueryValueEx(key, 'SearchList') - except WindowsError: - search = None - if search: - self._config_win32_search(search) - - def read_registry(self): - """Extract resolver configuration from the Windows registry.""" - lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - want_scan = False - try: - try: - # XP, 2000 - tcp_params = _winreg.OpenKey(lm, - r'SYSTEM\CurrentControlSet' - r'\Services\Tcpip\Parameters') - want_scan = True - except EnvironmentError: - # ME - tcp_params = _winreg.OpenKey(lm, - r'SYSTEM\CurrentControlSet' - r'\Services\VxD\MSTCP') - try: - self._config_win32_fromkey(tcp_params) - finally: - tcp_params.Close() - if want_scan: - interfaces = _winreg.OpenKey(lm, - r'SYSTEM\CurrentControlSet' - r'\Services\Tcpip\Parameters' - r'\Interfaces') - try: - i = 0 - while True: - try: - guid = _winreg.EnumKey(interfaces, i) - i += 1 - key = _winreg.OpenKey(interfaces, guid) - if not self._win32_is_nic_enabled(lm, guid, key): - continue - try: - self._config_win32_fromkey(key) - finally: - key.Close() - except EnvironmentError: - break - finally: - interfaces.Close() - finally: - lm.Close() - - def _win32_is_nic_enabled(self, lm, guid, interface_key): - # Look in the Windows Registry to determine whether the network - # interface corresponding to the given guid is enabled. - # - # (Code contributed by Paul Marks, thanks!) - # - try: - # This hard-coded location seems to be consistent, at least - # from Windows 2000 through Vista. - connection_key = _winreg.OpenKey( - lm, - r'SYSTEM\CurrentControlSet\Control\Network' - r'\{4D36E972-E325-11CE-BFC1-08002BE10318}' - r'\%s\Connection' % guid) - - try: - # The PnpInstanceID points to a key inside Enum - (pnp_id, ttype) = _winreg.QueryValueEx( - connection_key, 'PnpInstanceID') - - if ttype != _winreg.REG_SZ: - raise ValueError - - device_key = _winreg.OpenKey( - lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id) - - try: - # Get ConfigFlags for this device - (flags, ttype) = _winreg.QueryValueEx( - device_key, 'ConfigFlags') - - if ttype != _winreg.REG_DWORD: - raise ValueError - - # Based on experimentation, bit 0x1 indicates that the - # device is disabled. - return not (flags & 0x1) - - finally: - device_key.Close() - finally: - connection_key.Close() - except (EnvironmentError, ValueError): - # Pre-vista, enabled interfaces seem to have a non-empty - # NTEContextList; this was how dnspython detected enabled - # nics before the code above was contributed. We've retained - # the old method since we don't know if the code above works - # on Windows 95/98/ME. - try: - (nte, ttype) = _winreg.QueryValueEx(interface_key, - 'NTEContextList') - return nte is not None - except WindowsError: - return False - - def _compute_timeout(self, start): - now = time.time() - if now < start: - if start - now > 1: - # Time going backwards is bad. Just give up. - raise Timeout - else: - # Time went backwards, but only a little. This can - # happen, e.g. under vmware with older linux kernels. - # Pretend it didn't happen. - now = start - duration = now - start - if duration >= self.lifetime: - raise Timeout - return min(self.lifetime - duration, self.timeout) - - def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, - tcp=False, source=None, raise_on_no_answer=True): - """Query nameservers to find the answer to the question. - - The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects - of the appropriate type, or strings that can be converted into objects - of the appropriate type. E.g. For I{rdtype} the integer 2 and the - the string 'NS' both mean to query for records with DNS rdata type NS. - - @param qname: the query name - @type qname: dns.name.Name object or string - @param rdtype: the query type - @type rdtype: int or string - @param rdclass: the query class - @type rdclass: int or string - @param tcp: use TCP to make the query (default is False). - @type tcp: bool - @param source: bind to this IP address (defaults to machine default IP). - @type source: IP address in dotted quad notation - @param raise_on_no_answer: raise NoAnswer if there's no answer - (defaults is True). - @type raise_on_no_answer: bool - @rtype: dns.resolver.Answer instance - @raises Timeout: no answers could be found in the specified lifetime - @raises NXDOMAIN: the query name does not exist - @raises NoAnswer: the response did not contain an answer and - raise_on_no_answer is True. - @raises NoNameservers: no non-broken nameservers are available to - answer the question.""" - - if isinstance(qname, (str, unicode)): - qname = dns.name.from_text(qname, None) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if dns.rdatatype.is_metatype(rdtype): - raise NoMetaqueries - if isinstance(rdclass, (str, unicode)): - rdclass = dns.rdataclass.from_text(rdclass) - if dns.rdataclass.is_metaclass(rdclass): - raise NoMetaqueries - qnames_to_try = [] - if qname.is_absolute(): - qnames_to_try.append(qname) - else: - if len(qname) > 1: - qnames_to_try.append(qname.concatenate(dns.name.root)) - if self.search: - for suffix in self.search: - qnames_to_try.append(qname.concatenate(suffix)) - else: - qnames_to_try.append(qname.concatenate(self.domain)) - all_nxdomain = True - start = time.time() - for qname in qnames_to_try: - if self.cache: - answer = self.cache.get((qname, rdtype, rdclass)) - if not answer is None: - if answer.rrset is None and raise_on_no_answer: - raise NoAnswer - else: - return answer - request = dns.message.make_query(qname, rdtype, rdclass) - if not self.keyname is None: - request.use_tsig(self.keyring, self.keyname, - algorithm=self.keyalgorithm) - request.use_edns(self.edns, self.ednsflags, self.payload) - response = None - # - # make a copy of the servers list so we can alter it later. - # - nameservers = self.nameservers[:] - backoff = 0.10 - while response is None: - if len(nameservers) == 0: - raise NoNameservers - for nameserver in nameservers[:]: - timeout = self._compute_timeout(start) - try: - if tcp: - response = dns.query.tcp(request, nameserver, - timeout, self.port, - source=source) - else: - response = dns.query.udp(request, nameserver, - timeout, self.port, - source=source) - except (socket.error, dns.exception.Timeout): - # - # Communication failure or timeout. Go to the - # next server - # - response = None - continue - except dns.query.UnexpectedSource: - # - # Who knows? Keep going. - # - response = None - continue - except dns.exception.FormError: - # - # We don't understand what this server is - # saying. Take it out of the mix and - # continue. - # - nameservers.remove(nameserver) - response = None - continue - rcode = response.rcode() - if rcode == dns.rcode.NOERROR or \ - rcode == dns.rcode.NXDOMAIN: - break - # - # We got a response, but we're not happy with the - # rcode in it. Remove the server from the mix if - # the rcode isn't SERVFAIL. - # - if rcode != dns.rcode.SERVFAIL: - nameservers.remove(nameserver) - response = None - if not response is None: - break - # - # All nameservers failed! - # - if len(nameservers) > 0: - # - # But we still have servers to try. Sleep a bit - # so we don't pound them! - # - timeout = self._compute_timeout(start) - sleep_time = min(timeout, backoff) - backoff *= 2 - time.sleep(sleep_time) - if response.rcode() == dns.rcode.NXDOMAIN: - continue - all_nxdomain = False - break - if all_nxdomain: - raise NXDOMAIN - answer = Answer(qname, rdtype, rdclass, response, - raise_on_no_answer) - if self.cache: - self.cache.put((qname, rdtype, rdclass), answer) - return answer - - def use_tsig(self, keyring, keyname=None, - algorithm=dns.tsig.default_algorithm): - """Add a TSIG signature to the query. - - @param keyring: The TSIG keyring to use; defaults to None. - @type keyring: dict - @param keyname: The name of the TSIG key to use; defaults to None. - The key must be defined in the keyring. If a keyring is specified - but a keyname is not, then the key used will be the first key in the - keyring. Note that the order of keys in a dictionary is not defined, - so applications should supply a keyname when a keyring is used, unless - they know the keyring contains only one key. - @param algorithm: The TSIG key algorithm to use. The default - is dns.tsig.default_algorithm. - @type algorithm: string""" - self.keyring = keyring - if keyname is None: - self.keyname = self.keyring.keys()[0] - else: - self.keyname = keyname - self.keyalgorithm = algorithm - - def use_edns(self, edns, ednsflags, payload): - """Configure Edns. - - @param edns: The EDNS level to use. The default is -1, no Edns. - @type edns: int - @param ednsflags: The EDNS flags - @type ednsflags: int - @param payload: The EDNS payload size. The default is 0. - @type payload: int""" - - if edns is None: - edns = -1 - self.edns = edns - self.ednsflags = ednsflags - self.payload = payload - -default_resolver = None - -def get_default_resolver(): - """Get the default resolver, initializing it if necessary.""" - global default_resolver - if default_resolver is None: - default_resolver = Resolver() - return default_resolver - -def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, - tcp=False, source=None, raise_on_no_answer=True): - """Query nameservers to find the answer to the question. - - This is a convenience function that uses the default resolver - object to make the query. - @see: L{dns.resolver.Resolver.query} for more information on the - parameters.""" - return get_default_resolver().query(qname, rdtype, rdclass, tcp, source, - raise_on_no_answer) - -def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None): - """Find the name of the zone which contains the specified name. - - @param name: the query name - @type name: absolute dns.name.Name object or string - @param rdclass: The query class - @type rdclass: int - @param tcp: use TCP to make the query (default is False). - @type tcp: bool - @param resolver: the resolver to use - @type resolver: dns.resolver.Resolver object or None - @rtype: dns.name.Name""" - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, dns.name.root) - if resolver is None: - resolver = get_default_resolver() - if not name.is_absolute(): - raise NotAbsolute(name) - while 1: - try: - answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp) - if answer.rrset.name == name: - return name - # otherwise we were CNAMEd or DNAMEd and need to look higher - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - pass - try: - name = name.parent() - except dns.name.NoParent: - raise NoRootSOA - -# -# Support for overriding the system resolver for all python code in the -# running process. -# - -_protocols_for_socktype = { - socket.SOCK_DGRAM : [socket.SOL_UDP], - socket.SOCK_STREAM : [socket.SOL_TCP], - } - -_resolver = None -_original_getaddrinfo = socket.getaddrinfo -_original_getnameinfo = socket.getnameinfo -_original_getfqdn = socket.getfqdn -_original_gethostbyname = socket.gethostbyname -_original_gethostbyname_ex = socket.gethostbyname_ex -_original_gethostbyaddr = socket.gethostbyaddr - -def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0, - proto=0, flags=0): - if flags & (socket.AI_ADDRCONFIG|socket.AI_V4MAPPED) != 0: - raise NotImplementedError - if host is None and service is None: - raise socket.gaierror(socket.EAI_NONAME) - v6addrs = [] - v4addrs = [] - canonical_name = None - try: - # Is host None or a V6 address literal? - if host is None: - canonical_name = 'localhost' - if flags & socket.AI_PASSIVE != 0: - v6addrs.append('::') - v4addrs.append('0.0.0.0') - else: - v6addrs.append('::1') - v4addrs.append('127.0.0.1') - else: - parts = host.split('%') - if len(parts) == 2: - ahost = parts[0] - else: - ahost = host - addr = dns.ipv6.inet_aton(ahost) - v6addrs.append(host) - canonical_name = host - except: - try: - # Is it a V4 address literal? - addr = dns.ipv4.inet_aton(host) - v4addrs.append(host) - canonical_name = host - except: - if flags & socket.AI_NUMERICHOST == 0: - try: - qname = None - if family == socket.AF_INET6 or family == socket.AF_UNSPEC: - v6 = _resolver.query(host, dns.rdatatype.AAAA, - raise_on_no_answer=False) - # Note that setting host ensures we query the same name - # for A as we did for AAAA. - host = v6.qname - canonical_name = v6.canonical_name.to_text(True) - if v6.rrset is not None: - for rdata in v6.rrset: - v6addrs.append(rdata.address) - if family == socket.AF_INET or family == socket.AF_UNSPEC: - v4 = _resolver.query(host, dns.rdatatype.A, - raise_on_no_answer=False) - host = v4.qname - canonical_name = v4.canonical_name.to_text(True) - if v4.rrset is not None: - for rdata in v4.rrset: - v4addrs.append(rdata.address) - except dns.resolver.NXDOMAIN: - raise socket.gaierror(socket.EAI_NONAME) - except: - raise socket.gaierror(socket.EAI_SYSTEM) - port = None - try: - # Is it a port literal? - if service is None: - port = 0 - else: - port = int(service) - except: - if flags & socket.AI_NUMERICSERV == 0: - try: - port = socket.getservbyname(service) - except: - pass - if port is None: - raise socket.gaierror(socket.EAI_NONAME) - tuples = [] - if socktype == 0: - socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] - else: - socktypes = [socktype] - if flags & socket.AI_CANONNAME != 0: - cname = canonical_name - else: - cname = '' - if family == socket.AF_INET6 or family == socket.AF_UNSPEC: - for addr in v6addrs: - for socktype in socktypes: - for proto in _protocols_for_socktype[socktype]: - tuples.append((socket.AF_INET6, socktype, proto, - cname, (addr, port, 0, 0))) - if family == socket.AF_INET or family == socket.AF_UNSPEC: - for addr in v4addrs: - for socktype in socktypes: - for proto in _protocols_for_socktype[socktype]: - tuples.append((socket.AF_INET, socktype, proto, - cname, (addr, port))) - if len(tuples) == 0: - raise socket.gaierror(socket.EAI_NONAME) - return tuples - -def _getnameinfo(sockaddr, flags=0): - host = sockaddr[0] - port = sockaddr[1] - if len(sockaddr) == 4: - scope = sockaddr[3] - family = socket.AF_INET6 - else: - scope = None - family = socket.AF_INET - tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, - socket.SOL_TCP, 0) - if len(tuples) > 1: - raise socket.error('sockaddr resolved to multiple addresses') - addr = tuples[0][4][0] - if flags & socket.NI_DGRAM: - pname = 'udp' - else: - pname = 'tcp' - qname = dns.reversename.from_address(addr) - if flags & socket.NI_NUMERICHOST == 0: - try: - answer = _resolver.query(qname, 'PTR') - hostname = answer.rrset[0].target.to_text(True) - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - if flags & socket.NI_NAMEREQD: - raise socket.gaierror(socket.EAI_NONAME) - hostname = addr - if scope is not None: - hostname += '%' + str(scope) - else: - hostname = addr - if scope is not None: - hostname += '%' + str(scope) - if flags & socket.NI_NUMERICSERV: - service = str(port) - else: - service = socket.getservbyport(port, pname) - return (hostname, service) - -def _getfqdn(name=None): - if name is None: - name = socket.gethostname() - return _getnameinfo(_getaddrinfo(name, 80)[0][4])[0] - -def _gethostbyname(name): - return _gethostbyname_ex(name)[2][0] - -def _gethostbyname_ex(name): - aliases = [] - addresses = [] - tuples = _getaddrinfo(name, 0, socket.AF_INET, socket.SOCK_STREAM, - socket.SOL_TCP, socket.AI_CANONNAME) - canonical = tuples[0][3] - for item in tuples: - addresses.append(item[4][0]) - # XXX we just ignore aliases - return (canonical, aliases, addresses) - -def _gethostbyaddr(ip): - try: - addr = dns.ipv6.inet_aton(ip) - sockaddr = (ip, 80, 0, 0) - family = socket.AF_INET6 - except: - sockaddr = (ip, 80) - family = socket.AF_INET - (name, port) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) - aliases = [] - addresses = [] - tuples = _getaddrinfo(name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, - socket.AI_CANONNAME) - canonical = tuples[0][3] - for item in tuples: - addresses.append(item[4][0]) - # XXX we just ignore aliases - return (canonical, aliases, addresses) - -def override_system_resolver(resolver=None): - """Override the system resolver routines in the socket module with - versions which use dnspython's resolver. - - This can be useful in testing situations where you want to control - the resolution behavior of python code without having to change - the system's resolver settings (e.g. /etc/resolv.conf). - - The resolver to use may be specified; if it's not, the default - resolver will be used. - - @param resolver: the resolver to use - @type resolver: dns.resolver.Resolver object or None - """ - if resolver is None: - resolver = get_default_resolver() - global _resolver - _resolver = resolver - socket.getaddrinfo = _getaddrinfo - socket.getnameinfo = _getnameinfo - socket.getfqdn = _getfqdn - socket.gethostbyname = _gethostbyname - socket.gethostbyname_ex = _gethostbyname_ex - socket.gethostbyaddr = _gethostbyaddr - -def restore_system_resolver(): - """Undo the effects of override_system_resolver(). - """ - global _resolver - _resolver = None - socket.getaddrinfo = _original_getaddrinfo - socket.getnameinfo = _original_getnameinfo - socket.getfqdn = _original_getfqdn - socket.gethostbyname = _original_gethostbyname - socket.gethostbyname_ex = _original_gethostbyname_ex - socket.gethostbyaddr = _original_gethostbyaddr diff --git a/lib/dnspython/dns/reversename.py b/lib/dnspython/dns/reversename.py deleted file mode 100644 index 4925cfd6ff..0000000000 --- a/lib/dnspython/dns/reversename.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Reverse Map Names. - -@var ipv4_reverse_domain: The DNS IPv4 reverse-map domain, in-addr.arpa. -@type ipv4_reverse_domain: dns.name.Name object -@var ipv6_reverse_domain: The DNS IPv6 reverse-map domain, ip6.arpa. -@type ipv6_reverse_domain: dns.name.Name object -""" - -import dns.name -import dns.ipv6 -import dns.ipv4 - -ipv4_reverse_domain = dns.name.from_text('in-addr.arpa.') -ipv6_reverse_domain = dns.name.from_text('ip6.arpa.') - -def from_address(text): - """Convert an IPv4 or IPv6 address in textual form into a Name object whose - value is the reverse-map domain name of the address. - @param text: an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1', - '::1') - @type text: str - @rtype: dns.name.Name object - """ - try: - parts = list(dns.ipv6.inet_aton(text).encode('hex_codec')) - origin = ipv6_reverse_domain - except: - parts = ['%d' % ord(byte) for byte in dns.ipv4.inet_aton(text)] - origin = ipv4_reverse_domain - parts.reverse() - return dns.name.from_text('.'.join(parts), origin=origin) - -def to_address(name): - """Convert a reverse map domain name into textual address form. - @param name: an IPv4 or IPv6 address in reverse-map form. - @type name: dns.name.Name object - @rtype: str - """ - if name.is_subdomain(ipv4_reverse_domain): - name = name.relativize(ipv4_reverse_domain) - labels = list(name.labels) - labels.reverse() - text = '.'.join(labels) - # run through inet_aton() to check syntax and make pretty. - return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) - elif name.is_subdomain(ipv6_reverse_domain): - name = name.relativize(ipv6_reverse_domain) - labels = list(name.labels) - labels.reverse() - parts = [] - i = 0 - l = len(labels) - while i < l: - parts.append(''.join(labels[i:i+4])) - i += 4 - text = ':'.join(parts) - # run through inet_aton() to check syntax and make pretty. - return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) - else: - raise dns.exception.SyntaxError('unknown reverse-map address family') diff --git a/lib/dnspython/dns/rrset.py b/lib/dnspython/dns/rrset.py deleted file mode 100644 index f6051fea4f..0000000000 --- a/lib/dnspython/dns/rrset.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS RRsets (an RRset is a named rdataset)""" - -import dns.name -import dns.rdataset -import dns.rdataclass -import dns.renderer - -class RRset(dns.rdataset.Rdataset): - """A DNS RRset (named rdataset). - - RRset inherits from Rdataset, and RRsets can be treated as - Rdatasets in most cases. There are, however, a few notable - exceptions. RRsets have different to_wire() and to_text() method - arguments, reflecting the fact that RRsets always have an owner - name. - """ - - __slots__ = ['name', 'deleting'] - - def __init__(self, name, rdclass, rdtype, covers=dns.rdatatype.NONE, - deleting=None): - """Create a new RRset.""" - - super(RRset, self).__init__(rdclass, rdtype, covers) - self.name = name - self.deleting = deleting - - def _clone(self): - obj = super(RRset, self)._clone() - obj.name = self.name - obj.deleting = self.deleting - return obj - - def __repr__(self): - if self.covers == 0: - ctext = '' - else: - ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' - if not self.deleting is None: - dtext = ' delete=' + dns.rdataclass.to_text(self.deleting) - else: - dtext = '' - return '' - - def __str__(self): - return self.to_text() - - def __eq__(self, other): - """Two RRsets are equal if they have the same name and the same - rdataset - - @rtype: bool""" - if not isinstance(other, RRset): - return False - if self.name != other.name: - return False - return super(RRset, self).__eq__(other) - - def match(self, name, rdclass, rdtype, covers, deleting=None): - """Returns True if this rrset matches the specified class, type, - covers, and deletion state.""" - - if not super(RRset, self).match(rdclass, rdtype, covers): - return False - if self.name != name or self.deleting != deleting: - return False - return True - - def to_text(self, origin=None, relativize=True, **kw): - """Convert the RRset into DNS master file format. - - @see: L{dns.name.Name.choose_relativity} for more information - on how I{origin} and I{relativize} determine the way names - are emitted. - - Any additional keyword arguments are passed on to the rdata - to_text() method. - - @param origin: The origin for relative names, or None. - @type origin: dns.name.Name object - @param relativize: True if names should names be relativized - @type relativize: bool""" - - return super(RRset, self).to_text(self.name, origin, relativize, - self.deleting, **kw) - - def to_wire(self, file, compress=None, origin=None, **kw): - """Convert the RRset to wire format.""" - - return super(RRset, self).to_wire(self.name, file, compress, origin, - self.deleting, **kw) - - def to_rdataset(self): - """Convert an RRset into an Rdataset. - - @rtype: dns.rdataset.Rdataset object - """ - return dns.rdataset.from_rdata_list(self.ttl, list(self)) - - -def from_text_list(name, ttl, rdclass, rdtype, text_rdatas): - """Create an RRset with the specified name, TTL, class, and type, and with - the specified list of rdatas in text format. - - @rtype: dns.rrset.RRset object - """ - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - if isinstance(rdclass, (str, unicode)): - rdclass = dns.rdataclass.from_text(rdclass) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - r = RRset(name, rdclass, rdtype) - r.update_ttl(ttl) - for t in text_rdatas: - rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) - r.add(rd) - return r - -def from_text(name, ttl, rdclass, rdtype, *text_rdatas): - """Create an RRset with the specified name, TTL, class, and type and with - the specified rdatas in text format. - - @rtype: dns.rrset.RRset object - """ - - return from_text_list(name, ttl, rdclass, rdtype, text_rdatas) - -def from_rdata_list(name, ttl, rdatas): - """Create an RRset with the specified name and TTL, and with - the specified list of rdata objects. - - @rtype: dns.rrset.RRset object - """ - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - - if len(rdatas) == 0: - raise ValueError("rdata list must not be empty") - r = None - for rd in rdatas: - if r is None: - r = RRset(name, rd.rdclass, rd.rdtype) - r.update_ttl(ttl) - first_time = False - r.add(rd) - return r - -def from_rdata(name, ttl, *rdatas): - """Create an RRset with the specified name and TTL, and with - the specified rdata objects. - - @rtype: dns.rrset.RRset object - """ - - return from_rdata_list(name, ttl, rdatas) diff --git a/lib/dnspython/dns/set.py b/lib/dnspython/dns/set.py deleted file mode 100644 index 14c76a05c4..0000000000 --- a/lib/dnspython/dns/set.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""A simple Set class.""" - -class Set(object): - """A simple set class. - - Sets are not in Python until 2.3, and rdata are not immutable so - we cannot use sets.Set anyway. This class implements subset of - the 2.3 Set interface using a list as the container. - - @ivar items: A list of the items which are in the set - @type items: list""" - - __slots__ = ['items'] - - def __init__(self, items=None): - """Initialize the set. - - @param items: the initial set of items - @type items: any iterable or None - """ - - self.items = [] - if not items is None: - for item in items: - self.add(item) - - def __repr__(self): - return "dns.simpleset.Set(%s)" % repr(self.items) - - def add(self, item): - """Add an item to the set.""" - if not item in self.items: - self.items.append(item) - - def remove(self, item): - """Remove an item from the set.""" - self.items.remove(item) - - def discard(self, item): - """Remove an item from the set if present.""" - try: - self.items.remove(item) - except ValueError: - pass - - def _clone(self): - """Make a (shallow) copy of the set. - - There is a 'clone protocol' that subclasses of this class - should use. To make a copy, first call your super's _clone() - method, and use the object returned as the new instance. Then - make shallow copies of the attributes defined in the subclass. - - This protocol allows us to write the set algorithms that - return new instances (e.g. union) once, and keep using them in - subclasses. - """ - - cls = self.__class__ - obj = cls.__new__(cls) - obj.items = list(self.items) - return obj - - def __copy__(self): - """Make a (shallow) copy of the set.""" - return self._clone() - - def copy(self): - """Make a (shallow) copy of the set.""" - return self._clone() - - def union_update(self, other): - """Update the set, adding any elements from other which are not - already in the set. - @param other: the collection of items with which to update the set - @type other: Set object - """ - if not isinstance(other, Set): - raise ValueError('other must be a Set instance') - if self is other: - return - for item in other.items: - self.add(item) - - def intersection_update(self, other): - """Update the set, removing any elements from other which are not - in both sets. - @param other: the collection of items with which to update the set - @type other: Set object - """ - if not isinstance(other, Set): - raise ValueError('other must be a Set instance') - if self is other: - return - # we make a copy of the list so that we can remove items from - # the list without breaking the iterator. - for item in list(self.items): - if item not in other.items: - self.items.remove(item) - - def difference_update(self, other): - """Update the set, removing any elements from other which are in - the set. - @param other: the collection of items with which to update the set - @type other: Set object - """ - if not isinstance(other, Set): - raise ValueError('other must be a Set instance') - if self is other: - self.items = [] - else: - for item in other.items: - self.discard(item) - - def union(self, other): - """Return a new set which is the union of I{self} and I{other}. - - @param other: the other set - @type other: Set object - @rtype: the same type as I{self} - """ - - obj = self._clone() - obj.union_update(other) - return obj - - def intersection(self, other): - """Return a new set which is the intersection of I{self} and I{other}. - - @param other: the other set - @type other: Set object - @rtype: the same type as I{self} - """ - - obj = self._clone() - obj.intersection_update(other) - return obj - - def difference(self, other): - """Return a new set which I{self} - I{other}, i.e. the items - in I{self} which are not also in I{other}. - - @param other: the other set - @type other: Set object - @rtype: the same type as I{self} - """ - - obj = self._clone() - obj.difference_update(other) - return obj - - def __or__(self, other): - return self.union(other) - - def __and__(self, other): - return self.intersection(other) - - def __add__(self, other): - return self.union(other) - - def __sub__(self, other): - return self.difference(other) - - def __ior__(self, other): - self.union_update(other) - return self - - def __iand__(self, other): - self.intersection_update(other) - return self - - def __iadd__(self, other): - self.union_update(other) - return self - - def __isub__(self, other): - self.difference_update(other) - return self - - def update(self, other): - """Update the set, adding any elements from other which are not - already in the set. - @param other: the collection of items with which to update the set - @type other: any iterable type""" - for item in other: - self.add(item) - - def clear(self): - """Make the set empty.""" - self.items = [] - - def __eq__(self, other): - # Yes, this is inefficient but the sets we're dealing with are - # usually quite small, so it shouldn't hurt too much. - for item in self.items: - if not item in other.items: - return False - for item in other.items: - if not item in self.items: - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.items) - - def __iter__(self): - return iter(self.items) - - def __getitem__(self, i): - return self.items[i] - - def __delitem__(self, i): - del self.items[i] - - def __getslice__(self, i, j): - return self.items[i:j] - - def __delslice__(self, i, j): - del self.items[i:j] - - def issubset(self, other): - """Is I{self} a subset of I{other}? - - @rtype: bool - """ - - if not isinstance(other, Set): - raise ValueError('other must be a Set instance') - for item in self.items: - if not item in other.items: - return False - return True - - def issuperset(self, other): - """Is I{self} a superset of I{other}? - - @rtype: bool - """ - - if not isinstance(other, Set): - raise ValueError('other must be a Set instance') - for item in other.items: - if not item in self.items: - return False - return True diff --git a/lib/dnspython/dns/tokenizer.py b/lib/dnspython/dns/tokenizer.py deleted file mode 100644 index 4bff7b6c72..0000000000 --- a/lib/dnspython/dns/tokenizer.py +++ /dev/null @@ -1,547 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Tokenize DNS master file format""" - -import cStringIO -import sys - -import dns.exception -import dns.name -import dns.ttl - -_DELIMITERS = { - ' ' : True, - '\t' : True, - '\n' : True, - ';' : True, - '(' : True, - ')' : True, - '"' : True } - -_QUOTING_DELIMITERS = { '"' : True } - -EOF = 0 -EOL = 1 -WHITESPACE = 2 -IDENTIFIER = 3 -QUOTED_STRING = 4 -COMMENT = 5 -DELIMITER = 6 - -class UngetBufferFull(dns.exception.DNSException): - """Raised when an attempt is made to unget a token when the unget - buffer is full.""" - pass - -class Token(object): - """A DNS master file format token. - - @ivar ttype: The token type - @type ttype: int - @ivar value: The token value - @type value: string - @ivar has_escape: Does the token value contain escapes? - @type has_escape: bool - """ - - def __init__(self, ttype, value='', has_escape=False): - """Initialize a token instance. - - @param ttype: The token type - @type ttype: int - @ivar value: The token value - @type value: string - @ivar has_escape: Does the token value contain escapes? - @type has_escape: bool - """ - self.ttype = ttype - self.value = value - self.has_escape = has_escape - - def is_eof(self): - return self.ttype == EOF - - def is_eol(self): - return self.ttype == EOL - - def is_whitespace(self): - return self.ttype == WHITESPACE - - def is_identifier(self): - return self.ttype == IDENTIFIER - - def is_quoted_string(self): - return self.ttype == QUOTED_STRING - - def is_comment(self): - return self.ttype == COMMENT - - def is_delimiter(self): - return self.ttype == DELIMITER - - def is_eol_or_eof(self): - return (self.ttype == EOL or self.ttype == EOF) - - def __eq__(self, other): - if not isinstance(other, Token): - return False - return (self.ttype == other.ttype and - self.value == other.value) - - def __ne__(self, other): - if not isinstance(other, Token): - return True - return (self.ttype != other.ttype or - self.value != other.value) - - def __str__(self): - return '%d "%s"' % (self.ttype, self.value) - - def unescape(self): - if not self.has_escape: - return self - unescaped = '' - l = len(self.value) - i = 0 - while i < l: - c = self.value[i] - i += 1 - if c == '\\': - if i >= l: - raise dns.exception.UnexpectedEnd - c = self.value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = self.value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = self.value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) - unescaped += c - return Token(self.ttype, unescaped) - - # compatibility for old-style tuple tokens - - def __len__(self): - return 2 - - def __iter__(self): - return iter((self.ttype, self.value)) - - def __getitem__(self, i): - if i == 0: - return self.ttype - elif i == 1: - return self.value - else: - raise IndexError - -class Tokenizer(object): - """A DNS master file format tokenizer. - - A token is a (type, value) tuple, where I{type} is an int, and - I{value} is a string. The valid types are EOF, EOL, WHITESPACE, - IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER. - - @ivar file: The file to tokenize - @type file: file - @ivar ungotten_char: The most recently ungotten character, or None. - @type ungotten_char: string - @ivar ungotten_token: The most recently ungotten token, or None. - @type ungotten_token: (int, string) token tuple - @ivar multiline: The current multiline level. This value is increased - by one every time a '(' delimiter is read, and decreased by one every time - a ')' delimiter is read. - @type multiline: int - @ivar quoting: This variable is true if the tokenizer is currently - reading a quoted string. - @type quoting: bool - @ivar eof: This variable is true if the tokenizer has encountered EOF. - @type eof: bool - @ivar delimiters: The current delimiter dictionary. - @type delimiters: dict - @ivar line_number: The current line number - @type line_number: int - @ivar filename: A filename that will be returned by the L{where} method. - @type filename: string - """ - - def __init__(self, f=sys.stdin, filename=None): - """Initialize a tokenizer instance. - - @param f: The file to tokenize. The default is sys.stdin. - This parameter may also be a string, in which case the tokenizer - will take its input from the contents of the string. - @type f: file or string - @param filename: the name of the filename that the L{where} method - will return. - @type filename: string - """ - - if isinstance(f, str): - f = cStringIO.StringIO(f) - if filename is None: - filename = '' - else: - if filename is None: - if f is sys.stdin: - filename = '' - else: - filename = '' - self.file = f - self.ungotten_char = None - self.ungotten_token = None - self.multiline = 0 - self.quoting = False - self.eof = False - self.delimiters = _DELIMITERS - self.line_number = 1 - self.filename = filename - - def _get_char(self): - """Read a character from input. - @rtype: string - """ - - if self.ungotten_char is None: - if self.eof: - c = '' - else: - c = self.file.read(1) - if c == '': - self.eof = True - elif c == '\n': - self.line_number += 1 - else: - c = self.ungotten_char - self.ungotten_char = None - return c - - def where(self): - """Return the current location in the input. - - @rtype: (string, int) tuple. The first item is the filename of - the input, the second is the current line number. - """ - - return (self.filename, self.line_number) - - def _unget_char(self, c): - """Unget a character. - - The unget buffer for characters is only one character large; it is - an error to try to unget a character when the unget buffer is not - empty. - - @param c: the character to unget - @type c: string - @raises UngetBufferFull: there is already an ungotten char - """ - - if not self.ungotten_char is None: - raise UngetBufferFull - self.ungotten_char = c - - def skip_whitespace(self): - """Consume input until a non-whitespace character is encountered. - - The non-whitespace character is then ungotten, and the number of - whitespace characters consumed is returned. - - If the tokenizer is in multiline mode, then newlines are whitespace. - - @rtype: int - """ - - skipped = 0 - while True: - c = self._get_char() - if c != ' ' and c != '\t': - if (c != '\n') or not self.multiline: - self._unget_char(c) - return skipped - skipped += 1 - - def get(self, want_leading = False, want_comment = False): - """Get the next token. - - @param want_leading: If True, return a WHITESPACE token if the - first character read is whitespace. The default is False. - @type want_leading: bool - @param want_comment: If True, return a COMMENT token if the - first token read is a comment. The default is False. - @type want_comment: bool - @rtype: Token object - @raises dns.exception.UnexpectedEnd: input ended prematurely - @raises dns.exception.SyntaxError: input was badly formed - """ - - if not self.ungotten_token is None: - token = self.ungotten_token - self.ungotten_token = None - if token.is_whitespace(): - if want_leading: - return token - elif token.is_comment(): - if want_comment: - return token - else: - return token - skipped = self.skip_whitespace() - if want_leading and skipped > 0: - return Token(WHITESPACE, ' ') - token = '' - ttype = IDENTIFIER - has_escape = False - while True: - c = self._get_char() - if c == '' or c in self.delimiters: - if c == '' and self.quoting: - raise dns.exception.UnexpectedEnd - if token == '' and ttype != QUOTED_STRING: - if c == '(': - self.multiline += 1 - self.skip_whitespace() - continue - elif c == ')': - if not self.multiline > 0: - raise dns.exception.SyntaxError - self.multiline -= 1 - self.skip_whitespace() - continue - elif c == '"': - if not self.quoting: - self.quoting = True - self.delimiters = _QUOTING_DELIMITERS - ttype = QUOTED_STRING - continue - else: - self.quoting = False - self.delimiters = _DELIMITERS - self.skip_whitespace() - continue - elif c == '\n': - return Token(EOL, '\n') - elif c == ';': - while 1: - c = self._get_char() - if c == '\n' or c == '': - break - token += c - if want_comment: - self._unget_char(c) - return Token(COMMENT, token) - elif c == '': - if self.multiline: - raise dns.exception.SyntaxError('unbalanced parentheses') - return Token(EOF) - elif self.multiline: - self.skip_whitespace() - token = '' - continue - else: - return Token(EOL, '\n') - else: - # This code exists in case we ever want a - # delimiter to be returned. It never produces - # a token currently. - token = c - ttype = DELIMITER - else: - self._unget_char(c) - break - elif self.quoting: - if c == '\\': - c = self._get_char() - if c == '': - raise dns.exception.UnexpectedEnd - if c.isdigit(): - c2 = self._get_char() - if c2 == '': - raise dns.exception.UnexpectedEnd - c3 = self._get_char() - if c == '': - raise dns.exception.UnexpectedEnd - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) - elif c == '\n': - raise dns.exception.SyntaxError('newline in quoted string') - elif c == '\\': - # - # It's an escape. Put it and the next character into - # the token; it will be checked later for goodness. - # - token += c - has_escape = True - c = self._get_char() - if c == '' or c == '\n': - raise dns.exception.UnexpectedEnd - token += c - if token == '' and ttype != QUOTED_STRING: - if self.multiline: - raise dns.exception.SyntaxError('unbalanced parentheses') - ttype = EOF - return Token(ttype, token, has_escape) - - def unget(self, token): - """Unget a token. - - The unget buffer for tokens is only one token large; it is - an error to try to unget a token when the unget buffer is not - empty. - - @param token: the token to unget - @type token: Token object - @raises UngetBufferFull: there is already an ungotten token - """ - - if not self.ungotten_token is None: - raise UngetBufferFull - self.ungotten_token = token - - def next(self): - """Return the next item in an iteration. - @rtype: (int, string) - """ - - token = self.get() - if token.is_eof(): - raise StopIteration - return token - - def __iter__(self): - return self - - # Helpers - - def get_int(self): - """Read the next token and interpret it as an integer. - - @raises dns.exception.SyntaxError: - @rtype: int - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError('expecting an identifier') - if not token.value.isdigit(): - raise dns.exception.SyntaxError('expecting an integer') - return int(token.value) - - def get_uint8(self): - """Read the next token and interpret it as an 8-bit unsigned - integer. - - @raises dns.exception.SyntaxError: - @rtype: int - """ - - value = self.get_int() - if value < 0 or value > 255: - raise dns.exception.SyntaxError('%d is not an unsigned 8-bit integer' % value) - return value - - def get_uint16(self): - """Read the next token and interpret it as a 16-bit unsigned - integer. - - @raises dns.exception.SyntaxError: - @rtype: int - """ - - value = self.get_int() - if value < 0 or value > 65535: - raise dns.exception.SyntaxError('%d is not an unsigned 16-bit integer' % value) - return value - - def get_uint32(self): - """Read the next token and interpret it as a 32-bit unsigned - integer. - - @raises dns.exception.SyntaxError: - @rtype: int - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError('expecting an identifier') - if not token.value.isdigit(): - raise dns.exception.SyntaxError('expecting an integer') - value = long(token.value) - if value < 0 or value > 4294967296L: - raise dns.exception.SyntaxError('%d is not an unsigned 32-bit integer' % value) - return value - - def get_string(self, origin=None): - """Read the next token and interpret it as a string. - - @raises dns.exception.SyntaxError: - @rtype: string - """ - - token = self.get().unescape() - if not (token.is_identifier() or token.is_quoted_string()): - raise dns.exception.SyntaxError('expecting a string') - return token.value - - def get_identifier(self, origin=None): - """Read the next token and raise an exception if it is not an identifier. - - @raises dns.exception.SyntaxError: - @rtype: string - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError('expecting an identifier') - return token.value - - def get_name(self, origin=None): - """Read the next token and interpret it as a DNS name. - - @raises dns.exception.SyntaxError: - @rtype: dns.name.Name object""" - - token = self.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError('expecting an identifier') - return dns.name.from_text(token.value, origin) - - def get_eol(self): - """Read the next token and raise an exception if it isn't EOL or - EOF. - - @raises dns.exception.SyntaxError: - @rtype: string - """ - - token = self.get() - if not token.is_eol_or_eof(): - raise dns.exception.SyntaxError('expected EOL or EOF, got %d "%s"' % (token.ttype, token.value)) - return token.value - - def get_ttl(self): - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError('expecting an identifier') - return dns.ttl.from_text(token.value) diff --git a/lib/dnspython/dns/tsig.py b/lib/dnspython/dns/tsig.py deleted file mode 100644 index 63b925afbb..0000000000 --- a/lib/dnspython/dns/tsig.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS TSIG support.""" - -import hmac -import struct -import sys - -import dns.exception -import dns.hash -import dns.rdataclass -import dns.name - -class BadTime(dns.exception.DNSException): - """Raised if the current time is not within the TSIG's validity time.""" - pass - -class BadSignature(dns.exception.DNSException): - """Raised if the TSIG signature fails to verify.""" - pass - -class PeerError(dns.exception.DNSException): - """Base class for all TSIG errors generated by the remote peer""" - pass - -class PeerBadKey(PeerError): - """Raised if the peer didn't know the key we used""" - pass - -class PeerBadSignature(PeerError): - """Raised if the peer didn't like the signature we sent""" - pass - -class PeerBadTime(PeerError): - """Raised if the peer didn't like the time we sent""" - pass - -class PeerBadTruncation(PeerError): - """Raised if the peer didn't like amount of truncation in the TSIG we sent""" - pass - -# TSIG Algorithms - -HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") -HMAC_SHA1 = dns.name.from_text("hmac-sha1") -HMAC_SHA224 = dns.name.from_text("hmac-sha224") -HMAC_SHA256 = dns.name.from_text("hmac-sha256") -HMAC_SHA384 = dns.name.from_text("hmac-sha384") -HMAC_SHA512 = dns.name.from_text("hmac-sha512") - -default_algorithm = HMAC_MD5 - -BADSIG = 16 -BADKEY = 17 -BADTIME = 18 -BADTRUNC = 22 - -def sign(wire, keyname, secret, time, fudge, original_id, error, - other_data, request_mac, ctx=None, multi=False, first=True, - algorithm=default_algorithm): - """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata - for the input parameters, the HMAC MAC calculated by applying the - TSIG signature algorithm, and the TSIG digest context. - @rtype: (string, string, hmac.HMAC object) - @raises ValueError: I{other_data} is too long - @raises NotImplementedError: I{algorithm} is not supported - """ - - (algorithm_name, digestmod) = get_algorithm(algorithm) - if first: - ctx = hmac.new(secret, digestmod=digestmod) - ml = len(request_mac) - if ml > 0: - ctx.update(struct.pack('!H', ml)) - ctx.update(request_mac) - id = struct.pack('!H', original_id) - ctx.update(id) - ctx.update(wire[2:]) - if first: - ctx.update(keyname.to_digestable()) - ctx.update(struct.pack('!H', dns.rdataclass.ANY)) - ctx.update(struct.pack('!I', 0)) - long_time = time + 0L - upper_time = (long_time >> 32) & 0xffffL - lower_time = long_time & 0xffffffffL - time_mac = struct.pack('!HIH', upper_time, lower_time, fudge) - pre_mac = algorithm_name + time_mac - ol = len(other_data) - if ol > 65535: - raise ValueError('TSIG Other Data is > 65535 bytes') - post_mac = struct.pack('!HH', error, ol) + other_data - if first: - ctx.update(pre_mac) - ctx.update(post_mac) - else: - ctx.update(time_mac) - mac = ctx.digest() - mpack = struct.pack('!H', len(mac)) - tsig_rdata = pre_mac + mpack + mac + id + post_mac - if multi: - ctx = hmac.new(secret) - ml = len(mac) - ctx.update(struct.pack('!H', ml)) - ctx.update(mac) - else: - ctx = None - return (tsig_rdata, mac, ctx) - -def hmac_md5(wire, keyname, secret, time, fudge, original_id, error, - other_data, request_mac, ctx=None, multi=False, first=True, - algorithm=default_algorithm): - return sign(wire, keyname, secret, time, fudge, original_id, error, - other_data, request_mac, ctx, multi, first, algorithm) - -def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, - tsig_rdlen, ctx=None, multi=False, first=True): - """Validate the specified TSIG rdata against the other input parameters. - - @raises FormError: The TSIG is badly formed. - @raises BadTime: There is too much time skew between the client and the - server. - @raises BadSignature: The TSIG signature did not validate - @rtype: hmac.HMAC object""" - - (adcount,) = struct.unpack("!H", wire[10:12]) - if adcount == 0: - raise dns.exception.FormError - adcount -= 1 - new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] - current = tsig_rdata - (aname, used) = dns.name.from_wire(wire, current) - current = current + used - (upper_time, lower_time, fudge, mac_size) = \ - struct.unpack("!HIHH", wire[current:current + 10]) - time = ((upper_time + 0L) << 32) + (lower_time + 0L) - current += 10 - mac = wire[current:current + mac_size] - current += mac_size - (original_id, error, other_size) = \ - struct.unpack("!HHH", wire[current:current + 6]) - current += 6 - other_data = wire[current:current + other_size] - current += other_size - if current != tsig_rdata + tsig_rdlen: - raise dns.exception.FormError - if error != 0: - if error == BADSIG: - raise PeerBadSignature - elif error == BADKEY: - raise PeerBadKey - elif error == BADTIME: - raise PeerBadTime - elif error == BADTRUNC: - raise PeerBadTruncation - else: - raise PeerError('unknown TSIG error code %d' % error) - time_low = time - fudge - time_high = time + fudge - if now < time_low or now > time_high: - raise BadTime - (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge, - original_id, error, other_data, - request_mac, ctx, multi, first, aname) - if (our_mac != mac): - raise BadSignature - return ctx - -_hashes = None - -def _maybe_add_hash(tsig_alg, hash_alg): - try: - _hashes[tsig_alg] = dns.hash.get(hash_alg) - except KeyError: - pass - -def _setup_hashes(): - global _hashes - _hashes = {} - _maybe_add_hash(HMAC_SHA224, 'SHA224') - _maybe_add_hash(HMAC_SHA256, 'SHA256') - _maybe_add_hash(HMAC_SHA384, 'SHA384') - _maybe_add_hash(HMAC_SHA512, 'SHA512') - _maybe_add_hash(HMAC_SHA1, 'SHA1') - _maybe_add_hash(HMAC_MD5, 'MD5') - -def get_algorithm(algorithm): - """Returns the wire format string and the hash module to use for the - specified TSIG algorithm - - @rtype: (string, hash constructor) - @raises NotImplementedError: I{algorithm} is not supported - """ - - global _hashes - if _hashes is None: - _setup_hashes() - - if isinstance(algorithm, (str, unicode)): - algorithm = dns.name.from_text(algorithm) - - if sys.hexversion < 0x02050200 and \ - (algorithm == HMAC_SHA384 or algorithm == HMAC_SHA512): - raise NotImplementedError("TSIG algorithm " + str(algorithm) + - " requires Python 2.5.2 or later") - - try: - return (algorithm.to_digestable(), _hashes[algorithm]) - except KeyError: - raise NotImplementedError("TSIG algorithm " + str(algorithm) + - " is not supported") diff --git a/lib/dnspython/dns/tsigkeyring.py b/lib/dnspython/dns/tsigkeyring.py deleted file mode 100644 index 0ddd93410f..0000000000 --- a/lib/dnspython/dns/tsigkeyring.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""A place to store TSIG keys.""" - -import base64 - -import dns.name - -def from_text(textring): - """Convert a dictionary containing (textual DNS name, base64 secret) pairs - into a binary keyring which has (dns.name.Name, binary secret) pairs. - @rtype: dict""" - - keyring = {} - for keytext in textring: - keyname = dns.name.from_text(keytext) - secret = base64.decodestring(textring[keytext]) - keyring[keyname] = secret - return keyring - -def to_text(keyring): - """Convert a dictionary containing (dns.name.Name, binary secret) pairs - into a text keyring which has (textual DNS name, base64 secret) pairs. - @rtype: dict""" - - textring = {} - for keyname in keyring: - keytext = dns.name.to_text(keyname) - secret = base64.encodestring(keyring[keyname]) - textring[keytext] = secret - return textring diff --git a/lib/dnspython/dns/ttl.py b/lib/dnspython/dns/ttl.py deleted file mode 100644 index ab6ddf4e66..0000000000 --- a/lib/dnspython/dns/ttl.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS TTL conversion.""" - -import dns.exception - -class BadTTL(dns.exception.SyntaxError): - pass - -def from_text(text): - """Convert the text form of a TTL to an integer. - - The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. - - @param text: the textual TTL - @type text: string - @raises dns.ttl.BadTTL: the TTL is not well-formed - @rtype: int - """ - - if text.isdigit(): - total = long(text) - else: - if not text[0].isdigit(): - raise BadTTL - total = 0L - current = 0L - for c in text: - if c.isdigit(): - current *= 10 - current += long(c) - else: - c = c.lower() - if c == 'w': - total += current * 604800L - elif c == 'd': - total += current * 86400L - elif c == 'h': - total += current * 3600L - elif c == 'm': - total += current * 60L - elif c == 's': - total += current - else: - raise BadTTL("unknown unit '%s'" % c) - current = 0 - if not current == 0: - raise BadTTL("trailing integer") - if total < 0L or total > 2147483647L: - raise BadTTL("TTL should be between 0 and 2^31 - 1 (inclusive)") - return total diff --git a/lib/dnspython/dns/update.py b/lib/dnspython/dns/update.py deleted file mode 100644 index e692226918..0000000000 --- a/lib/dnspython/dns/update.py +++ /dev/null @@ -1,245 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Dynamic Update Support""" - -import dns.message -import dns.name -import dns.opcode -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.tsig - -class Update(dns.message.Message): - def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None, - keyname=None, keyalgorithm=dns.tsig.default_algorithm): - """Initialize a new DNS Update object. - - @param zone: The zone which is being updated. - @type zone: A dns.name.Name or string - @param rdclass: The class of the zone; defaults to dns.rdataclass.IN. - @type rdclass: An int designating the class, or a string whose value - is the name of a class. - @param keyring: The TSIG keyring to use; defaults to None. - @type keyring: dict - @param keyname: The name of the TSIG key to use; defaults to None. - The key must be defined in the keyring. If a keyring is specified - but a keyname is not, then the key used will be the first key in the - keyring. Note that the order of keys in a dictionary is not defined, - so applications should supply a keyname when a keyring is used, unless - they know the keyring contains only one key. - @type keyname: dns.name.Name or string - @param keyalgorithm: The TSIG algorithm to use; defaults to - dns.tsig.default_algorithm. Constants for TSIG algorithms are defined - in dns.tsig, and the currently implemented algorithms are - HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and - HMAC_SHA512. - @type keyalgorithm: string - """ - super(Update, self).__init__() - self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) - if isinstance(zone, (str, unicode)): - zone = dns.name.from_text(zone) - self.origin = zone - if isinstance(rdclass, str): - rdclass = dns.rdataclass.from_text(rdclass) - self.zone_rdclass = rdclass - self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA, - create=True, force_unique=True) - if not keyring is None: - self.use_tsig(keyring, keyname, algorithm=keyalgorithm) - - def _add_rr(self, name, ttl, rd, deleting=None, section=None): - """Add a single RR to the update section.""" - - if section is None: - section = self.authority - covers = rd.covers() - rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype, - covers, deleting, True, True) - rrset.add(rd, ttl) - - def _add(self, replace, section, name, *args): - """Add records. The first argument is the replace mode. If - false, RRs are added to an existing RRset; if true, the RRset - is replaced with the specified contents. The second - argument is the section to add to. The third argument - is always a name. The other arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string...""" - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - if isinstance(args[0], dns.rdataset.Rdataset): - for rds in args: - if replace: - self.delete(name, rds.rdtype) - for rd in rds: - self._add_rr(name, rds.ttl, rd, section=section) - else: - args = list(args) - ttl = int(args.pop(0)) - if isinstance(args[0], dns.rdata.Rdata): - if replace: - self.delete(name, args[0].rdtype) - for rd in args: - self._add_rr(name, ttl, rd, section=section) - else: - rdtype = args.pop(0) - if isinstance(rdtype, str): - rdtype = dns.rdatatype.from_text(rdtype) - if replace: - self.delete(name, rdtype) - for s in args: - rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, - self.origin) - self._add_rr(name, ttl, rd, section=section) - - def add(self, name, *args): - """Add records. The first argument is always a name. The other - arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string...""" - self._add(False, self.authority, name, *args) - - def delete(self, name, *args): - """Delete records. The first argument is always a name. The other - arguments can be: - - - I{nothing} - - - rdataset... - - - rdata... - - - rdtype, [string...]""" - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - if len(args) == 0: - rrset = self.find_rrset(self.authority, name, dns.rdataclass.ANY, - dns.rdatatype.ANY, dns.rdatatype.NONE, - dns.rdatatype.ANY, True, True) - elif isinstance(args[0], dns.rdataset.Rdataset): - for rds in args: - for rd in rds: - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - else: - args = list(args) - if isinstance(args[0], dns.rdata.Rdata): - for rd in args: - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - else: - rdtype = args.pop(0) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if len(args) == 0: - rrset = self.find_rrset(self.authority, name, - self.zone_rdclass, rdtype, - dns.rdatatype.NONE, - dns.rdataclass.ANY, - True, True) - else: - for s in args: - rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, - self.origin) - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - - def replace(self, name, *args): - """Replace records. The first argument is always a name. The other - arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - - Note that if you want to replace the entire node, you should do - a delete of the name followed by one or more calls to add.""" - - self._add(True, self.authority, name, *args) - - def present(self, name, *args): - """Require that an owner name (and optionally an rdata type, - or specific rdataset) exists as a prerequisite to the - execution of the update. The first argument is always a name. - The other arguments can be: - - - rdataset... - - - rdata... - - - rdtype, string...""" - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - if len(args) == 0: - rrset = self.find_rrset(self.answer, name, - dns.rdataclass.ANY, dns.rdatatype.ANY, - dns.rdatatype.NONE, None, - True, True) - elif isinstance(args[0], dns.rdataset.Rdataset) or \ - isinstance(args[0], dns.rdata.Rdata) or \ - len(args) > 1: - if not isinstance(args[0], dns.rdataset.Rdataset): - # Add a 0 TTL - args = list(args) - args.insert(0, 0) - self._add(False, self.answer, name, *args) - else: - rdtype = args[0] - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - rrset = self.find_rrset(self.answer, name, - dns.rdataclass.ANY, rdtype, - dns.rdatatype.NONE, None, - True, True) - - def absent(self, name, rdtype=None): - """Require that an owner name (and optionally an rdata type) does - not exist as a prerequisite to the execution of the update.""" - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - if rdtype is None: - rrset = self.find_rrset(self.answer, name, - dns.rdataclass.NONE, dns.rdatatype.ANY, - dns.rdatatype.NONE, None, - True, True) - else: - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - rrset = self.find_rrset(self.answer, name, - dns.rdataclass.NONE, rdtype, - dns.rdatatype.NONE, None, - True, True) - - def to_wire(self, origin=None, max_size=65535): - """Return a string containing the update in DNS compressed wire - format. - @rtype: string""" - if origin is None: - origin = self.origin - return super(Update, self).to_wire(origin, max_size) diff --git a/lib/dnspython/dns/version.py b/lib/dnspython/dns/version.py deleted file mode 100644 index 7de430b638..0000000000 --- a/lib/dnspython/dns/version.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""dnspython release version information.""" - -MAJOR = 1 -MINOR = 10 -MICRO = 0 -RELEASELEVEL = 0x0f -SERIAL = 0 - -if RELEASELEVEL == 0x0f: - version = '%d.%d.%d' % (MAJOR, MINOR, MICRO) -elif RELEASELEVEL == 0x00: - version = '%d.%d.%dx%d' % \ - (MAJOR, MINOR, MICRO, SERIAL) -else: - version = '%d.%d.%d%x%d' % \ - (MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL) - -hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | \ - SERIAL diff --git a/lib/dnspython/dns/wiredata.py b/lib/dnspython/dns/wiredata.py deleted file mode 100644 index 86d954a90a..0000000000 --- a/lib/dnspython/dns/wiredata.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Wire Data Helper""" - -import sys - -import dns.exception - -class WireData(str): - # WireData is a string with stricter slicing - def __getitem__(self, key): - try: - return WireData(super(WireData, self).__getitem__(key)) - except IndexError: - raise dns.exception.FormError - def __getslice__(self, i, j): - try: - if j == sys.maxint: - # handle the case where the right bound is unspecified - j = len(self) - if i < 0 or j < 0: - raise dns.exception.FormError - # If it's not an empty slice, access left and right bounds - # to make sure they're valid - if i != j: - super(WireData, self).__getitem__(i) - super(WireData, self).__getitem__(j - 1) - return WireData(super(WireData, self).__getslice__(i, j)) - except IndexError: - raise dns.exception.FormError - def __iter__(self): - i = 0 - while 1: - try: - yield self[i] - i += 1 - except dns.exception.FormError: - raise StopIteration - def unwrap(self): - return str(self) - -def maybe_wrap(wire): - if not isinstance(wire, WireData): - return WireData(wire) - else: - return wire diff --git a/lib/dnspython/dns/zone.py b/lib/dnspython/dns/zone.py deleted file mode 100644 index 67c952d3cc..0000000000 --- a/lib/dnspython/dns/zone.py +++ /dev/null @@ -1,855 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Zones.""" - -from __future__ import generators - -import sys - -import dns.exception -import dns.name -import dns.node -import dns.rdataclass -import dns.rdatatype -import dns.rdata -import dns.rrset -import dns.tokenizer -import dns.ttl - -class BadZone(dns.exception.DNSException): - """The zone is malformed.""" - pass - -class NoSOA(BadZone): - """The zone has no SOA RR at its origin.""" - pass - -class NoNS(BadZone): - """The zone has no NS RRset at its origin.""" - pass - -class UnknownOrigin(BadZone): - """The zone's origin is unknown.""" - pass - -class Zone(object): - """A DNS zone. - - A Zone is a mapping from names to nodes. The zone object may be - treated like a Python dictionary, e.g. zone[name] will retrieve - the node associated with that name. The I{name} may be a - dns.name.Name object, or it may be a string. In the either case, - if the name is relative it is treated as relative to the origin of - the zone. - - @ivar rdclass: The zone's rdata class; the default is class IN. - @type rdclass: int - @ivar origin: The origin of the zone. - @type origin: dns.name.Name object - @ivar nodes: A dictionary mapping the names of nodes in the zone to the - nodes themselves. - @type nodes: dict - @ivar relativize: should names in the zone be relativized? - @type relativize: bool - @cvar node_factory: the factory used to create a new node - @type node_factory: class or callable - """ - - node_factory = dns.node.Node - - __slots__ = ['rdclass', 'origin', 'nodes', 'relativize'] - - def __init__(self, origin, rdclass=dns.rdataclass.IN, relativize=True): - """Initialize a zone object. - - @param origin: The origin of the zone. - @type origin: dns.name.Name object - @param rdclass: The zone's rdata class; the default is class IN. - @type rdclass: int""" - - self.rdclass = rdclass - self.origin = origin - self.nodes = {} - self.relativize = relativize - - def __eq__(self, other): - """Two zones are equal if they have the same origin, class, and - nodes. - @rtype: bool - """ - - if not isinstance(other, Zone): - return False - if self.rdclass != other.rdclass or \ - self.origin != other.origin or \ - self.nodes != other.nodes: - return False - return True - - def __ne__(self, other): - """Are two zones not equal? - @rtype: bool - """ - - return not self.__eq__(other) - - def _validate_name(self, name): - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, None) - elif not isinstance(name, dns.name.Name): - raise KeyError("name parameter must be convertable to a DNS name") - if name.is_absolute(): - if not name.is_subdomain(self.origin): - raise KeyError("name parameter must be a subdomain of the zone origin") - if self.relativize: - name = name.relativize(self.origin) - return name - - def __getitem__(self, key): - key = self._validate_name(key) - return self.nodes[key] - - def __setitem__(self, key, value): - key = self._validate_name(key) - self.nodes[key] = value - - def __delitem__(self, key): - key = self._validate_name(key) - del self.nodes[key] - - def __iter__(self): - return self.nodes.iterkeys() - - def iterkeys(self): - return self.nodes.iterkeys() - - def keys(self): - return self.nodes.keys() - - def itervalues(self): - return self.nodes.itervalues() - - def values(self): - return self.nodes.values() - - def iteritems(self): - return self.nodes.iteritems() - - def items(self): - return self.nodes.items() - - def get(self, key): - key = self._validate_name(key) - return self.nodes.get(key) - - def __contains__(self, other): - return other in self.nodes - - def find_node(self, name, create=False): - """Find a node in the zone, possibly creating it. - - @param name: the name of the node to find - @type name: dns.name.Name object or string - @param create: should the node be created if it doesn't exist? - @type create: bool - @raises KeyError: the name is not known and create was not specified. - @rtype: dns.node.Node object - """ - - name = self._validate_name(name) - node = self.nodes.get(name) - if node is None: - if not create: - raise KeyError - node = self.node_factory() - self.nodes[name] = node - return node - - def get_node(self, name, create=False): - """Get a node in the zone, possibly creating it. - - This method is like L{find_node}, except it returns None instead - of raising an exception if the node does not exist and creation - has not been requested. - - @param name: the name of the node to find - @type name: dns.name.Name object or string - @param create: should the node be created if it doesn't exist? - @type create: bool - @rtype: dns.node.Node object or None - """ - - try: - node = self.find_node(name, create) - except KeyError: - node = None - return node - - def delete_node(self, name): - """Delete the specified node if it exists. - - It is not an error if the node does not exist. - """ - - name = self._validate_name(name) - if self.nodes.has_key(name): - del self.nodes[name] - - def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, - create=False): - """Look for rdata with the specified name and type in the zone, - and return an rdataset encapsulating it. - - The I{name}, I{rdtype}, and I{covers} parameters may be - strings, in which case they will be converted to their proper - type. - - The rdataset returned is not a copy; changes to it will change - the zone. - - KeyError is raised if the name or type are not found. - Use L{get_rdataset} if you want to have None returned instead. - - @param name: the owner name to look for - @type name: DNS.name.Name object or string - @param rdtype: the rdata type desired - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - @param create: should the node and rdataset be created if they do not - exist? - @type create: bool - @raises KeyError: the node or rdata could not be found - @rtype: dns.rrset.RRset object - """ - - name = self._validate_name(name) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): - covers = dns.rdatatype.from_text(covers) - node = self.find_node(name, create) - return node.find_rdataset(self.rdclass, rdtype, covers, create) - - def get_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, - create=False): - """Look for rdata with the specified name and type in the zone, - and return an rdataset encapsulating it. - - The I{name}, I{rdtype}, and I{covers} parameters may be - strings, in which case they will be converted to their proper - type. - - The rdataset returned is not a copy; changes to it will change - the zone. - - None is returned if the name or type are not found. - Use L{find_rdataset} if you want to have KeyError raised instead. - - @param name: the owner name to look for - @type name: DNS.name.Name object or string - @param rdtype: the rdata type desired - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - @param create: should the node and rdataset be created if they do not - exist? - @type create: bool - @rtype: dns.rrset.RRset object - """ - - try: - rdataset = self.find_rdataset(name, rdtype, covers, create) - except KeyError: - rdataset = None - return rdataset - - def delete_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE): - """Delete the rdataset matching I{rdtype} and I{covers}, if it - exists at the node specified by I{name}. - - The I{name}, I{rdtype}, and I{covers} parameters may be - strings, in which case they will be converted to their proper - type. - - It is not an error if the node does not exist, or if there is no - matching rdataset at the node. - - If the node has no rdatasets after the deletion, it will itself - be deleted. - - @param name: the owner name to look for - @type name: DNS.name.Name object or string - @param rdtype: the rdata type desired - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - """ - - name = self._validate_name(name) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): - covers = dns.rdatatype.from_text(covers) - node = self.get_node(name) - if not node is None: - node.delete_rdataset(self.rdclass, rdtype, covers) - if len(node) == 0: - self.delete_node(name) - - def replace_rdataset(self, name, replacement): - """Replace an rdataset at name. - - It is not an error if there is no rdataset matching I{replacement}. - - Ownership of the I{replacement} object is transferred to the zone; - in other words, this method does not store a copy of I{replacement} - at the node, it stores I{replacement} itself. - - If the I{name} node does not exist, it is created. - - @param name: the owner name - @type name: DNS.name.Name object or string - @param replacement: the replacement rdataset - @type replacement: dns.rdataset.Rdataset - """ - - if replacement.rdclass != self.rdclass: - raise ValueError('replacement.rdclass != zone.rdclass') - node = self.find_node(name, True) - node.replace_rdataset(replacement) - - def find_rrset(self, name, rdtype, covers=dns.rdatatype.NONE): - """Look for rdata with the specified name and type in the zone, - and return an RRset encapsulating it. - - The I{name}, I{rdtype}, and I{covers} parameters may be - strings, in which case they will be converted to their proper - type. - - This method is less efficient than the similar - L{find_rdataset} because it creates an RRset instead of - returning the matching rdataset. It may be more convenient - for some uses since it returns an object which binds the owner - name to the rdata. - - This method may not be used to create new nodes or rdatasets; - use L{find_rdataset} instead. - - KeyError is raised if the name or type are not found. - Use L{get_rrset} if you want to have None returned instead. - - @param name: the owner name to look for - @type name: DNS.name.Name object or string - @param rdtype: the rdata type desired - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - @raises KeyError: the node or rdata could not be found - @rtype: dns.rrset.RRset object - """ - - name = self._validate_name(name) - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): - covers = dns.rdatatype.from_text(covers) - rdataset = self.nodes[name].find_rdataset(self.rdclass, rdtype, covers) - rrset = dns.rrset.RRset(name, self.rdclass, rdtype, covers) - rrset.update(rdataset) - return rrset - - def get_rrset(self, name, rdtype, covers=dns.rdatatype.NONE): - """Look for rdata with the specified name and type in the zone, - and return an RRset encapsulating it. - - The I{name}, I{rdtype}, and I{covers} parameters may be - strings, in which case they will be converted to their proper - type. - - This method is less efficient than the similar L{get_rdataset} - because it creates an RRset instead of returning the matching - rdataset. It may be more convenient for some uses since it - returns an object which binds the owner name to the rdata. - - This method may not be used to create new nodes or rdatasets; - use L{find_rdataset} instead. - - None is returned if the name or type are not found. - Use L{find_rrset} if you want to have KeyError raised instead. - - @param name: the owner name to look for - @type name: DNS.name.Name object or string - @param rdtype: the rdata type desired - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - @rtype: dns.rrset.RRset object - """ - - try: - rrset = self.find_rrset(name, rdtype, covers) - except KeyError: - rrset = None - return rrset - - def iterate_rdatasets(self, rdtype=dns.rdatatype.ANY, - covers=dns.rdatatype.NONE): - """Return a generator which yields (name, rdataset) tuples for - all rdatasets in the zone which have the specified I{rdtype} - and I{covers}. If I{rdtype} is dns.rdatatype.ANY, the default, - then all rdatasets will be matched. - - @param rdtype: int or string - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - """ - - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): - covers = dns.rdatatype.from_text(covers) - for (name, node) in self.iteritems(): - for rds in node: - if rdtype == dns.rdatatype.ANY or \ - (rds.rdtype == rdtype and rds.covers == covers): - yield (name, rds) - - def iterate_rdatas(self, rdtype=dns.rdatatype.ANY, - covers=dns.rdatatype.NONE): - """Return a generator which yields (name, ttl, rdata) tuples for - all rdatas in the zone which have the specified I{rdtype} - and I{covers}. If I{rdtype} is dns.rdatatype.ANY, the default, - then all rdatas will be matched. - - @param rdtype: int or string - @type rdtype: int or string - @param covers: the covered type (defaults to None) - @type covers: int or string - """ - - if isinstance(rdtype, (str, unicode)): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): - covers = dns.rdatatype.from_text(covers) - for (name, node) in self.iteritems(): - for rds in node: - if rdtype == dns.rdatatype.ANY or \ - (rds.rdtype == rdtype and rds.covers == covers): - for rdata in rds: - yield (name, rds.ttl, rdata) - - def to_file(self, f, sorted=True, relativize=True, nl=None): - """Write a zone to a file. - - @param f: file or string. If I{f} is a string, it is treated - as the name of a file to open. - @param sorted: if True, the file will be written with the - names sorted in DNSSEC order from least to greatest. Otherwise - the names will be written in whatever order they happen to have - in the zone's dictionary. - @param relativize: if True, domain names in the output will be - relativized to the zone's origin (if possible). - @type relativize: bool - @param nl: The end of line string. If not specified, the - output will use the platform's native end-of-line marker (i.e. - LF on POSIX, CRLF on Windows, CR on Macintosh). - @type nl: string or None - """ - - if sys.hexversion >= 0x02030000: - # allow Unicode filenames - str_type = basestring - else: - str_type = str - if nl is None: - opts = 'w' - else: - opts = 'wb' - if isinstance(f, str_type): - f = file(f, opts) - want_close = True - else: - want_close = False - try: - if sorted: - names = self.keys() - names.sort() - else: - names = self.iterkeys() - for n in names: - l = self[n].to_text(n, origin=self.origin, - relativize=relativize) - if nl is None: - print >> f, l - else: - f.write(l) - f.write(nl) - finally: - if want_close: - f.close() - - def check_origin(self): - """Do some simple checking of the zone's origin. - - @raises dns.zone.NoSOA: there is no SOA RR - @raises dns.zone.NoNS: there is no NS RRset - @raises KeyError: there is no origin node - """ - if self.relativize: - name = dns.name.empty - else: - name = self.origin - if self.get_rdataset(name, dns.rdatatype.SOA) is None: - raise NoSOA - if self.get_rdataset(name, dns.rdatatype.NS) is None: - raise NoNS - - -class _MasterReader(object): - """Read a DNS master file - - @ivar tok: The tokenizer - @type tok: dns.tokenizer.Tokenizer object - @ivar ttl: The default TTL - @type ttl: int - @ivar last_name: The last name read - @type last_name: dns.name.Name object - @ivar current_origin: The current origin - @type current_origin: dns.name.Name object - @ivar relativize: should names in the zone be relativized? - @type relativize: bool - @ivar zone: the zone - @type zone: dns.zone.Zone object - @ivar saved_state: saved reader state (used when processing $INCLUDE) - @type saved_state: list of (tokenizer, current_origin, last_name, file) - tuples. - @ivar current_file: the file object of the $INCLUDed file being parsed - (None if no $INCLUDE is active). - @ivar allow_include: is $INCLUDE allowed? - @type allow_include: bool - @ivar check_origin: should sanity checks of the origin node be done? - The default is True. - @type check_origin: bool - """ - - def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone, - allow_include=False, check_origin=True): - if isinstance(origin, (str, unicode)): - origin = dns.name.from_text(origin) - self.tok = tok - self.current_origin = origin - self.relativize = relativize - self.ttl = 0 - self.last_name = None - self.zone = zone_factory(origin, rdclass, relativize=relativize) - self.saved_state = [] - self.current_file = None - self.allow_include = allow_include - self.check_origin = check_origin - - def _eat_line(self): - while 1: - token = self.tok.get() - if token.is_eol_or_eof(): - break - - def _rr_line(self): - """Process one line from a DNS master file.""" - # Name - if self.current_origin is None: - raise UnknownOrigin - token = self.tok.get(want_leading = True) - if not token.is_whitespace(): - self.last_name = dns.name.from_text(token.value, self.current_origin) - else: - token = self.tok.get() - if token.is_eol_or_eof(): - # treat leading WS followed by EOL/EOF as if they were EOL/EOF. - return - self.tok.unget(token) - name = self.last_name - if not name.is_subdomain(self.zone.origin): - self._eat_line() - return - if self.relativize: - name = name.relativize(self.zone.origin) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # TTL - try: - ttl = dns.ttl.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.ttl.BadTTL: - ttl = self.ttl - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except: - rdclass = self.zone.rdclass - if rdclass != self.zone.rdclass: - raise dns.exception.SyntaxError("RR class is not zone's class") - # Type - try: - rdtype = dns.rdatatype.from_text(token.value) - except: - raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) - n = self.zone.nodes.get(name) - if n is None: - n = self.zone.node_factory() - self.zone.nodes[name] = n - try: - rd = dns.rdata.from_text(rdclass, rdtype, self.tok, - self.current_origin, False) - except dns.exception.SyntaxError: - # Catch and reraise. - (ty, va) = sys.exc_info()[:2] - raise va - except: - # All exceptions that occur in the processing of rdata - # are treated as syntax errors. This is not strictly - # correct, but it is correct almost all of the time. - # We convert them to syntax errors so that we can emit - # helpful filename:line info. - (ty, va) = sys.exc_info()[:2] - raise dns.exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) - - rd.choose_relativity(self.zone.origin, self.relativize) - covers = rd.covers() - rds = n.find_rdataset(rdclass, rdtype, covers, True) - rds.add(rd, ttl) - - def read(self): - """Read a DNS master file and build a zone object. - - @raises dns.zone.NoSOA: No SOA RR was found at the zone origin - @raises dns.zone.NoNS: No NS RRset was found at the zone origin - """ - - try: - while 1: - token = self.tok.get(True, True).unescape() - if token.is_eof(): - if not self.current_file is None: - self.current_file.close() - if len(self.saved_state) > 0: - (self.tok, - self.current_origin, - self.last_name, - self.current_file, - self.ttl) = self.saved_state.pop(-1) - continue - break - elif token.is_eol(): - continue - elif token.is_comment(): - self.tok.get_eol() - continue - elif token.value[0] == '$': - u = token.value.upper() - if u == '$TTL': - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError("bad $TTL") - self.ttl = dns.ttl.from_text(token.value) - self.tok.get_eol() - elif u == '$ORIGIN': - self.current_origin = self.tok.get_name() - self.tok.get_eol() - if self.zone.origin is None: - self.zone.origin = self.current_origin - elif u == '$INCLUDE' and self.allow_include: - token = self.tok.get() - if not token.is_quoted_string(): - raise dns.exception.SyntaxError("bad filename in $INCLUDE") - filename = token.value - token = self.tok.get() - if token.is_identifier(): - new_origin = dns.name.from_text(token.value, \ - self.current_origin) - self.tok.get_eol() - elif not token.is_eol_or_eof(): - raise dns.exception.SyntaxError("bad origin in $INCLUDE") - else: - new_origin = self.current_origin - self.saved_state.append((self.tok, - self.current_origin, - self.last_name, - self.current_file, - self.ttl)) - self.current_file = file(filename, 'r') - self.tok = dns.tokenizer.Tokenizer(self.current_file, - filename) - self.current_origin = new_origin - else: - raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'") - continue - self.tok.unget(token) - self._rr_line() - except dns.exception.SyntaxError, detail: - (filename, line_number) = self.tok.where() - if detail is None: - detail = "syntax error" - raise dns.exception.SyntaxError("%s:%d: %s" % (filename, line_number, detail)) - - # Now that we're done reading, do some basic checking of the zone. - if self.check_origin: - self.zone.check_origin() - -def from_text(text, origin = None, rdclass = dns.rdataclass.IN, - relativize = True, zone_factory=Zone, filename=None, - allow_include=False, check_origin=True): - """Build a zone object from a master file format string. - - @param text: the master file format input - @type text: string. - @param origin: The origin of the zone; if not specified, the first - $ORIGIN statement in the master file will determine the origin of the - zone. - @type origin: dns.name.Name object or string - @param rdclass: The zone's rdata class; the default is class IN. - @type rdclass: int - @param relativize: should names be relativized? The default is True - @type relativize: bool - @param zone_factory: The zone factory to use - @type zone_factory: function returning a Zone - @param filename: The filename to emit when describing where an error - occurred; the default is ''. - @type filename: string - @param allow_include: is $INCLUDE allowed? - @type allow_include: bool - @param check_origin: should sanity checks of the origin node be done? - The default is True. - @type check_origin: bool - @raises dns.zone.NoSOA: No SOA RR was found at the zone origin - @raises dns.zone.NoNS: No NS RRset was found at the zone origin - @rtype: dns.zone.Zone object - """ - - # 'text' can also be a file, but we don't publish that fact - # since it's an implementation detail. The official file - # interface is from_file(). - - if filename is None: - filename = '' - tok = dns.tokenizer.Tokenizer(text, filename) - reader = _MasterReader(tok, origin, rdclass, relativize, zone_factory, - allow_include=allow_include, - check_origin=check_origin) - reader.read() - return reader.zone - -def from_file(f, origin = None, rdclass = dns.rdataclass.IN, - relativize = True, zone_factory=Zone, filename=None, - allow_include=True, check_origin=True): - """Read a master file and build a zone object. - - @param f: file or string. If I{f} is a string, it is treated - as the name of a file to open. - @param origin: The origin of the zone; if not specified, the first - $ORIGIN statement in the master file will determine the origin of the - zone. - @type origin: dns.name.Name object or string - @param rdclass: The zone's rdata class; the default is class IN. - @type rdclass: int - @param relativize: should names be relativized? The default is True - @type relativize: bool - @param zone_factory: The zone factory to use - @type zone_factory: function returning a Zone - @param filename: The filename to emit when describing where an error - occurred; the default is '', or the value of I{f} if I{f} is a - string. - @type filename: string - @param allow_include: is $INCLUDE allowed? - @type allow_include: bool - @param check_origin: should sanity checks of the origin node be done? - The default is True. - @type check_origin: bool - @raises dns.zone.NoSOA: No SOA RR was found at the zone origin - @raises dns.zone.NoNS: No NS RRset was found at the zone origin - @rtype: dns.zone.Zone object - """ - - if sys.hexversion >= 0x02030000: - # allow Unicode filenames; turn on universal newline support - str_type = basestring - opts = 'rU' - else: - str_type = str - opts = 'r' - if isinstance(f, str_type): - if filename is None: - filename = f - f = file(f, opts) - want_close = True - else: - if filename is None: - filename = '' - want_close = False - - try: - z = from_text(f, origin, rdclass, relativize, zone_factory, - filename, allow_include, check_origin) - finally: - if want_close: - f.close() - return z - -def from_xfr(xfr, zone_factory=Zone, relativize=True): - """Convert the output of a zone transfer generator into a zone object. - - @param xfr: The xfr generator - @type xfr: generator of dns.message.Message objects - @param relativize: should names be relativized? The default is True. - It is essential that the relativize setting matches the one specified - to dns.query.xfr(). - @type relativize: bool - @raises dns.zone.NoSOA: No SOA RR was found at the zone origin - @raises dns.zone.NoNS: No NS RRset was found at the zone origin - @rtype: dns.zone.Zone object - """ - - z = None - for r in xfr: - if z is None: - if relativize: - origin = r.origin - else: - origin = r.answer[0].name - rdclass = r.answer[0].rdclass - z = zone_factory(origin, rdclass, relativize=relativize) - for rrset in r.answer: - znode = z.nodes.get(rrset.name) - if not znode: - znode = z.node_factory() - z.nodes[rrset.name] = znode - zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype, - rrset.covers, True) - zrds.update_ttl(rrset.ttl) - for rd in rrset: - rd.choose_relativity(z.origin, relativize) - zrds.add(rd) - z.check_origin() - return z diff --git a/lib/dnspython/examples/ddns.py b/lib/dnspython/examples/ddns.py deleted file mode 100755 index 84814b73cf..0000000000 --- a/lib/dnspython/examples/ddns.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python - -# -# Use a TSIG-signed DDNS update to update our hostname-to-address -# mapping. -# -# usage: ddns.py -# -# On linux systems, you can automatically update your DNS any time an -# interface comes up by adding an ifup-local script that invokes this -# python code. -# -# E.g. on my systems I have this -# -# #!/bin/sh -# -# DEVICE=$1 -# -# if [ "X${DEVICE}" == "Xeth0" ]; then -# IPADDR=`LANG= LC_ALL= ifconfig ${DEVICE} | grep 'inet addr' | -# awk -F: '{ print $2 } ' | awk '{ print $1 }'` -# /usr/local/sbin/ddns.py $IPADDR -# fi -# -# in /etc/ifup-local. -# - -import sys - -import dns.update -import dns.query -import dns.tsigkeyring - -# -# Replace the keyname and secret with appropriate values for your -# configuration. -# -keyring = dns.tsigkeyring.from_text({ - 'keyname.' : 'NjHwPsMKjdN++dOfE5iAiQ==' - }) - -# -# Replace "example." with your domain, and "host" with your hostname. -# -update = dns.update.Update('example.', keyring=keyring) -update.replace('host', 300, 'A', sys.argv[1]) - -# -# Replace "10.0.0.1" with the IP address of your master server. -# -response = dns.query.tcp(update, '10.0.0.1', timeout=10) diff --git a/lib/dnspython/examples/e164.py b/lib/dnspython/examples/e164.py deleted file mode 100755 index ad40ccf84b..0000000000 --- a/lib/dnspython/examples/e164.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -import dns.e164 -n = dns.e164.from_e164("+1 555 1212") -print n -print dns.e164.to_e164(n) diff --git a/lib/dnspython/examples/mx.py b/lib/dnspython/examples/mx.py deleted file mode 100755 index 3036e70ddf..0000000000 --- a/lib/dnspython/examples/mx.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python - -import dns.resolver - -answers = dns.resolver.query('nominum.com', 'MX') -for rdata in answers: - print 'Host', rdata.exchange, 'has preference', rdata.preference diff --git a/lib/dnspython/examples/name.py b/lib/dnspython/examples/name.py deleted file mode 100755 index b099c49d16..0000000000 --- a/lib/dnspython/examples/name.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python - -import dns.name - -n = dns.name.from_text('www.dnspython.org') -o = dns.name.from_text('dnspython.org') -print n.is_subdomain(o) # True -print n.is_superdomain(o) # False -print n > o # True -rel = n.relativize(o) # rel is the relative name www -n2 = rel + o -print n2 == n # True -print n.labels # ['www', 'dnspython', 'org', ''] diff --git a/lib/dnspython/examples/reverse.py b/lib/dnspython/examples/reverse.py deleted file mode 100755 index 8657baed44..0000000000 --- a/lib/dnspython/examples/reverse.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python - -# Usage: reverse.py ... -# -# This demo script will load in all of the zones specified by the -# filenames on the command line, find all the A RRs in them, and -# construct a reverse mapping table that maps each IP address used to -# the list of names mapping to that address. The table is then sorted -# nicely and printed. -# -# Note! The zone name is taken from the basename of the filename, so -# you must use filenames like "/wherever/you/like/dnspython.org" and -# not something like "/wherever/you/like/foo.db" (unless you're -# working with the ".db" GTLD, of course :)). -# -# If this weren't a demo script, there'd be a way of specifying the -# origin for each zone instead of constructing it from the filename. - -import dns.zone -import dns.ipv4 -import os.path -import sys - -reverse_map = {} - -for filename in sys.argv[1:]: - zone = dns.zone.from_file(filename, os.path.basename(filename), - relativize=False) - for (name, ttl, rdata) in zone.iterate_rdatas('A'): - try: - reverse_map[rdata.address].append(name.to_text()) - except KeyError: - reverse_map[rdata.address] = [name.to_text()] - -keys = reverse_map.keys() -keys.sort(lambda a1, a2: cmp(dns.ipv4.inet_aton(a1), dns.ipv4.inet_aton(a2))) -for k in keys: - v = reverse_map[k] - v.sort() - print k, v diff --git a/lib/dnspython/examples/reverse_name.py b/lib/dnspython/examples/reverse_name.py deleted file mode 100755 index 351896b015..0000000000 --- a/lib/dnspython/examples/reverse_name.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -import dns.reversename -n = dns.reversename.from_address("127.0.0.1") -print n -print dns.reversename.to_address(n) diff --git a/lib/dnspython/examples/xfr.py b/lib/dnspython/examples/xfr.py deleted file mode 100755 index e67ab18927..0000000000 --- a/lib/dnspython/examples/xfr.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -import dns.query -import dns.resolver -import dns.zone - -soa_answer = dns.resolver.query('dnspython.org', 'SOA') -master_answer = dns.resolver.query(soa_answer[0].mname, 'A') - -z = dns.zone.from_xfr(dns.query.xfr(master_answer[0].address, 'dnspython.org')) -names = z.nodes.keys() -names.sort() -for n in names: - print z[n].to_text(n) diff --git a/lib/dnspython/examples/zonediff.py b/lib/dnspython/examples/zonediff.py deleted file mode 100755 index ad81fb1d2d..0000000000 --- a/lib/dnspython/examples/zonediff.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python -# -# Small library and commandline tool to do logical diffs of zonefiles -# ./zonediff -h gives you help output -# -# Requires dnspython to do all the heavy lifting -# -# (c)2009 Dennis Kaarsemaker -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -"""See diff_zones.__doc__ for more information""" - -__all__ = ['diff_zones', 'format_changes_plain', 'format_changes_html'] - -try: - import dns.zone -except ImportError: - import sys - sys.stderr.write("Please install dnspython") - sys.exit(1) - -def diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False): - """diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False) -> changes - Compares two dns.zone.Zone objects and returns a list of all changes - in the format (name, oldnode, newnode). - - If ignore_ttl is true, a node will not be added to this list if the - only change is its TTL. - - If ignore_soa is true, a node will not be added to this list if the - only changes is a change in a SOA Rdata set. - - The returned nodes do include all Rdata sets, including unchanged ones. - """ - - changes = [] - for name in zone1: - name = str(name) - n1 = zone1.get_node(name) - n2 = zone2.get_node(name) - if not n2: - changes.append((str(name), n1, n2)) - elif _nodes_differ(n1, n2, ignore_ttl, ignore_soa): - changes.append((str(name), n1, n2)) - - for name in zone2: - n1 = zone1.get_node(name) - if not n1: - n2 = zone2.get_node(name) - changes.append((str(name), n1, n2)) - return changes - -def _nodes_differ(n1, n2, ignore_ttl, ignore_soa): - if ignore_soa or not ignore_ttl: - # Compare datasets directly - for r in n1.rdatasets: - if ignore_soa and r.rdtype == dns.rdatatype.SOA: - continue - if r not in n2.rdatasets: - return True - if not ignore_ttl: - return r.ttl != n2.find_rdataset(r.rdclass, r.rdtype).ttl - - for r in n2.rdatasets: - if ignore_soa and r.rdtype == dns.rdatatype.SOA: - continue - if r not in n1.rdatasets: - return True - else: - return n1 != n2 - -def format_changes_plain(oldf, newf, changes, ignore_ttl=False): - """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str - Given 2 filenames and a list of changes from diff_zones, produce diff-like - output. If ignore_ttl is True, TTL-only changes are not displayed""" - - ret = "--- %s\n+++ %s\n" % (oldf, newf) - for name, old, new in changes: - ret += "@ %s\n" % name - if not old: - for r in new.rdatasets: - ret += "+ %s\n" % str(r).replace('\n','\n+ ') - elif not new: - for r in old.rdatasets: - ret += "- %s\n" % str(r).replace('\n','\n+ ') - else: - for r in old.rdatasets: - if r not in new.rdatasets or (r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): - ret += "- %s\n" % str(r).replace('\n','\n+ ') - for r in new.rdatasets: - if r not in old.rdatasets or (r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): - ret += "+ %s\n" % str(r).replace('\n','\n+ ') - return ret - -def format_changes_html(oldf, newf, changes, ignore_ttl=False): - """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str - Given 2 filenames and a list of changes from diff_zones, produce nice html - output. If ignore_ttl is True, TTL-only changes are not displayed""" - - ret = ''' - - - - - - - - \n''' % (oldf, newf) - - for name, old, new in changes: - ret += ' \n \n' % name - if not old: - for r in new.rdatasets: - ret += ' \n \n' % str(r).replace('\n','
') - elif not new: - for r in old.rdatasets: - ret += ' \n \n' % str(r).replace('\n','
') - else: - ret += ' \n' - ret += ' \n' - ret += ' \n' - return ret + ' \n
 %s%s
%s %s%s ' - for r in old.rdatasets: - if r not in new.rdatasets or (r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): - ret += str(r).replace('\n','
') - ret += '
' - for r in new.rdatasets: - if r not in old.rdatasets or (r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): - ret += str(r).replace('\n','
') - ret += '
' - -# Make this module usable as a script too. -if __name__ == '__main__': - import optparse - import subprocess - import sys - import traceback - - usage = """%prog zonefile1 zonefile2 - Show differences between zones in a diff-like format -%prog [--git|--bzr|--rcs] zonefile rev1 [rev2] - Show differences between two revisions of a zonefile - -The differences shown will be logical differences, not textual differences. -""" - p = optparse.OptionParser(usage=usage) - p.add_option('-s', '--ignore-soa', action="store_true", default=False, dest="ignore_soa", - help="Ignore SOA-only changes to records") - p.add_option('-t', '--ignore-ttl', action="store_true", default=False, dest="ignore_ttl", - help="Ignore TTL-only changes to Rdata") - p.add_option('-T', '--traceback', action="store_true", default=False, dest="tracebacks", - help="Show python tracebacks when errors occur") - p.add_option('-H', '--html', action="store_true", default=False, dest="html", - help="Print HTML output") - p.add_option('-g', '--git', action="store_true", default=False, dest="use_git", - help="Use git revisions instead of real files") - p.add_option('-b', '--bzr', action="store_true", default=False, dest="use_bzr", - help="Use bzr revisions instead of real files") - p.add_option('-r', '--rcs', action="store_true", default=False, dest="use_rcs", - help="Use rcs revisions instead of real files") - opts, args = p.parse_args() - opts.use_vc = opts.use_git or opts.use_bzr or opts.use_rcs - - def _open(what, err): - if isinstance(what, basestring): - # Open as normal file - try: - return open(what, 'rb') - except: - sys.stderr.write(err + "\n") - if opts.tracebacks: - traceback.print_exc() - else: - # Must be a list, open subprocess - try: - proc = subprocess.Popen(what, stdout=subprocess.PIPE) - proc.wait() - if proc.returncode == 0: - return proc.stdout - sys.stderr.write(err + "\n") - except: - sys.stderr.write(err + "\n") - if opts.tracebacks: - traceback.print_exc() - - if not opts.use_vc and len(args) != 2: - p.print_help() - sys.exit(64) - if opts.use_vc and len(args) not in (2,3): - p.print_help() - sys.exit(64) - - # Open file desriptors - if not opts.use_vc: - oldn, newn = args - else: - if len(args) == 3: - filename, oldr, newr = args - oldn = "%s:%s" % (oldr, filename) - newn = "%s:%s" % (newr, filename) - else: - filename, oldr = args - newr = None - oldn = "%s:%s" % (oldr, filename) - newn = filename - - - old, new = None, None - oldz, newz = None, None - if opts.use_bzr: - old = _open(["bzr", "cat", "-r" + oldr, filename], - "Unable to retrieve revision %s of %s" % (oldr, filename)) - if newr != None: - new = _open(["bzr", "cat", "-r" + newr, filename], - "Unable to retrieve revision %s of %s" % (newr, filename)) - elif opts.use_git: - old = _open(["git", "show", oldn], - "Unable to retrieve revision %s of %s" % (oldr, filename)) - if newr != None: - new = _open(["git", "show", newn], - "Unable to retrieve revision %s of %s" % (newr, filename)) - elif opts.use_rcs: - old = _open(["co", "-q", "-p", "-r" + oldr, filename], - "Unable to retrieve revision %s of %s" % (oldr, filename)) - if newr != None: - new = _open(["co", "-q", "-p", "-r" + newr, filename], - "Unable to retrieve revision %s of %s" % (newr, filename)) - if not opts.use_vc: - old = _open(oldn, "Unable to open %s" % oldn) - if not opts.use_vc or newr == None: - new = _open(newn, "Unable to open %s" % newn) - - if not old or not new: - sys.exit(65) - - # Parse the zones - try: - oldz = dns.zone.from_file(old, origin = '.', check_origin=False) - except dns.exception.DNSException: - sys.stderr.write("Incorrect zonefile: %s\n", old) - if opts.tracebacks: - traceback.print_exc() - try: - newz = dns.zone.from_file(new, origin = '.', check_origin=False) - except dns.exception.DNSException: - sys.stderr.write("Incorrect zonefile: %s\n" % new) - if opts.tracebacks: - traceback.print_exc() - if not oldz or not newz: - sys.exit(65) - - changes = diff_zones(oldz, newz, opts.ignore_ttl, opts.ignore_soa) - changes.sort() - - if not changes: - sys.exit(0) - if opts.html: - print format_changes_html(oldn, newn, changes, opts.ignore_ttl) - else: - print format_changes_plain(oldn, newn, changes, opts.ignore_ttl) - sys.exit(1) diff --git a/lib/dnspython/setup.py b/lib/dnspython/setup.py deleted file mode 100755 index 33d7c20679..0000000000 --- a/lib/dnspython/setup.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import sys -from distutils.core import setup - -version = '1.10.0' - -kwargs = { - 'name' : 'dnspython', - 'version' : version, - 'description' : 'DNS toolkit', - 'long_description' : \ - """dnspython is a DNS toolkit for Python. It supports almost all -record types. It can be used for queries, zone transfers, and dynamic -updates. It supports TSIG authenticated messages and EDNS0. - -dnspython provides both high and low level access to DNS. The high -level classes perform queries for data of a given name, type, and -class, and return an answer set. The low level classes allow -direct manipulation of DNS zones, messages, names, and records.""", - 'author' : 'Bob Halley', - 'author_email' : 'halley@dnspython.org', - 'license' : 'BSD-like', - 'url' : 'http://www.dnspython.org', - 'packages' : ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY'], - 'download_url' : \ - 'http://www.dnspython.org/kits/%s/dnspython-%s.tar.gz' % (version, version), - 'classifiers' : [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: System Administrators", - "License :: Freeware", - "Operating System :: Microsoft :: Windows :: Windows 95/98/2000", - "Operating System :: POSIX", - "Programming Language :: Python", - "Topic :: Internet :: Name Service (DNS)", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - } - -if sys.hexversion >= 0x02050000: - kwargs['requires'] = [] - kwargs['provides'] = ['dns'] - -setup(**kwargs) diff --git a/lib/dnspython/tests/Makefile b/lib/dnspython/tests/Makefile deleted file mode 100644 index 6ab444f451..0000000000 --- a/lib/dnspython/tests/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -# $Id: Makefile,v 1.5 2004/03/19 00:17:27 halley Exp $ - -PYTHON=python - -check: test - -test: - @for i in *.py; do \ - echo "Running $$i:"; \ - ${PYTHON} $$i || exit 1; \ - done diff --git a/lib/dnspython/tests/bugs.py b/lib/dnspython/tests/bugs.py deleted file mode 100644 index c2fa6b6659..0000000000 --- a/lib/dnspython/tests/bugs.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.ttl - -class BugsTestCase(unittest.TestCase): - - def test_float_LOC(self): - rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.LOC, - "30 30 0.000 N 100 30 0.000 W 10.00m 20m 2000m 20m") - self.failUnless(rdata.float_latitude == 30.5) - self.failUnless(rdata.float_longitude == -100.5) - - def test_SOA_BIND8_TTL(self): - rdata1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, - "a b 100 1s 1m 1h 1d") - rdata2 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, - "a b 100 1 60 3600 86400") - self.failUnless(rdata1 == rdata2) - - def test_TTL_bounds_check(self): - def bad(): - ttl = dns.ttl.from_text("2147483648") - self.failUnlessRaises(dns.ttl.BadTTL, bad) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/dnssec.py b/lib/dnspython/tests/dnssec.py deleted file mode 100644 index 7b4546a086..0000000000 --- a/lib/dnspython/tests/dnssec.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (C) 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.dnssec -import dns.name -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rrset - -abs_dnspython_org = dns.name.from_text('dnspython.org') - -abs_keys = { abs_dnspython_org : - dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'DNSKEY', - '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', - '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') - } - -rel_keys = { dns.name.empty : - dns.rrset.from_text('@', 3600, 'IN', 'DNSKEY', - '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', - '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') - } - -when = 1290250287 - -abs_soa = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'SOA', - 'howl.dnspython.org. hostmaster.dnspython.org. 2010020047 3600 1800 604800 3600') - -abs_other_soa = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'SOA', - 'foo.dnspython.org. hostmaster.dnspython.org. 2010020047 3600 1800 604800 3600') - -abs_soa_rrsig = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'RRSIG', - 'SOA 5 2 3600 20101127004331 20101119213831 61695 dnspython.org. sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=') - -rel_soa = dns.rrset.from_text('@', 3600, 'IN', 'SOA', - 'howl hostmaster 2010020047 3600 1800 604800 3600') - -rel_other_soa = dns.rrset.from_text('@', 3600, 'IN', 'SOA', - 'foo hostmaster 2010020047 3600 1800 604800 3600') - -rel_soa_rrsig = dns.rrset.from_text('@', 3600, 'IN', 'RRSIG', - 'SOA 5 2 3600 20101127004331 20101119213831 61695 @ sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=') - -sep_key = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, - '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=') - -good_ds = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, - '57349 5 2 53A79A3E7488AB44FFC56B2D1109F0699D1796DD977E72108B841F96 E47D7013') - -when2 = 1290425644 - -abs_example = dns.name.from_text('example') - -abs_dsa_keys = { abs_example : - dns.rrset.from_text('example.', 86400, 'IN', 'DNSKEY', - '257 3 3 CI3nCqyJsiCJHTjrNsJOT4RaszetzcJPYuoH3F9ZTVt3KJXncCVR3bwn 1w0iavKljb9hDlAYSfHbFCp4ic/rvg4p1L8vh5s8ToMjqDNl40A0hUGQ Ybx5hsECyK+qHoajilUX1phYSAD8d9WAGO3fDWzUPBuzR7o85NiZCDxz yXuNVfni0uhj9n1KYhEO5yAbbruDGN89wIZcxMKuQsdUY2GYD93ssnBv a55W6XRABYWayKZ90WkRVODLVYLSn53Pj/wwxGH+XdhIAZJXimrZL4yl My7rtBsLMqq8Ihs4Tows7LqYwY7cp6y/50tw6pj8tFqMYcPUjKZV36l1 M/2t5BVg3i7IK61Aidt6aoC3TDJtzAxg3ZxfjZWJfhHjMJqzQIfbW5b9 q1mjFsW5EUv39RaNnX+3JWPRLyDqD4pIwDyqfutMsdk/Py3paHn82FGp CaOg+nicqZ9TiMZURN/XXy5JoXUNQ3RNvbHCUiPUe18KUkY6mTfnyHld 1l9YCWmzXQVClkx/hOYxjJ4j8Ife58+Obu5X', - '256 3 3 CJE1yb9YRQiw5d2xZrMUMR+cGCTt1bp1KDCefmYKmS+Z1+q9f42ETVhx JRiQwXclYwmxborzIkSZegTNYIV6mrYwbNB27Q44c3UGcspb3PiOw5TC jNPRYEcdwGvDZ2wWy+vkSV/S9tHXY8O6ODiE6abZJDDg/RnITyi+eoDL R3KZ5n/V1f1T1b90rrV6EewhBGQJpQGDogaXb2oHww9Tm6NfXyo7SoMM pbwbzOckXv+GxRPJIQNSF4D4A9E8XCksuzVVdE/0lr37+uoiAiPia38U 5W2QWe/FJAEPLjIp2eTzf0TrADc1pKP1wrA2ASpdzpm/aX3IB5RPp8Ew S9U72eBFZJAUwg635HxJVxH1maG6atzorR566E+e0OZSaxXS9o1o6QqN 3oPlYLGPORDiExilKfez3C/x/yioOupW9K5eKF0gmtaqrHX0oq9s67f/ RIM2xVaKHgG9Vf2cgJIZkhv7sntujr+E4htnRmy9P9BxyFxsItYxPI6Z bzygHAZpGhlI/7ltEGlIwKxyTK3ZKBm67q7B') - } - -abs_dsa_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', - 'ns1.example. hostmaster.example. 2 10800 3600 604800 86400') - -abs_other_dsa_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', - 'ns1.example. hostmaster.example. 2 10800 3600 604800 86401') - -abs_dsa_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', - 'SOA 3 1 86400 20101129143231 20101122112731 42088 example. CGul9SuBofsktunV8cJs4eRs6u+3NCS3yaPKvBbD+pB2C76OUXDZq9U=') - -example_sep_key = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, - '257 3 3 CI3nCqyJsiCJHTjrNsJOT4RaszetzcJPYuoH3F9ZTVt3KJXncCVR3bwn 1w0iavKljb9hDlAYSfHbFCp4ic/rvg4p1L8vh5s8ToMjqDNl40A0hUGQ Ybx5hsECyK+qHoajilUX1phYSAD8d9WAGO3fDWzUPBuzR7o85NiZCDxz yXuNVfni0uhj9n1KYhEO5yAbbruDGN89wIZcxMKuQsdUY2GYD93ssnBv a55W6XRABYWayKZ90WkRVODLVYLSn53Pj/wwxGH+XdhIAZJXimrZL4yl My7rtBsLMqq8Ihs4Tows7LqYwY7cp6y/50tw6pj8tFqMYcPUjKZV36l1 M/2t5BVg3i7IK61Aidt6aoC3TDJtzAxg3ZxfjZWJfhHjMJqzQIfbW5b9 q1mjFsW5EUv39RaNnX+3JWPRLyDqD4pIwDyqfutMsdk/Py3paHn82FGp CaOg+nicqZ9TiMZURN/XXy5JoXUNQ3RNvbHCUiPUe18KUkY6mTfnyHld 1l9YCWmzXQVClkx/hOYxjJ4j8Ife58+Obu5X') - -example_ds_sha1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, - '18673 3 1 71b71d4f3e11bbd71b4eff12cde69f7f9215bbe7') - -example_ds_sha256 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, - '18673 3 2 eb8344cbbf07c9d3d3d6c81d10c76653e28d8611a65e639ef8f716e4e4e5d913') - -class DNSSECValidatorTestCase(unittest.TestCase): - - def testAbsoluteRSAGood(self): - dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) - - def testAbsoluteRSABad(self): - def bad(): - dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, - when) - self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) - - def testRelativeRSAGood(self): - dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, - abs_dnspython_org, when) - - def testRelativeRSABad(self): - def bad(): - dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, - abs_dnspython_org, when) - self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) - - def testMakeSHA256DS(self): - ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256') - self.failUnless(ds == good_ds) - - def testAbsoluteDSAGood(self): - dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, - when2) - - def testAbsoluteDSABad(self): - def bad(): - dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, - abs_dsa_keys, None, when2) - self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) - - def testMakeExampleSHA1DS(self): - ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA1') - self.failUnless(ds == example_ds_sha1) - - def testMakeExampleSHA256DS(self): - ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA256') - self.failUnless(ds == example_ds_sha256) - -if __name__ == '__main__': - import_ok = False - try: - import Crypto.Util.number - import_ok = True - except: - pass - if import_ok: - unittest.main() - else: - print 'skipping DNSSEC tests because pycrypto is not installed' diff --git a/lib/dnspython/tests/example b/lib/dnspython/tests/example deleted file mode 100644 index 2f753a2f75..0000000000 --- a/lib/dnspython/tests/example +++ /dev/null @@ -1,226 +0,0 @@ -; Copyright (C) 2000, 2001 Internet Software Consortium. -; -; Permission to use, copy, modify, and distribute this software for any -; purpose with or without fee is hereby granted, provided that the above -; copyright notice and this permission notice appear in all copies. -; -; THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM -; DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL -; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL -; INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, -; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING -; FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -; WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -; $Id: example,v 1.13 2004/03/19 00:06:37 halley Exp $ - -$ORIGIN . -$TTL 300 ; 5 minutes -example IN SOA ns1.example. hostmaster.example. ( - 1 ; serial - 2000 ; refresh (2000 seconds) - 2000 ; retry (2000 seconds) - 1814400 ; expire (3 weeks) - 3600 ; minimum (1 hour) - ) -example. NS ns1.example. -ns1.example. A 10.53.0.1 -example. NS ns2.example. -ns2.example. A 10.53.0.2 - -$ORIGIN example. -* MX 10 mail -a TXT "foo foo foo" - PTR foo.net. -;; The next line not starting with ';;' is leading whitespace followed by -;; EOL. We want to treat that as if EOL had appeared alone. - -;; The next line not starting with ';;' is leading whitespace followed by -;; a comment followed by EOL. We want to treat that as if EOL had appeared -;; alone. - ; foo -$TTL 3600 ; 1 hour -a01 A 0.0.0.0 -a02 A 255.255.255.255 -;; -;; XXXRTH dnspython doesn't currently implement A6, and since -;; A6 records are effectively dead, it may never do so. -;; -;;a601 A6 0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff -;; A6 64 ::ffff:ffff:ffff:ffff foo. -;; A6 127 ::1 foo. -;; A6 128 . -aaaa01 AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff -aaaa02 AAAA ::1 -afsdb01 AFSDB 0 hostname -afsdb02 AFSDB 65535 . -$TTL 300 ; 5 minutes -b CNAME foo.net. -c A 73.80.65.49 -$TTL 3600 ; 1 hour -cert01 CERT 65534 65535 PRIVATEOID ( - MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi - WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl - d80jEeC8aTrO+KKmCaY= ) -cname01 CNAME cname-target. -cname02 CNAME cname-target -cname03 CNAME . -$TTL 300 ; 5 minutes -d A 73.80.65.49 -$TTL 3600 ; 1 hour -dhcid01 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69l - OjxfNuVAA2kjEA= ) -dhcid02 DHCID ( AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdW - L3b/NaiUDlW2No= ) -dhcid03 DHCID ( AAABxLmlskllE0MVjd57zHcWmEH3pCQ6V - ytcKD//7es/deY= ) -dname01 DNAME dname-target. -dname02 DNAME dname-target -dname03 DNAME . -$TTL 300 ; 5 minutes -e MX 10 mail - TXT "one" - TXT "three" - TXT "two" - A 73.80.65.49 - A 73.80.65.50 - A 73.80.65.52 - A 73.80.65.51 -f A 73.80.65.52 -$TTL 3600 ; 1 hour -gpos01 GPOS "-22.6882" "116.8652" "250.0" -;; -;; XXXRTH I have commented out the following line because I don't think -;; it is a valid GPOS record. -;; -;;gpos02 GPOS "" "" "" -hinfo01 HINFO "Generic PC clone" "NetBSD-1.4" -hinfo02 HINFO "PC" "NetBSD" -isdn01 ISDN "isdn-address" -isdn02 ISDN "isdn-address" "subaddress" -isdn03 ISDN "isdn-address" -isdn04 ISDN "isdn-address" "subaddress" -;; dnspython no longer supports old DNSSEC -;;key01 KEY 512 255 1 ( -;; AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR -;; yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 -;; GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o -;; jqf0BaqHT+8= ) -;;key02 KEY HOST|FLAG4 DNSSEC RSAMD5 ( -;; AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR -;; yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 -;; GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o -;; jqf0BaqHT+8= ) -kx01 KX 10 kdc -kx02 KX 10 . -loc01 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20m 2000m 20m -loc02 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20m 2000m 20m -loc03 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m -loc04 LOC 60 9 1.5 N 24 39 0.000 E 10.00m 20m 2000m 20m -loc05 LOC 60 9 1.51 N 24 39 0.000 E 10.00m 20m 2000m 20m -;; -;; XXXRTH These are all obsolete and unused. dnspython doesn't implement -;; them -;;mb01 MG madname -;;mb02 MG . -;;mg01 MG mgmname -;;mg02 MG . -;;minfo01 MINFO rmailbx emailbx -;;minfo02 MINFO . . -;;mr01 MR mrname -;;mr02 MR . -mx01 MX 10 mail -mx02 MX 10 . -naptr01 NAPTR 0 0 "" "" "" . -naptr02 NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. -nsap-ptr01 NSAP-PTR foo. - NSAP-PTR . -nsap01 NSAP 0x47000580005a0000000001e133ffffff00016100 -nsap02 NSAP 0x47.000580005a0000000001e133ffffff000161.00 -;;nxt01 NXT a.secure ( NS SOA MX SIG KEY LOC NXT ) -;;nxt02 NXT . ( NSAP-PTR NXT ) -;;nxt03 NXT . ( A ) -;;nxt04 NXT . ( 127 ) -ptr01 PTR example. -px01 PX 65535 foo. bar. -px02 PX 65535 . . -rp01 RP mbox-dname txt-dname -rp02 RP . . -rt01 RT 0 intermediate-host -rt02 RT 65535 . -$TTL 300 ; 5 minutes -s NS ns.s -$ORIGIN s.example. -ns A 73.80.65.49 -$ORIGIN example. -$TTL 3600 ; 1 hour -;;sig01 SIG NXT 1 3 3600 ( -;; 20200101000000 20030101000000 2143 foo -;; MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi -;; WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl -;; d80jEeC8aTrO+KKmCaY= ) -srv01 SRV 0 0 0 . -srv02 SRV 65535 65535 65535 old-slow-box.example.com. -$TTL 301 ; 5 minutes 1 second -t A 73.80.65.49 -$TTL 3600 ; 1 hour -txt01 TXT "foo" -txt02 TXT "foo" "bar" -txt03 TXT "foo" -txt04 TXT "foo" "bar" -txt05 TXT "foo bar" -txt06 TXT "foo bar" -txt07 TXT "foo bar" -txt08 TXT "foo\010bar" -txt09 TXT "foo\010bar" -txt10 TXT "foo bar" -txt11 TXT "\"foo\"" -txt12 TXT "\"foo\"" -txt13 TXT foo -$TTL 300 ; 5 minutes -u TXT "txt-not-in-nxt" -$ORIGIN u.example. -a A 73.80.65.49 -b A 73.80.65.49 -$ORIGIN example. -$TTL 3600 ; 1 hour -wks01 WKS 10.0.0.1 6 ( 0 1 2 21 23 ) -wks02 WKS 10.0.0.1 17 ( 0 1 2 53 ) -wks03 WKS 10.0.0.2 6 ( 65535 ) -x2501 X25 "123456789" -dlv01 DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 -ds01 DS 12345 3 1 123456789abcdef67890123456789abcdef67890 -apl01 APL 1:192.168.32.0/21 !1:192.168.38.0/28 -apl02 APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 -unknown2 TYPE999 \# 8 0a0000010a000001 -rrsig01 RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/ vILz45IkskceFGgiWCn/GxHhai6V AuHAoNUz4YoU1tVfSCSqQYn6//11 U6Nld80jEeC8aTrO+KKmCaY= -nsec01 NSEC a.secure. A MX RRSIG NSEC TYPE1234 -nsec02 NSEC . NSAP-PTR NSEC -nsec03 NSEC . NSEC TYPE65535 -dnskey01 DNSKEY 512 255 1 ( - AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR - yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 - GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o - jqf0BaqHT+8= ) -dnskey02 DNSKEY 257 3 RSAMD5 ( - AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR - yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 - GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o - jqf0BaqHT+8= ) -; -; test known type using unknown RR syntax -; -unknown3 A \# 4 7f000002 -sshfp1 SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab -spf SPF "v=spf1 mx -all" -ipseckey01 IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== -ipseckey02 IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== -ipseckey03 IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== -ipseckey04 IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== -ipseckey05 IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== -nsec301 NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG -nsec302 NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG -nsec3param01 NSEC3PARAM 1 1 12 aabbccdd -nsec3param02 NSEC3PARAM 1 1 12 - -hip01 HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2 diff --git a/lib/dnspython/tests/example1.good b/lib/dnspython/tests/example1.good deleted file mode 100644 index 0834d17150..0000000000 --- a/lib/dnspython/tests/example1.good +++ /dev/null @@ -1,114 +0,0 @@ -@ 300 IN SOA ns1 hostmaster 1 2000 2000 1814400 3600 -@ 300 IN NS ns1 -@ 300 IN NS ns2 -* 300 IN MX 10 mail -a 300 IN TXT "foo foo foo" -a 300 IN PTR foo.net. -a01 3600 IN A 0.0.0.0 -a02 3600 IN A 255.255.255.255 -aaaa01 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff -aaaa02 3600 IN AAAA ::1 -afsdb01 3600 IN AFSDB 0 hostname -afsdb02 3600 IN AFSDB 65535 . -apl01 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 -apl02 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 -b 300 IN CNAME foo.net. -c 300 IN A 73.80.65.49 -cert01 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= -cname01 3600 IN CNAME cname-target. -cname02 3600 IN CNAME cname-target -cname03 3600 IN CNAME . -d 300 IN A 73.80.65.49 -dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= -dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= -dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= -dlv01 3600 IN DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 -dname01 3600 IN DNAME dname-target. -dname02 3600 IN DNAME dname-target -dname03 3600 IN DNAME . -dnskey01 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= -dnskey02 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= -ds01 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 -e 300 IN MX 10 mail -e 300 IN TXT "one" -e 300 IN TXT "three" -e 300 IN TXT "two" -e 300 IN A 73.80.65.49 -e 300 IN A 73.80.65.50 -e 300 IN A 73.80.65.52 -e 300 IN A 73.80.65.51 -f 300 IN A 73.80.65.52 -gpos01 3600 IN GPOS -22.6882 116.8652 250.0 -hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" -hinfo02 3600 IN HINFO "PC" "NetBSD" -hip01 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2 -ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -isdn01 3600 IN ISDN "isdn-address" -isdn02 3600 IN ISDN "isdn-address" "subaddress" -isdn03 3600 IN ISDN "isdn-address" -isdn04 3600 IN ISDN "isdn-address" "subaddress" -kx01 3600 IN KX 10 kdc -kx02 3600 IN KX 10 . -loc01 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -loc02 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -loc03 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m -loc04 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -loc05 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -mx01 3600 IN MX 10 mail -mx02 3600 IN MX 10 . -naptr01 3600 IN NAPTR 0 0 "" "" "" . -naptr02 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. -ns1 300 IN A 10.53.0.1 -ns2 300 IN A 10.53.0.2 -nsap-ptr01 3600 IN NSAP-PTR foo. -nsap-ptr01 3600 IN NSAP-PTR . -nsap01 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 -nsap02 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 -nsec01 3600 IN NSEC a.secure. A MX RRSIG NSEC TYPE1234 -nsec02 3600 IN NSEC . NSAP-PTR NSEC -nsec03 3600 IN NSEC . NSEC TYPE65535 -nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM -nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM -nsec3param01 3600 IN NSEC3PARAM 1 1 12 aabbccdd -nsec3param02 3600 IN NSEC3PARAM 1 1 12 - -ptr01 3600 IN PTR @ -px01 3600 IN PX 65535 foo. bar. -px02 3600 IN PX 65535 . . -rp01 3600 IN RP mbox-dname txt-dname -rp02 3600 IN RP . . -rrsig01 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= -rt01 3600 IN RT 0 intermediate-host -rt02 3600 IN RT 65535 . -s 300 IN NS ns.s -ns.s 300 IN A 73.80.65.49 -spf 3600 IN SPF "v=spf1 mx -all" -srv01 3600 IN SRV 0 0 0 . -srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. -sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab -t 301 IN A 73.80.65.49 -txt01 3600 IN TXT "foo" -txt02 3600 IN TXT "foo" "bar" -txt03 3600 IN TXT "foo" -txt04 3600 IN TXT "foo" "bar" -txt05 3600 IN TXT "foo bar" -txt06 3600 IN TXT "foo bar" -txt07 3600 IN TXT "foo bar" -txt08 3600 IN TXT "foo\010bar" -txt09 3600 IN TXT "foo\010bar" -txt10 3600 IN TXT "foo bar" -txt11 3600 IN TXT "\"foo\"" -txt12 3600 IN TXT "\"foo\"" -txt13 3600 IN TXT "foo" -u 300 IN TXT "txt-not-in-nxt" -a.u 300 IN A 73.80.65.49 -b.u 300 IN A 73.80.65.49 -unknown2 3600 IN TYPE999 \# 8 0a0000010a000001 -unknown3 3600 IN A 127.0.0.2 -wks01 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 -wks02 3600 IN WKS 10.0.0.1 17 0 1 2 53 -wks03 3600 IN WKS 10.0.0.2 6 65535 -x2501 3600 IN X25 "123456789" diff --git a/lib/dnspython/tests/example2.good b/lib/dnspython/tests/example2.good deleted file mode 100644 index de4bcd59fd..0000000000 --- a/lib/dnspython/tests/example2.good +++ /dev/null @@ -1,114 +0,0 @@ -example. 300 IN SOA ns1.example. hostmaster.example. 1 2000 2000 1814400 3600 -example. 300 IN NS ns1.example. -example. 300 IN NS ns2.example. -*.example. 300 IN MX 10 mail.example. -a.example. 300 IN TXT "foo foo foo" -a.example. 300 IN PTR foo.net. -a01.example. 3600 IN A 0.0.0.0 -a02.example. 3600 IN A 255.255.255.255 -aaaa01.example. 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff -aaaa02.example. 3600 IN AAAA ::1 -afsdb01.example. 3600 IN AFSDB 0 hostname.example. -afsdb02.example. 3600 IN AFSDB 65535 . -apl01.example. 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 -apl02.example. 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 -b.example. 300 IN CNAME foo.net. -c.example. 300 IN A 73.80.65.49 -cert01.example. 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= -cname01.example. 3600 IN CNAME cname-target. -cname02.example. 3600 IN CNAME cname-target.example. -cname03.example. 3600 IN CNAME . -d.example. 300 IN A 73.80.65.49 -dhcid01.example. 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= -dhcid02.example. 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= -dhcid03.example. 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= -dlv01.example. 3600 IN DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 -dname01.example. 3600 IN DNAME dname-target. -dname02.example. 3600 IN DNAME dname-target.example. -dname03.example. 3600 IN DNAME . -dnskey01.example. 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= -dnskey02.example. 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= -ds01.example. 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 -e.example. 300 IN MX 10 mail.example. -e.example. 300 IN TXT "one" -e.example. 300 IN TXT "three" -e.example. 300 IN TXT "two" -e.example. 300 IN A 73.80.65.49 -e.example. 300 IN A 73.80.65.50 -e.example. 300 IN A 73.80.65.52 -e.example. 300 IN A 73.80.65.51 -f.example. 300 IN A 73.80.65.52 -gpos01.example. 3600 IN GPOS -22.6882 116.8652 250.0 -hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" -hinfo02.example. 3600 IN HINFO "PC" "NetBSD" -hip01.example. 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example. -ipseckey01.example. 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey02.example. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey03.example. 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey04.example. 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -ipseckey05.example. 3600 IN IPSECKEY 10 3 2 mygateway2.example. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== -isdn01.example. 3600 IN ISDN "isdn-address" -isdn02.example. 3600 IN ISDN "isdn-address" "subaddress" -isdn03.example. 3600 IN ISDN "isdn-address" -isdn04.example. 3600 IN ISDN "isdn-address" "subaddress" -kx01.example. 3600 IN KX 10 kdc.example. -kx02.example. 3600 IN KX 10 . -loc01.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -loc02.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -loc03.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m -loc04.example. 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -loc05.example. 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m -mx01.example. 3600 IN MX 10 mail.example. -mx02.example. 3600 IN MX 10 . -naptr01.example. 3600 IN NAPTR 0 0 "" "" "" . -naptr02.example. 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. -ns1.example. 300 IN A 10.53.0.1 -ns2.example. 300 IN A 10.53.0.2 -nsap-ptr01.example. 3600 IN NSAP-PTR foo. -nsap-ptr01.example. 3600 IN NSAP-PTR . -nsap01.example. 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 -nsap02.example. 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 -nsec01.example. 3600 IN NSEC a.secure. A MX RRSIG NSEC TYPE1234 -nsec02.example. 3600 IN NSEC . NSAP-PTR NSEC -nsec03.example. 3600 IN NSEC . NSEC TYPE65535 -nsec301.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM -nsec302.example. 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM -nsec3param01.example. 3600 IN NSEC3PARAM 1 1 12 aabbccdd -nsec3param02.example. 3600 IN NSEC3PARAM 1 1 12 - -ptr01.example. 3600 IN PTR example. -px01.example. 3600 IN PX 65535 foo. bar. -px02.example. 3600 IN PX 65535 . . -rp01.example. 3600 IN RP mbox-dname.example. txt-dname.example. -rp02.example. 3600 IN RP . . -rrsig01.example. 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo.example. MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= -rt01.example. 3600 IN RT 0 intermediate-host.example. -rt02.example. 3600 IN RT 65535 . -s.example. 300 IN NS ns.s.example. -ns.s.example. 300 IN A 73.80.65.49 -spf.example. 3600 IN SPF "v=spf1 mx -all" -srv01.example. 3600 IN SRV 0 0 0 . -srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. -sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab -t.example. 301 IN A 73.80.65.49 -txt01.example. 3600 IN TXT "foo" -txt02.example. 3600 IN TXT "foo" "bar" -txt03.example. 3600 IN TXT "foo" -txt04.example. 3600 IN TXT "foo" "bar" -txt05.example. 3600 IN TXT "foo bar" -txt06.example. 3600 IN TXT "foo bar" -txt07.example. 3600 IN TXT "foo bar" -txt08.example. 3600 IN TXT "foo\010bar" -txt09.example. 3600 IN TXT "foo\010bar" -txt10.example. 3600 IN TXT "foo bar" -txt11.example. 3600 IN TXT "\"foo\"" -txt12.example. 3600 IN TXT "\"foo\"" -txt13.example. 3600 IN TXT "foo" -u.example. 300 IN TXT "txt-not-in-nxt" -a.u.example. 300 IN A 73.80.65.49 -b.u.example. 300 IN A 73.80.65.49 -unknown2.example. 3600 IN TYPE999 \# 8 0a0000010a000001 -unknown3.example. 3600 IN A 127.0.0.2 -wks01.example. 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 -wks02.example. 3600 IN WKS 10.0.0.1 17 0 1 2 53 -wks03.example. 3600 IN WKS 10.0.0.2 6 65535 -x2501.example. 3600 IN X25 "123456789" diff --git a/lib/dnspython/tests/flags.py b/lib/dnspython/tests/flags.py deleted file mode 100644 index b3cf6716bc..0000000000 --- a/lib/dnspython/tests/flags.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.flags -import dns.rcode -import dns.opcode - -class FlagsTestCase(unittest.TestCase): - - def test_rcode1(self): - self.failUnless(dns.rcode.from_text('FORMERR') == dns.rcode.FORMERR) - - def test_rcode2(self): - self.failUnless(dns.rcode.to_text(dns.rcode.FORMERR) == "FORMERR") - - def test_rcode3(self): - self.failUnless(dns.rcode.to_flags(dns.rcode.FORMERR) == (1, 0)) - - def test_rcode4(self): - self.failUnless(dns.rcode.to_flags(dns.rcode.BADVERS) == \ - (0, 0x01000000)) - - def test_rcode6(self): - self.failUnless(dns.rcode.from_flags(0, 0x01000000) == \ - dns.rcode.BADVERS) - - def test_rcode6(self): - self.failUnless(dns.rcode.from_flags(5, 0) == dns.rcode.REFUSED) - - def test_rcode7(self): - def bad(): - dns.rcode.to_flags(4096) - self.failUnlessRaises(ValueError, bad) - - def test_flags1(self): - self.failUnless(dns.flags.from_text("RA RD AA QR") == \ - dns.flags.QR|dns.flags.AA|dns.flags.RD|dns.flags.RA) - - def test_flags2(self): - flags = dns.flags.QR|dns.flags.AA|dns.flags.RD|dns.flags.RA - self.failUnless(dns.flags.to_text(flags) == "QR AA RD RA") - - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/message.py b/lib/dnspython/tests/message.py deleted file mode 100644 index 931bb1977d..0000000000 --- a/lib/dnspython/tests/message.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import os -import unittest - -import dns.exception -import dns.message - -query_text = """id 1234 -opcode QUERY -rcode NOERROR -flags RD -edns 0 -eflags DO -payload 4096 -;QUESTION -wwww.dnspython.org. IN A -;ANSWER -;AUTHORITY -;ADDITIONAL""" - -goodhex = '04d201000001000000000001047777777709646e73707974686f6e' \ - '036f726700000100010000291000000080000000' - -goodwire = goodhex.decode('hex_codec') - -answer_text = """id 1234 -opcode QUERY -rcode NOERROR -flags QR AA RD -;QUESTION -dnspython.org. IN SOA -;ANSWER -dnspython.org. 3600 IN SOA woof.dnspython.org. hostmaster.dnspython.org. 2003052700 3600 1800 604800 3600 -;AUTHORITY -dnspython.org. 3600 IN NS ns1.staff.nominum.org. -dnspython.org. 3600 IN NS ns2.staff.nominum.org. -dnspython.org. 3600 IN NS woof.play-bow.org. -;ADDITIONAL -woof.play-bow.org. 3600 IN A 204.152.186.150 -""" - -goodhex2 = '04d2 8500 0001 0001 0003 0001' \ - '09646e73707974686f6e036f726700 0006 0001' \ - 'c00c 0006 0001 00000e10 0028 ' \ - '04776f6f66c00c 0a686f73746d6173746572c00c' \ - '7764289c 00000e10 00000708 00093a80 00000e10' \ - 'c00c 0002 0001 00000e10 0014' \ - '036e7331057374616666076e6f6d696e756dc016' \ - 'c00c 0002 0001 00000e10 0006 036e7332c063' \ - 'c00c 0002 0001 00000e10 0010 04776f6f6608706c61792d626f77c016' \ - 'c091 0001 0001 00000e10 0004 cc98ba96' - - -goodwire2 = goodhex2.replace(' ', '').decode('hex_codec') - -query_text_2 = """id 1234 -opcode QUERY -rcode 4095 -flags RD -edns 0 -eflags DO -payload 4096 -;QUESTION -wwww.dnspython.org. IN A -;ANSWER -;AUTHORITY -;ADDITIONAL""" - -goodhex3 = '04d2010f0001000000000001047777777709646e73707974686f6e' \ - '036f726700000100010000291000ff0080000000' - -goodwire3 = goodhex3.decode('hex_codec') - -class MessageTestCase(unittest.TestCase): - - def test_comparison_eq1(self): - q1 = dns.message.from_text(query_text) - q2 = dns.message.from_text(query_text) - self.failUnless(q1 == q2) - - def test_comparison_ne1(self): - q1 = dns.message.from_text(query_text) - q2 = dns.message.from_text(query_text) - q2.id = 10 - self.failUnless(q1 != q2) - - def test_comparison_ne2(self): - q1 = dns.message.from_text(query_text) - q2 = dns.message.from_text(query_text) - q2.question = [] - self.failUnless(q1 != q2) - - def test_comparison_ne3(self): - q1 = dns.message.from_text(query_text) - self.failUnless(q1 != 1) - - def test_EDNS_to_wire1(self): - q = dns.message.from_text(query_text) - w = q.to_wire() - self.failUnless(w == goodwire) - - def test_EDNS_from_wire1(self): - m = dns.message.from_wire(goodwire) - self.failUnless(str(m) == query_text) - - def test_EDNS_to_wire2(self): - q = dns.message.from_text(query_text_2) - w = q.to_wire() - self.failUnless(w == goodwire3) - - def test_EDNS_from_wire2(self): - m = dns.message.from_wire(goodwire3) - self.failUnless(str(m) == query_text_2) - - def test_TooBig(self): - def bad(): - q = dns.message.from_text(query_text) - for i in xrange(0, 25): - rrset = dns.rrset.from_text('foo%d.' % i, 3600, - dns.rdataclass.IN, - dns.rdatatype.A, - '10.0.0.%d' % i) - q.additional.append(rrset) - w = q.to_wire(max_size=512) - self.failUnlessRaises(dns.exception.TooBig, bad) - - def test_answer1(self): - a = dns.message.from_text(answer_text) - wire = a.to_wire(want_shuffle=False) - self.failUnless(wire == goodwire2) - - def test_TrailingJunk(self): - def bad(): - badwire = goodwire + '\x00' - m = dns.message.from_wire(badwire) - self.failUnlessRaises(dns.message.TrailingJunk, bad) - - def test_ShortHeader(self): - def bad(): - badwire = '\x00' * 11 - m = dns.message.from_wire(badwire) - self.failUnlessRaises(dns.message.ShortHeader, bad) - - def test_RespondingToResponse(self): - def bad(): - q = dns.message.make_query('foo', 'A') - r1 = dns.message.make_response(q) - r2 = dns.message.make_response(r1) - self.failUnlessRaises(dns.exception.FormError, bad) - - def test_ExtendedRcodeSetting(self): - m = dns.message.make_query('foo', 'A') - m.set_rcode(4095) - self.failUnless(m.rcode() == 4095) - m.set_rcode(2) - self.failUnless(m.rcode() == 2) - - def test_EDNSVersionCoherence(self): - m = dns.message.make_query('foo', 'A') - m.use_edns(1) - self.failUnless((m.ednsflags >> 16) & 0xFF == 1) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/name.py b/lib/dnspython/tests/name.py deleted file mode 100644 index e30e43d3a2..0000000000 --- a/lib/dnspython/tests/name.py +++ /dev/null @@ -1,697 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import cStringIO -import socket - -import dns.name -import dns.reversename -import dns.e164 - -class NameTestCase(unittest.TestCase): - def setUp(self): - self.origin = dns.name.from_text('example.') - - def testFromTextRel1(self): - n = dns.name.from_text('foo.bar') - self.failUnless(n.labels == ('foo', 'bar', '')) - - def testFromTextRel2(self): - n = dns.name.from_text('foo.bar', origin=self.origin) - self.failUnless(n.labels == ('foo', 'bar', 'example', '')) - - def testFromTextRel3(self): - n = dns.name.from_text('foo.bar', origin=None) - self.failUnless(n.labels == ('foo', 'bar')) - - def testFromTextRel4(self): - n = dns.name.from_text('@', origin=None) - self.failUnless(n == dns.name.empty) - - def testFromTextRel5(self): - n = dns.name.from_text('@', origin=self.origin) - self.failUnless(n == self.origin) - - def testFromTextAbs1(self): - n = dns.name.from_text('foo.bar.') - self.failUnless(n.labels == ('foo', 'bar', '')) - - def testTortureFromText(self): - good = [ - r'.', - r'a', - r'a.', - r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - r'\000.\008.\010.\032.\046.\092.\099.\255', - r'\\', - r'\..\.', - r'\\.\\', - r'!"#%&/()=+-', - r'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255', - ] - bad = [ - r'..', - r'.a', - r'\\..', - '\\', # yes, we don't want the 'r' prefix! - r'\0', - r'\00', - r'\00Z', - r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - r'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255', - ] - for t in good: - try: - n = dns.name.from_text(t) - except: - self.fail("good test '%s' raised an exception" % t) - for t in bad: - caught = False - try: - n = dns.name.from_text(t) - except: - caught = True - if not caught: - self.fail("bad test '%s' did not raise an exception" % t) - - def testImmutable1(self): - def bad(): - self.origin.labels = () - self.failUnlessRaises(TypeError, bad) - - def testImmutable2(self): - def bad(): - self.origin.labels[0] = 'foo' - self.failUnlessRaises(TypeError, bad) - - def testAbs1(self): - self.failUnless(dns.name.root.is_absolute()) - - def testAbs2(self): - self.failUnless(not dns.name.empty.is_absolute()) - - def testAbs3(self): - self.failUnless(self.origin.is_absolute()) - - def testAbs3(self): - n = dns.name.from_text('foo', origin=None) - self.failUnless(not n.is_absolute()) - - def testWild1(self): - n = dns.name.from_text('*.foo', origin=None) - self.failUnless(n.is_wild()) - - def testWild2(self): - n = dns.name.from_text('*a.foo', origin=None) - self.failUnless(not n.is_wild()) - - def testWild3(self): - n = dns.name.from_text('a.*.foo', origin=None) - self.failUnless(not n.is_wild()) - - def testWild4(self): - self.failUnless(not dns.name.root.is_wild()) - - def testWild5(self): - self.failUnless(not dns.name.empty.is_wild()) - - def testHash1(self): - n1 = dns.name.from_text('fOo.COM') - n2 = dns.name.from_text('foo.com') - self.failUnless(hash(n1) == hash(n2)) - - def testCompare1(self): - n1 = dns.name.from_text('a') - n2 = dns.name.from_text('b') - self.failUnless(n1 < n2) - self.failUnless(n2 > n1) - - def testCompare2(self): - n1 = dns.name.from_text('') - n2 = dns.name.from_text('b') - self.failUnless(n1 < n2) - self.failUnless(n2 > n1) - - def testCompare3(self): - self.failUnless(dns.name.empty < dns.name.root) - self.failUnless(dns.name.root > dns.name.empty) - - def testCompare4(self): - self.failUnless(dns.name.root != 1) - - def testCompare5(self): - self.failUnless(dns.name.root < 1 or dns.name.root > 1) - - def testSubdomain1(self): - self.failUnless(not dns.name.empty.is_subdomain(dns.name.root)) - - def testSubdomain2(self): - self.failUnless(not dns.name.root.is_subdomain(dns.name.empty)) - - def testSubdomain3(self): - n = dns.name.from_text('foo', origin=self.origin) - self.failUnless(n.is_subdomain(self.origin)) - - def testSubdomain4(self): - n = dns.name.from_text('foo', origin=self.origin) - self.failUnless(n.is_subdomain(dns.name.root)) - - def testSubdomain5(self): - n = dns.name.from_text('foo', origin=self.origin) - self.failUnless(n.is_subdomain(n)) - - def testSuperdomain1(self): - self.failUnless(not dns.name.empty.is_superdomain(dns.name.root)) - - def testSuperdomain2(self): - self.failUnless(not dns.name.root.is_superdomain(dns.name.empty)) - - def testSuperdomain3(self): - n = dns.name.from_text('foo', origin=self.origin) - self.failUnless(self.origin.is_superdomain(n)) - - def testSuperdomain4(self): - n = dns.name.from_text('foo', origin=self.origin) - self.failUnless(dns.name.root.is_superdomain(n)) - - def testSuperdomain5(self): - n = dns.name.from_text('foo', origin=self.origin) - self.failUnless(n.is_superdomain(n)) - - def testCanonicalize1(self): - n = dns.name.from_text('FOO.bar', origin=self.origin) - c = n.canonicalize() - self.failUnless(c.labels == ('foo', 'bar', 'example', '')) - - def testToText1(self): - n = dns.name.from_text('FOO.bar', origin=self.origin) - t = n.to_text() - self.failUnless(t == 'FOO.bar.example.') - - def testToText2(self): - n = dns.name.from_text('FOO.bar', origin=self.origin) - t = n.to_text(True) - self.failUnless(t == 'FOO.bar.example') - - def testToText3(self): - n = dns.name.from_text('FOO.bar', origin=None) - t = n.to_text() - self.failUnless(t == 'FOO.bar') - - def testToText4(self): - t = dns.name.empty.to_text() - self.failUnless(t == '@') - - def testToText5(self): - t = dns.name.root.to_text() - self.failUnless(t == '.') - - def testToText6(self): - n = dns.name.from_text('FOO bar', origin=None) - t = n.to_text() - self.failUnless(t == r'FOO\032bar') - - def testToText7(self): - n = dns.name.from_text(r'FOO\.bar', origin=None) - t = n.to_text() - self.failUnless(t == r'FOO\.bar') - - def testToText8(self): - n = dns.name.from_text(r'\070OO\.bar', origin=None) - t = n.to_text() - self.failUnless(t == r'FOO\.bar') - - def testSlice1(self): - n = dns.name.from_text(r'a.b.c.', origin=None) - s = n[:] - self.failUnless(s == ('a', 'b', 'c', '')) - - def testSlice2(self): - n = dns.name.from_text(r'a.b.c.', origin=None) - s = n[:2] - self.failUnless(s == ('a', 'b')) - - def testSlice3(self): - n = dns.name.from_text(r'a.b.c.', origin=None) - s = n[2:] - self.failUnless(s == ('c', '')) - - def testEmptyLabel1(self): - def bad(): - n = dns.name.Name(['a', '', 'b']) - self.failUnlessRaises(dns.name.EmptyLabel, bad) - - def testEmptyLabel2(self): - def bad(): - n = dns.name.Name(['', 'b']) - self.failUnlessRaises(dns.name.EmptyLabel, bad) - - def testEmptyLabel3(self): - n = dns.name.Name(['b', '']) - self.failUnless(n) - - def testLongLabel(self): - n = dns.name.Name(['a' * 63]) - self.failUnless(n) - - def testLabelTooLong(self): - def bad(): - n = dns.name.Name(['a' * 64, 'b']) - self.failUnlessRaises(dns.name.LabelTooLong, bad) - - def testLongName(self): - n = dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 62]) - self.failUnless(n) - - def testNameTooLong(self): - def bad(): - n = dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 63]) - self.failUnlessRaises(dns.name.NameTooLong, bad) - - def testConcat1(self): - n1 = dns.name.Name(['a', 'b']) - n2 = dns.name.Name(['c', 'd']) - e = dns.name.Name(['a', 'b', 'c', 'd']) - r = n1 + n2 - self.failUnless(r == e) - - def testConcat2(self): - n1 = dns.name.Name(['a', 'b']) - n2 = dns.name.Name([]) - e = dns.name.Name(['a', 'b']) - r = n1 + n2 - self.failUnless(r == e) - - def testConcat2(self): - n1 = dns.name.Name([]) - n2 = dns.name.Name(['a', 'b']) - e = dns.name.Name(['a', 'b']) - r = n1 + n2 - self.failUnless(r == e) - - def testConcat3(self): - n1 = dns.name.Name(['a', 'b', '']) - n2 = dns.name.Name([]) - e = dns.name.Name(['a', 'b', '']) - r = n1 + n2 - self.failUnless(r == e) - - def testConcat4(self): - n1 = dns.name.Name(['a', 'b']) - n2 = dns.name.Name(['c', '']) - e = dns.name.Name(['a', 'b', 'c', '']) - r = n1 + n2 - self.failUnless(r == e) - - def testConcat5(self): - def bad(): - n1 = dns.name.Name(['a', 'b', '']) - n2 = dns.name.Name(['c']) - r = n1 + n2 - self.failUnlessRaises(dns.name.AbsoluteConcatenation, bad) - - def testBadEscape(self): - def bad(): - n = dns.name.from_text(r'a.b\0q1.c.') - print n - self.failUnlessRaises(dns.name.BadEscape, bad) - - def testDigestable1(self): - n = dns.name.from_text('FOO.bar') - d = n.to_digestable() - self.failUnless(d == '\x03foo\x03bar\x00') - - def testDigestable2(self): - n1 = dns.name.from_text('FOO.bar') - n2 = dns.name.from_text('foo.BAR.') - d1 = n1.to_digestable() - d2 = n2.to_digestable() - self.failUnless(d1 == d2) - - def testDigestable3(self): - d = dns.name.root.to_digestable() - self.failUnless(d == '\x00') - - def testDigestable4(self): - n = dns.name.from_text('FOO.bar', None) - d = n.to_digestable(dns.name.root) - self.failUnless(d == '\x03foo\x03bar\x00') - - def testBadDigestable(self): - def bad(): - n = dns.name.from_text('FOO.bar', None) - d = n.to_digestable() - self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) - - def testToWire1(self): - n = dns.name.from_text('FOO.bar') - f = cStringIO.StringIO() - compress = {} - n.to_wire(f, compress) - self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00') - - def testToWire2(self): - n = dns.name.from_text('FOO.bar') - f = cStringIO.StringIO() - compress = {} - n.to_wire(f, compress) - n.to_wire(f, compress) - self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00\xc0\x00') - - def testToWire3(self): - n1 = dns.name.from_text('FOO.bar') - n2 = dns.name.from_text('foo.bar') - f = cStringIO.StringIO() - compress = {} - n1.to_wire(f, compress) - n2.to_wire(f, compress) - self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00\xc0\x00') - - def testToWire4(self): - n1 = dns.name.from_text('FOO.bar') - n2 = dns.name.from_text('a.foo.bar') - f = cStringIO.StringIO() - compress = {} - n1.to_wire(f, compress) - n2.to_wire(f, compress) - self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00\x01\x61\xc0\x00') - - def testToWire5(self): - n1 = dns.name.from_text('FOO.bar') - n2 = dns.name.from_text('a.foo.bar') - f = cStringIO.StringIO() - compress = {} - n1.to_wire(f, compress) - n2.to_wire(f, None) - self.failUnless(f.getvalue() == \ - '\x03FOO\x03bar\x00\x01\x61\x03foo\x03bar\x00') - - def testToWire6(self): - n = dns.name.from_text('FOO.bar') - v = n.to_wire() - self.failUnless(v == '\x03FOO\x03bar\x00') - - def testBadToWire(self): - def bad(): - n = dns.name.from_text('FOO.bar', None) - f = cStringIO.StringIO() - compress = {} - n.to_wire(f, compress) - self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) - - def testSplit1(self): - n = dns.name.from_text('foo.bar.') - (prefix, suffix) = n.split(2) - ep = dns.name.from_text('foo', None) - es = dns.name.from_text('bar.', None) - self.failUnless(prefix == ep and suffix == es) - - def testSplit2(self): - n = dns.name.from_text('foo.bar.') - (prefix, suffix) = n.split(1) - ep = dns.name.from_text('foo.bar', None) - es = dns.name.from_text('.', None) - self.failUnless(prefix == ep and suffix == es) - - def testSplit3(self): - n = dns.name.from_text('foo.bar.') - (prefix, suffix) = n.split(0) - ep = dns.name.from_text('foo.bar.', None) - es = dns.name.from_text('', None) - self.failUnless(prefix == ep and suffix == es) - - def testSplit4(self): - n = dns.name.from_text('foo.bar.') - (prefix, suffix) = n.split(3) - ep = dns.name.from_text('', None) - es = dns.name.from_text('foo.bar.', None) - self.failUnless(prefix == ep and suffix == es) - - def testBadSplit1(self): - def bad(): - n = dns.name.from_text('foo.bar.') - (prefix, suffix) = n.split(-1) - self.failUnlessRaises(ValueError, bad) - - def testBadSplit2(self): - def bad(): - n = dns.name.from_text('foo.bar.') - (prefix, suffix) = n.split(4) - self.failUnlessRaises(ValueError, bad) - - def testRelativize1(self): - n = dns.name.from_text('a.foo.bar.', None) - o = dns.name.from_text('bar.', None) - e = dns.name.from_text('a.foo', None) - self.failUnless(n.relativize(o) == e) - - def testRelativize2(self): - n = dns.name.from_text('a.foo.bar.', None) - o = n - e = dns.name.empty - self.failUnless(n.relativize(o) == e) - - def testRelativize3(self): - n = dns.name.from_text('a.foo.bar.', None) - o = dns.name.from_text('blaz.', None) - e = n - self.failUnless(n.relativize(o) == e) - - def testRelativize4(self): - n = dns.name.from_text('a.foo', None) - o = dns.name.root - e = n - self.failUnless(n.relativize(o) == e) - - def testDerelativize1(self): - n = dns.name.from_text('a.foo', None) - o = dns.name.from_text('bar.', None) - e = dns.name.from_text('a.foo.bar.', None) - self.failUnless(n.derelativize(o) == e) - - def testDerelativize2(self): - n = dns.name.empty - o = dns.name.from_text('a.foo.bar.', None) - e = o - self.failUnless(n.derelativize(o) == e) - - def testDerelativize3(self): - n = dns.name.from_text('a.foo.bar.', None) - o = dns.name.from_text('blaz.', None) - e = n - self.failUnless(n.derelativize(o) == e) - - def testChooseRelativity1(self): - n = dns.name.from_text('a.foo.bar.', None) - o = dns.name.from_text('bar.', None) - e = dns.name.from_text('a.foo', None) - self.failUnless(n.choose_relativity(o, True) == e) - - def testChooseRelativity2(self): - n = dns.name.from_text('a.foo.bar.', None) - o = dns.name.from_text('bar.', None) - e = n - self.failUnless(n.choose_relativity(o, False) == e) - - def testChooseRelativity3(self): - n = dns.name.from_text('a.foo', None) - o = dns.name.from_text('bar.', None) - e = dns.name.from_text('a.foo.bar.', None) - self.failUnless(n.choose_relativity(o, False) == e) - - def testChooseRelativity4(self): - n = dns.name.from_text('a.foo', None) - o = None - e = n - self.failUnless(n.choose_relativity(o, True) == e) - - def testChooseRelativity5(self): - n = dns.name.from_text('a.foo', None) - o = None - e = n - self.failUnless(n.choose_relativity(o, False) == e) - - def testChooseRelativity6(self): - n = dns.name.from_text('a.foo.', None) - o = None - e = n - self.failUnless(n.choose_relativity(o, True) == e) - - def testChooseRelativity7(self): - n = dns.name.from_text('a.foo.', None) - o = None - e = n - self.failUnless(n.choose_relativity(o, False) == e) - - def testFromWire1(self): - w = '\x03foo\x00\xc0\x00' - (n1, cused1) = dns.name.from_wire(w, 0) - (n2, cused2) = dns.name.from_wire(w, cused1) - en1 = dns.name.from_text('foo.') - en2 = en1 - ecused1 = 5 - ecused2 = 2 - self.failUnless(n1 == en1 and cused1 == ecused1 and \ - n2 == en2 and cused2 == ecused2) - - def testFromWire1(self): - w = '\x03foo\x00\x01a\xc0\x00\x01b\xc0\x05' - current = 0 - (n1, cused1) = dns.name.from_wire(w, current) - current += cused1 - (n2, cused2) = dns.name.from_wire(w, current) - current += cused2 - (n3, cused3) = dns.name.from_wire(w, current) - en1 = dns.name.from_text('foo.') - en2 = dns.name.from_text('a.foo.') - en3 = dns.name.from_text('b.a.foo.') - ecused1 = 5 - ecused2 = 4 - ecused3 = 4 - self.failUnless(n1 == en1 and cused1 == ecused1 and \ - n2 == en2 and cused2 == ecused2 and \ - n3 == en3 and cused3 == ecused3) - - def testBadFromWire1(self): - def bad(): - w = '\x03foo\xc0\x04' - (n, cused) = dns.name.from_wire(w, 0) - self.failUnlessRaises(dns.name.BadPointer, bad) - - def testBadFromWire2(self): - def bad(): - w = '\x03foo\xc0\x05' - (n, cused) = dns.name.from_wire(w, 0) - self.failUnlessRaises(dns.name.BadPointer, bad) - - def testBadFromWire3(self): - def bad(): - w = '\xbffoo' - (n, cused) = dns.name.from_wire(w, 0) - self.failUnlessRaises(dns.name.BadLabelType, bad) - - def testBadFromWire4(self): - def bad(): - w = '\x41foo' - (n, cused) = dns.name.from_wire(w, 0) - self.failUnlessRaises(dns.name.BadLabelType, bad) - - def testParent1(self): - n = dns.name.from_text('foo.bar.') - self.failUnless(n.parent() == dns.name.from_text('bar.')) - self.failUnless(n.parent().parent() == dns.name.root) - - def testParent2(self): - n = dns.name.from_text('foo.bar', None) - self.failUnless(n.parent() == dns.name.from_text('bar', None)) - self.failUnless(n.parent().parent() == dns.name.empty) - - def testParent3(self): - def bad(): - n = dns.name.root - n.parent() - self.failUnlessRaises(dns.name.NoParent, bad) - - def testParent4(self): - def bad(): - n = dns.name.empty - n.parent() - self.failUnlessRaises(dns.name.NoParent, bad) - - def testFromUnicode1(self): - n = dns.name.from_text(u'foo.bar') - self.failUnless(n.labels == ('foo', 'bar', '')) - - def testFromUnicode2(self): - n = dns.name.from_text(u'foo\u1234bar.bar') - self.failUnless(n.labels == ('xn--foobar-r5z', 'bar', '')) - - def testFromUnicodeAlternateDot1(self): - n = dns.name.from_text(u'foo\u3002bar') - self.failUnless(n.labels == ('foo', 'bar', '')) - - def testFromUnicodeAlternateDot2(self): - n = dns.name.from_text(u'foo\uff0ebar') - self.failUnless(n.labels == ('foo', 'bar', '')) - - def testFromUnicodeAlternateDot3(self): - n = dns.name.from_text(u'foo\uff61bar') - self.failUnless(n.labels == ('foo', 'bar', '')) - - def testToUnicode1(self): - n = dns.name.from_text(u'foo.bar') - s = n.to_unicode() - self.failUnless(s == u'foo.bar.') - - def testToUnicode2(self): - n = dns.name.from_text(u'foo\u1234bar.bar') - s = n.to_unicode() - self.failUnless(s == u'foo\u1234bar.bar.') - - def testToUnicode3(self): - n = dns.name.from_text('foo.bar') - s = n.to_unicode() - self.failUnless(s == u'foo.bar.') - - def testReverseIPv4(self): - e = dns.name.from_text('1.0.0.127.in-addr.arpa.') - n = dns.reversename.from_address('127.0.0.1') - self.failUnless(e == n) - - def testReverseIPv6(self): - e = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') - n = dns.reversename.from_address('::1') - self.failUnless(e == n) - - def testBadReverseIPv4(self): - def bad(): - n = dns.reversename.from_address('127.0.foo.1') - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def testBadReverseIPv6(self): - def bad(): - n = dns.reversename.from_address('::1::1') - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def testForwardIPv4(self): - n = dns.name.from_text('1.0.0.127.in-addr.arpa.') - e = '127.0.0.1' - text = dns.reversename.to_address(n) - self.failUnless(text == e) - - def testForwardIPv6(self): - n = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') - e = '::1' - text = dns.reversename.to_address(n) - self.failUnless(text == e) - - def testE164ToEnum(self): - text = '+1 650 555 1212' - e = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') - n = dns.e164.from_e164(text) - self.failUnless(n == e) - - def testEnumToE164(self): - n = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') - e = '+16505551212' - text = dns.e164.to_e164(n) - self.failUnless(text == e) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/namedict.py b/lib/dnspython/tests/namedict.py deleted file mode 100644 index e256bfe679..0000000000 --- a/lib/dnspython/tests/namedict.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.name -import dns.namedict - -class NameTestCase(unittest.TestCase): - - def setUp(self): - self.ndict = dns.namedict.NameDict() - n1 = dns.name.from_text('foo.bar.') - n2 = dns.name.from_text('bar.') - self.ndict[n1] = 1 - self.ndict[n2] = 2 - self.rndict = dns.namedict.NameDict() - n1 = dns.name.from_text('foo.bar', None) - n2 = dns.name.from_text('bar', None) - self.rndict[n1] = 1 - self.rndict[n2] = 2 - - def testDepth(self): - self.failUnless(self.ndict.max_depth == 3) - - def testLookup1(self): - k = dns.name.from_text('foo.bar.') - self.failUnless(self.ndict[k] == 1) - - def testLookup2(self): - k = dns.name.from_text('foo.bar.') - self.failUnless(self.ndict.get_deepest_match(k)[1] == 1) - - def testLookup3(self): - k = dns.name.from_text('a.b.c.foo.bar.') - self.failUnless(self.ndict.get_deepest_match(k)[1] == 1) - - def testLookup4(self): - k = dns.name.from_text('a.b.c.bar.') - self.failUnless(self.ndict.get_deepest_match(k)[1] == 2) - - def testLookup5(self): - def bad(): - n = dns.name.from_text('a.b.c.') - (k, v) = self.ndict.get_deepest_match(n) - self.failUnlessRaises(KeyError, bad) - - def testLookup6(self): - def bad(): - (k, v) = self.ndict.get_deepest_match(dns.name.empty) - self.failUnlessRaises(KeyError, bad) - - def testLookup7(self): - self.ndict[dns.name.empty] = 100 - n = dns.name.from_text('a.b.c.') - (k, v) = self.ndict.get_deepest_match(n) - self.failUnless(v == 100) - - def testLookup8(self): - def bad(): - self.ndict['foo'] = 100 - self.failUnlessRaises(ValueError, bad) - - def testRelDepth(self): - self.failUnless(self.rndict.max_depth == 2) - - def testRelLookup1(self): - k = dns.name.from_text('foo.bar', None) - self.failUnless(self.rndict[k] == 1) - - def testRelLookup2(self): - k = dns.name.from_text('foo.bar', None) - self.failUnless(self.rndict.get_deepest_match(k)[1] == 1) - - def testRelLookup3(self): - k = dns.name.from_text('a.b.c.foo.bar', None) - self.failUnless(self.rndict.get_deepest_match(k)[1] == 1) - - def testRelLookup4(self): - k = dns.name.from_text('a.b.c.bar', None) - self.failUnless(self.rndict.get_deepest_match(k)[1] == 2) - - def testRelLookup7(self): - self.rndict[dns.name.empty] = 100 - n = dns.name.from_text('a.b.c', None) - (k, v) = self.rndict.get_deepest_match(n) - self.failUnless(v == 100) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/ntoaaton.py b/lib/dnspython/tests/ntoaaton.py deleted file mode 100644 index 9d8bedd702..0000000000 --- a/lib/dnspython/tests/ntoaaton.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.exception -import dns.ipv4 -import dns.ipv6 - -# for convenience -aton4 = dns.ipv4.inet_aton -ntoa4 = dns.ipv4.inet_ntoa -aton6 = dns.ipv6.inet_aton -ntoa6 = dns.ipv6.inet_ntoa - -v4_bad_addrs = ['256.1.1.1', '1.1.1', '1.1.1.1.1', '01.1.1.1', - '+1.1.1.1', '1.1.1.1+', '1..2.3.4', '.1.2.3.4', - '1.2.3.4.'] - -class NtoAAtoNTestCase(unittest.TestCase): - - def test_aton1(self): - a = aton6('::') - self.failUnless(a == '\x00' * 16) - - def test_aton2(self): - a = aton6('::1') - self.failUnless(a == '\x00' * 15 + '\x01') - - def test_aton3(self): - a = aton6('::10.0.0.1') - self.failUnless(a == '\x00' * 12 + '\x0a\x00\x00\x01') - - def test_aton4(self): - a = aton6('abcd::dcba') - self.failUnless(a == '\xab\xcd' + '\x00' * 12 + '\xdc\xba') - - def test_aton5(self): - a = aton6('1:2:3:4:5:6:7:8') - self.failUnless(a == \ - '00010002000300040005000600070008'.decode('hex_codec')) - - def test_bad_aton1(self): - def bad(): - a = aton6('abcd:dcba') - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def test_bad_aton2(self): - def bad(): - a = aton6('abcd::dcba::1') - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def test_bad_aton3(self): - def bad(): - a = aton6('1:2:3:4:5:6:7:8:9') - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def test_aton1(self): - a = aton6('::') - self.failUnless(a == '\x00' * 16) - - def test_aton2(self): - a = aton6('::1') - self.failUnless(a == '\x00' * 15 + '\x01') - - def test_aton3(self): - a = aton6('::10.0.0.1') - self.failUnless(a == '\x00' * 12 + '\x0a\x00\x00\x01') - - def test_aton4(self): - a = aton6('abcd::dcba') - self.failUnless(a == '\xab\xcd' + '\x00' * 12 + '\xdc\xba') - - def test_ntoa1(self): - b = '00010002000300040005000600070008'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '1:2:3:4:5:6:7:8') - - def test_ntoa2(self): - b = '\x00' * 16 - t = ntoa6(b) - self.failUnless(t == '::') - - def test_ntoa3(self): - b = '\x00' * 15 + '\x01' - t = ntoa6(b) - self.failUnless(t == '::1') - - def test_ntoa4(self): - b = '\x80' + '\x00' * 15 - t = ntoa6(b) - self.failUnless(t == '8000::') - - def test_ntoa5(self): - b = '\x01\xcd' + '\x00' * 12 + '\x03\xef' - t = ntoa6(b) - self.failUnless(t == '1cd::3ef') - - def test_ntoa6(self): - b = 'ffff00000000ffff000000000000ffff'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == 'ffff:0:0:ffff::ffff') - - def test_ntoa7(self): - b = '00000000ffff000000000000ffffffff'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '0:0:ffff::ffff:ffff') - - def test_ntoa8(self): - b = 'ffff0000ffff00000000ffff00000000'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == 'ffff:0:ffff::ffff:0:0') - - def test_ntoa9(self): - b = '0000000000000000000000000a000001'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '::10.0.0.1') - - def test_ntoa10(self): - b = '0000000000000000000000010a000001'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '::1:a00:1') - - def test_ntoa11(self): - b = '00000000000000000000ffff0a000001'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '::ffff:10.0.0.1') - - def test_ntoa12(self): - b = '000000000000000000000000ffffffff'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '::255.255.255.255') - - def test_ntoa13(self): - b = '00000000000000000000ffffffffffff'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '::ffff:255.255.255.255') - - def test_ntoa14(self): - b = '0000000000000000000000000001ffff'.decode('hex_codec') - t = ntoa6(b) - self.failUnless(t == '::0.1.255.255') - - def test_bad_ntoa1(self): - def bad(): - a = ntoa6('') - self.failUnlessRaises(ValueError, bad) - - def test_bad_ntoa2(self): - def bad(): - a = ntoa6('\x00' * 17) - self.failUnlessRaises(ValueError, bad) - - def test_good_v4_aton(self): - pairs = [('1.2.3.4', '\x01\x02\x03\x04'), - ('255.255.255.255', '\xff\xff\xff\xff'), - ('0.0.0.0', '\x00\x00\x00\x00')] - for (t, b) in pairs: - b1 = aton4(t) - t1 = ntoa4(b1) - self.failUnless(b1 == b) - self.failUnless(t1 == t) - - def test_bad_v4_aton(self): - def make_bad(a): - def bad(): - return aton4(a) - return bad - for addr in v4_bad_addrs: - self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) - - def test_bad_v6_aton(self): - addrs = ['+::0', '0::0::', '::0::', '1:2:3:4:5:6:7:8:9', - ':::::::'] - embedded = ['::' + x for x in v4_bad_addrs] - addrs.extend(embedded) - def make_bad(a): - def bad(): - x = aton6(a) - return bad - for addr in addrs: - self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/rdtypeandclass.py b/lib/dnspython/tests/rdtypeandclass.py deleted file mode 100644 index f3c0628d36..0000000000 --- a/lib/dnspython/tests/rdtypeandclass.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.rdataclass -import dns.rdatatype - -class RdTypeAndClassTestCase(unittest.TestCase): - - # Classes - - def test_class_meta1(self): - self.failUnless(dns.rdataclass.is_metaclass(dns.rdataclass.ANY)) - - def test_class_meta2(self): - self.failUnless(not dns.rdataclass.is_metaclass(dns.rdataclass.IN)) - - def test_class_bytext1(self): - self.failUnless(dns.rdataclass.from_text('IN') == dns.rdataclass.IN) - - def test_class_bytext2(self): - self.failUnless(dns.rdataclass.from_text('CLASS1') == - dns.rdataclass.IN) - - def test_class_bytext_bounds1(self): - self.failUnless(dns.rdataclass.from_text('CLASS0') == 0) - self.failUnless(dns.rdataclass.from_text('CLASS65535') == 65535) - - def test_class_bytext_bounds2(self): - def bad(): - junk = dns.rdataclass.from_text('CLASS65536') - self.failUnlessRaises(ValueError, bad) - - def test_class_bytext_unknown(self): - def bad(): - junk = dns.rdataclass.from_text('XXX') - self.failUnlessRaises(dns.rdataclass.UnknownRdataclass, bad) - - def test_class_totext1(self): - self.failUnless(dns.rdataclass.to_text(dns.rdataclass.IN) == 'IN') - - def test_class_totext1(self): - self.failUnless(dns.rdataclass.to_text(999) == 'CLASS999') - - def test_class_totext_bounds1(self): - def bad(): - junk = dns.rdataclass.to_text(-1) - self.failUnlessRaises(ValueError, bad) - - def test_class_totext_bounds2(self): - def bad(): - junk = dns.rdataclass.to_text(65536) - self.failUnlessRaises(ValueError, bad) - - # Types - - def test_type_meta1(self): - self.failUnless(dns.rdatatype.is_metatype(dns.rdatatype.ANY)) - - def test_type_meta2(self): - self.failUnless(dns.rdatatype.is_metatype(dns.rdatatype.OPT)) - - def test_type_meta3(self): - self.failUnless(not dns.rdatatype.is_metatype(dns.rdatatype.A)) - - def test_type_singleton1(self): - self.failUnless(dns.rdatatype.is_singleton(dns.rdatatype.SOA)) - - def test_type_singleton2(self): - self.failUnless(not dns.rdatatype.is_singleton(dns.rdatatype.A)) - - def test_type_bytext1(self): - self.failUnless(dns.rdatatype.from_text('A') == dns.rdatatype.A) - - def test_type_bytext2(self): - self.failUnless(dns.rdatatype.from_text('TYPE1') == - dns.rdatatype.A) - - def test_type_bytext_bounds1(self): - self.failUnless(dns.rdatatype.from_text('TYPE0') == 0) - self.failUnless(dns.rdatatype.from_text('TYPE65535') == 65535) - - def test_type_bytext_bounds2(self): - def bad(): - junk = dns.rdatatype.from_text('TYPE65536') - self.failUnlessRaises(ValueError, bad) - - def test_type_bytext_unknown(self): - def bad(): - junk = dns.rdatatype.from_text('XXX') - self.failUnlessRaises(dns.rdatatype.UnknownRdatatype, bad) - - def test_type_totext1(self): - self.failUnless(dns.rdatatype.to_text(dns.rdatatype.A) == 'A') - - def test_type_totext1(self): - self.failUnless(dns.rdatatype.to_text(999) == 'TYPE999') - - def test_type_totext_bounds1(self): - def bad(): - junk = dns.rdatatype.to_text(-1) - self.failUnlessRaises(ValueError, bad) - - def test_type_totext_bounds2(self): - def bad(): - junk = dns.rdatatype.to_text(65536) - self.failUnlessRaises(ValueError, bad) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/resolver.py b/lib/dnspython/tests/resolver.py deleted file mode 100644 index 28d5a42cdf..0000000000 --- a/lib/dnspython/tests/resolver.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import select -import sys -import time -import unittest - -import dns.name -import dns.message -import dns.name -import dns.rdataclass -import dns.rdatatype -import dns.resolver - -resolv_conf = """ - /t/t -# comment 1 -; comment 2 -domain foo -nameserver 10.0.0.1 -nameserver 10.0.0.2 -""" - -message_text = """id 1234 -opcode QUERY -rcode NOERROR -flags QR AA RD -;QUESTION -example. IN A -;ANSWER -example. 1 IN A 10.0.0.1 -;AUTHORITY -;ADDITIONAL -""" - -class BaseResolverTests(object): - - if sys.platform != 'win32': - def testRead(self): - f = cStringIO.StringIO(resolv_conf) - r = dns.resolver.Resolver(f) - self.failUnless(r.nameservers == ['10.0.0.1', '10.0.0.2'] and - r.domain == dns.name.from_text('foo')) - - def testCacheExpiration(self): - message = dns.message.from_text(message_text) - name = dns.name.from_text('example.') - answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, - message) - cache = dns.resolver.Cache() - cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) - time.sleep(2) - self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) - is None) - - def testCacheCleaning(self): - message = dns.message.from_text(message_text) - name = dns.name.from_text('example.') - answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, - message) - cache = dns.resolver.Cache(cleaning_interval=1.0) - cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) - time.sleep(2) - self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) - is None) - - def testZoneForName1(self): - name = dns.name.from_text('www.dnspython.org.') - ezname = dns.name.from_text('dnspython.org.') - zname = dns.resolver.zone_for_name(name) - self.failUnless(zname == ezname) - - def testZoneForName2(self): - name = dns.name.from_text('a.b.www.dnspython.org.') - ezname = dns.name.from_text('dnspython.org.') - zname = dns.resolver.zone_for_name(name) - self.failUnless(zname == ezname) - - def testZoneForName3(self): - name = dns.name.from_text('dnspython.org.') - ezname = dns.name.from_text('dnspython.org.') - zname = dns.resolver.zone_for_name(name) - self.failUnless(zname == ezname) - - def testZoneForName4(self): - def bad(): - name = dns.name.from_text('dnspython.org', None) - zname = dns.resolver.zone_for_name(name) - self.failUnlessRaises(dns.resolver.NotAbsolute, bad) - -class PollingMonkeyPatchMixin(object): - def setUp(self): - self.__native_polling_backend = dns.query._polling_backend - dns.query._set_polling_backend(self.polling_backend()) - - unittest.TestCase.setUp(self) - - def tearDown(self): - dns.query._set_polling_backend(self.__native_polling_backend) - - unittest.TestCase.tearDown(self) - -class SelectResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase): - def polling_backend(self): - return dns.query._select_for - -if hasattr(select, 'poll'): - class PollResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase): - def polling_backend(self): - return dns.query._poll_for - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/rrset.py b/lib/dnspython/tests/rrset.py deleted file mode 100644 index be1324b0ec..0000000000 --- a/lib/dnspython/tests/rrset.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.rrset - -class RRsetTestCase(unittest.TestCase): - - def testEqual1(self): - r1 = dns.rrset.from_text('foo', 300, 'in', 'a', '10.0.0.1', '10.0.0.2') - r2 = dns.rrset.from_text('FOO', 300, 'in', 'a', '10.0.0.2', '10.0.0.1') - self.failUnless(r1 == r2) - - def testEqual2(self): - r1 = dns.rrset.from_text('foo', 300, 'in', 'a', '10.0.0.1', '10.0.0.2') - r2 = dns.rrset.from_text('FOO', 600, 'in', 'a', '10.0.0.2', '10.0.0.1') - self.failUnless(r1 == r2) - - def testNotEqual1(self): - r1 = dns.rrset.from_text('fooa', 30, 'in', 'a', '10.0.0.1', '10.0.0.2') - r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') - self.failUnless(r1 != r2) - - def testNotEqual2(self): - r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1', '10.0.0.3') - r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') - self.failUnless(r1 != r2) - - def testNotEqual3(self): - r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1', '10.0.0.2', - '10.0.0.3') - r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') - self.failUnless(r1 != r2) - - def testNotEqual4(self): - r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1') - r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') - self.failUnless(r1 != r2) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/set.py b/lib/dnspython/tests/set.py deleted file mode 100644 index 583d20cfa0..0000000000 --- a/lib/dnspython/tests/set.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.set - -# for convenience -S = dns.set.Set - -class SimpleSetTestCase(unittest.TestCase): - - def testLen1(self): - s1 = S() - self.failUnless(len(s1) == 0) - - def testLen2(self): - s1 = S([1, 2, 3]) - self.failUnless(len(s1) == 3) - - def testLen3(self): - s1 = S([1, 2, 3, 3, 3]) - self.failUnless(len(s1) == 3) - - def testUnion1(self): - s1 = S([1, 2, 3]) - s2 = S([1, 2, 3]) - e = S([1, 2, 3]) - self.failUnless(s1 | s2 == e) - - def testUnion2(self): - s1 = S([1, 2, 3]) - s2 = S([]) - e = S([1, 2, 3]) - self.failUnless(s1 | s2 == e) - - def testUnion3(self): - s1 = S([1, 2, 3]) - s2 = S([3, 4]) - e = S([1, 2, 3, 4]) - self.failUnless(s1 | s2 == e) - - def testIntersection1(self): - s1 = S([1, 2, 3]) - s2 = S([1, 2, 3]) - e = S([1, 2, 3]) - self.failUnless(s1 & s2 == e) - - def testIntersection2(self): - s1 = S([0, 1, 2, 3]) - s2 = S([1, 2, 3, 4]) - e = S([1, 2, 3]) - self.failUnless(s1 & s2 == e) - - def testIntersection3(self): - s1 = S([1, 2, 3]) - s2 = S([]) - e = S([]) - self.failUnless(s1 & s2 == e) - - def testIntersection4(self): - s1 = S([1, 2, 3]) - s2 = S([5, 4]) - e = S([]) - self.failUnless(s1 & s2 == e) - - def testDifference1(self): - s1 = S([1, 2, 3]) - s2 = S([5, 4]) - e = S([1, 2, 3]) - self.failUnless(s1 - s2 == e) - - def testDifference2(self): - s1 = S([1, 2, 3]) - s2 = S([]) - e = S([1, 2, 3]) - self.failUnless(s1 - s2 == e) - - def testDifference3(self): - s1 = S([1, 2, 3]) - s2 = S([3, 2]) - e = S([1]) - self.failUnless(s1 - s2 == e) - - def testDifference4(self): - s1 = S([1, 2, 3]) - s2 = S([3, 2, 1]) - e = S([]) - self.failUnless(s1 - s2 == e) - - def testSubset1(self): - s1 = S([1, 2, 3]) - s2 = S([3, 2, 1]) - self.failUnless(s1.issubset(s2)) - - def testSubset2(self): - s1 = S([1, 2, 3]) - self.failUnless(s1.issubset(s1)) - - def testSubset3(self): - s1 = S([]) - s2 = S([1, 2, 3]) - self.failUnless(s1.issubset(s2)) - - def testSubset4(self): - s1 = S([1]) - s2 = S([1, 2, 3]) - self.failUnless(s1.issubset(s2)) - - def testSubset5(self): - s1 = S([]) - s2 = S([]) - self.failUnless(s1.issubset(s2)) - - def testSubset6(self): - s1 = S([1, 4]) - s2 = S([1, 2, 3]) - self.failUnless(not s1.issubset(s2)) - - def testSuperset1(self): - s1 = S([1, 2, 3]) - s2 = S([3, 2, 1]) - self.failUnless(s1.issuperset(s2)) - - def testSuperset2(self): - s1 = S([1, 2, 3]) - self.failUnless(s1.issuperset(s1)) - - def testSuperset3(self): - s1 = S([1, 2, 3]) - s2 = S([]) - self.failUnless(s1.issuperset(s2)) - - def testSuperset4(self): - s1 = S([1, 2, 3]) - s2 = S([1]) - self.failUnless(s1.issuperset(s2)) - - def testSuperset5(self): - s1 = S([]) - s2 = S([]) - self.failUnless(s1.issuperset(s2)) - - def testSuperset6(self): - s1 = S([1, 2, 3]) - s2 = S([1, 4]) - self.failUnless(not s1.issuperset(s2)) - - def testUpdate1(self): - s1 = S([1, 2, 3]) - u = (4, 5, 6) - e = S([1, 2, 3, 4, 5, 6]) - s1.update(u) - self.failUnless(s1 == e) - - def testUpdate2(self): - s1 = S([1, 2, 3]) - u = [] - e = S([1, 2, 3]) - s1.update(u) - self.failUnless(s1 == e) - - def testGetitem(self): - s1 = S([1, 2, 3]) - i0 = s1[0] - i1 = s1[1] - i2 = s1[2] - s2 = S([i0, i1, i2]) - self.failUnless(s1 == s2) - - def testGetslice(self): - s1 = S([1, 2, 3]) - slice = s1[0:2] - self.failUnless(len(slice) == 2) - item = s1[2] - slice.append(item) - s2 = S(slice) - self.failUnless(s1 == s2) - - def testDelitem(self): - s1 = S([1, 2, 3]) - del s1[0] - i1 = s1[0] - i2 = s1[1] - self.failUnless(i1 != i2) - self.failUnless(i1 == 1 or i1 == 2 or i1 == 3) - self.failUnless(i2 == 1 or i2 == 2 or i2 == 3) - - def testDelslice(self): - s1 = S([1, 2, 3]) - del s1[0:2] - i1 = s1[0] - self.failUnless(i1 == 1 or i1 == 2 or i1 == 3) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/tokenizer.py b/lib/dnspython/tests/tokenizer.py deleted file mode 100644 index 1d561ae1b5..0000000000 --- a/lib/dnspython/tests/tokenizer.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.exception -import dns.tokenizer - -Token = dns.tokenizer.Token - -class TokenizerTestCase(unittest.TestCase): - - def testQuotedString1(self): - tok = dns.tokenizer.Tokenizer(r'"foo"') - token = tok.get() - self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo')) - - def testQuotedString2(self): - tok = dns.tokenizer.Tokenizer(r'""') - token = tok.get() - self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '')) - - def testQuotedString3(self): - tok = dns.tokenizer.Tokenizer(r'"\"foo\""') - token = tok.get() - self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '"foo"')) - - def testQuotedString4(self): - tok = dns.tokenizer.Tokenizer(r'"foo\010bar"') - token = tok.get() - self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo\x0abar')) - - def testQuotedString5(self): - def bad(): - tok = dns.tokenizer.Tokenizer(r'"foo') - token = tok.get() - self.failUnlessRaises(dns.exception.UnexpectedEnd, bad) - - def testQuotedString6(self): - def bad(): - tok = dns.tokenizer.Tokenizer(r'"foo\01') - token = tok.get() - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def testQuotedString7(self): - def bad(): - tok = dns.tokenizer.Tokenizer('"foo\nbar"') - token = tok.get() - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def testEmpty1(self): - tok = dns.tokenizer.Tokenizer('') - token = tok.get() - self.failUnless(token.is_eof()) - - def testEmpty2(self): - tok = dns.tokenizer.Tokenizer('') - token1 = tok.get() - token2 = tok.get() - self.failUnless(token1.is_eof() and token2.is_eof()) - - def testEOL(self): - tok = dns.tokenizer.Tokenizer('\n') - token1 = tok.get() - token2 = tok.get() - self.failUnless(token1.is_eol() and token2.is_eof()) - - def testWS1(self): - tok = dns.tokenizer.Tokenizer(' \n') - token1 = tok.get() - self.failUnless(token1.is_eol()) - - def testWS2(self): - tok = dns.tokenizer.Tokenizer(' \n') - token1 = tok.get(want_leading=True) - self.failUnless(token1.is_whitespace()) - - def testComment1(self): - tok = dns.tokenizer.Tokenizer(' ;foo\n') - token1 = tok.get() - self.failUnless(token1.is_eol()) - - def testComment2(self): - tok = dns.tokenizer.Tokenizer(' ;foo\n') - token1 = tok.get(want_comment = True) - token2 = tok.get() - self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo') and - token2.is_eol()) - - def testComment3(self): - tok = dns.tokenizer.Tokenizer(' ;foo bar\n') - token1 = tok.get(want_comment = True) - token2 = tok.get() - self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo bar') and - token2.is_eol()) - - def testMultiline1(self): - tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)') - tokens = list(iter(tok)) - self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'), - Token(dns.tokenizer.IDENTIFIER, 'bar')]) - - def testMultiline2(self): - tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)\n') - tokens = list(iter(tok)) - self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'), - Token(dns.tokenizer.IDENTIFIER, 'bar'), - Token(dns.tokenizer.EOL, '\n')]) - def testMultiline3(self): - def bad(): - tok = dns.tokenizer.Tokenizer('foo)') - tokens = list(iter(tok)) - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def testMultiline4(self): - def bad(): - tok = dns.tokenizer.Tokenizer('((foo)') - tokens = list(iter(tok)) - self.failUnlessRaises(dns.exception.SyntaxError, bad) - - def testUnget1(self): - tok = dns.tokenizer.Tokenizer('foo') - t1 = tok.get() - tok.unget(t1) - t2 = tok.get() - self.failUnless(t1 == t2 and t1.ttype == dns.tokenizer.IDENTIFIER and \ - t1.value == 'foo') - - def testUnget2(self): - def bad(): - tok = dns.tokenizer.Tokenizer('foo') - t1 = tok.get() - tok.unget(t1) - tok.unget(t1) - self.failUnlessRaises(dns.tokenizer.UngetBufferFull, bad) - - def testGetEOL1(self): - tok = dns.tokenizer.Tokenizer('\n') - t = tok.get_eol() - self.failUnless(t == '\n') - - def testGetEOL2(self): - tok = dns.tokenizer.Tokenizer('') - t = tok.get_eol() - self.failUnless(t == '') - - def testEscapedDelimiter1(self): - tok = dns.tokenizer.Tokenizer(r'ch\ ld') - t = tok.get() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ ld') - - def testEscapedDelimiter2(self): - tok = dns.tokenizer.Tokenizer(r'ch\032ld') - t = tok.get() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\032ld') - - def testEscapedDelimiter3(self): - tok = dns.tokenizer.Tokenizer(r'ch\ild') - t = tok.get() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ild') - - def testEscapedDelimiter1u(self): - tok = dns.tokenizer.Tokenizer(r'ch\ ld') - t = tok.get().unescape() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch ld') - - def testEscapedDelimiter2u(self): - tok = dns.tokenizer.Tokenizer(r'ch\032ld') - t = tok.get().unescape() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == 'ch ld') - - def testEscapedDelimiter3u(self): - tok = dns.tokenizer.Tokenizer(r'ch\ild') - t = tok.get().unescape() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'child') - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/update.py b/lib/dnspython/tests/update.py deleted file mode 100644 index 92ddb565fc..0000000000 --- a/lib/dnspython/tests/update.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import unittest - -import dns.update -import dns.rdata -import dns.rdataset - -goodhex = '0001 2800 0001 0005 0007 0000' \ - '076578616d706c6500 0006 0001' \ - '03666f6fc00c 00ff 00ff 00000000 0000' \ - 'c019 0001 00ff 00000000 0000' \ - '03626172c00c 0001 0001 00000000 0004 0a000005' \ - '05626c617a32c00c 00ff 00fe 00000000 0000' \ - 'c049 0001 00fe 00000000 0000' \ - 'c019 0001 00ff 00000000 0000' \ - 'c019 0001 0001 0000012c 0004 0a000001' \ - 'c019 0001 0001 0000012c 0004 0a000002' \ - 'c035 0001 0001 0000012c 0004 0a000003' \ - 'c035 0001 00fe 00000000 0004 0a000004' \ - '04626c617ac00c 0001 00ff 00000000 0000' \ - 'c049 00ff 00ff 00000000 0000' - -goodwire = goodhex.replace(' ', '').decode('hex_codec') - -update_text="""id 1 -opcode UPDATE -rcode NOERROR -;ZONE -example. IN SOA -;PREREQ -foo ANY ANY -foo ANY A -bar 0 IN A 10.0.0.5 -blaz2 NONE ANY -blaz2 NONE A -;UPDATE -foo ANY A -foo 300 IN A 10.0.0.1 -foo 300 IN A 10.0.0.2 -bar 300 IN A 10.0.0.3 -bar 0 NONE A 10.0.0.4 -blaz ANY A -blaz2 ANY ANY -""" - -class UpdateTestCase(unittest.TestCase): - - def test_to_wire1(self): - update = dns.update.Update('example') - update.id = 1 - update.present('foo') - update.present('foo', 'a') - update.present('bar', 'a', '10.0.0.5') - update.absent('blaz2') - update.absent('blaz2', 'a') - update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') - update.add('bar', 300, 'a', '10.0.0.3') - update.delete('bar', 'a', '10.0.0.4') - update.delete('blaz','a') - update.delete('blaz2') - self.failUnless(update.to_wire() == goodwire) - - def test_to_wire2(self): - update = dns.update.Update('example') - update.id = 1 - update.present('foo') - update.present('foo', 'a') - update.present('bar', 'a', '10.0.0.5') - update.absent('blaz2') - update.absent('blaz2', 'a') - update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') - update.add('bar', 300, dns.rdata.from_text(1, 1, '10.0.0.3')) - update.delete('bar', 'a', '10.0.0.4') - update.delete('blaz','a') - update.delete('blaz2') - self.failUnless(update.to_wire() == goodwire) - - def test_to_wire3(self): - update = dns.update.Update('example') - update.id = 1 - update.present('foo') - update.present('foo', 'a') - update.present('bar', 'a', '10.0.0.5') - update.absent('blaz2') - update.absent('blaz2', 'a') - update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') - update.add('bar', dns.rdataset.from_text(1, 1, 300, '10.0.0.3')) - update.delete('bar', 'a', '10.0.0.4') - update.delete('blaz','a') - update.delete('blaz2') - self.failUnless(update.to_wire() == goodwire) - - def test_from_text1(self): - update = dns.message.from_text(update_text) - w = update.to_wire(origin=dns.name.from_text('example'), - want_shuffle=False) - self.failUnless(w == goodwire) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/tests/zone.py b/lib/dnspython/tests/zone.py deleted file mode 100644 index 31e7405b0f..0000000000 --- a/lib/dnspython/tests/zone.py +++ /dev/null @@ -1,389 +0,0 @@ -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import cStringIO -import filecmp -import os -import unittest - -import dns.exception -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rrset -import dns.zone - -example_text = """$TTL 3600 -$ORIGIN example. -@ soa foo bar 1 2 3 4 5 -@ ns ns1 -@ ns ns2 -ns1 a 10.0.0.1 -ns2 a 10.0.0.2 -$TTL 300 -$ORIGIN foo.example. -bar mx 0 blaz -""" - -example_text_output = """@ 3600 IN SOA foo bar 1 2 3 4 5 -@ 3600 IN NS ns1 -@ 3600 IN NS ns2 -bar.foo 300 IN MX 0 blaz.foo -ns1 3600 IN A 10.0.0.1 -ns2 3600 IN A 10.0.0.2 -""" - -something_quite_similar = """@ 3600 IN SOA foo bar 1 2 3 4 5 -@ 3600 IN NS ns1 -@ 3600 IN NS ns2 -bar.foo 300 IN MX 0 blaz.foo -ns1 3600 IN A 10.0.0.1 -ns2 3600 IN A 10.0.0.3 -""" - -something_different = """@ 3600 IN SOA fooa bar 1 2 3 4 5 -@ 3600 IN NS ns11 -@ 3600 IN NS ns21 -bar.fooa 300 IN MX 0 blaz.fooa -ns11 3600 IN A 10.0.0.11 -ns21 3600 IN A 10.0.0.21 -""" - -ttl_example_text = """$TTL 1h -$ORIGIN example. -@ soa foo bar 1 2 3 4 5 -@ ns ns1 -@ ns ns2 -ns1 1d1s a 10.0.0.1 -ns2 1w1D1h1m1S a 10.0.0.2 -""" - -no_soa_text = """$TTL 1h -$ORIGIN example. -@ ns ns1 -@ ns ns2 -ns1 1d1s a 10.0.0.1 -ns2 1w1D1h1m1S a 10.0.0.2 -""" - -no_ns_text = """$TTL 1h -$ORIGIN example. -@ soa foo bar 1 2 3 4 5 -""" - -include_text = """$INCLUDE "example" -""" - -bad_directive_text = """$FOO bar -$ORIGIN example. -@ soa foo bar 1 2 3 4 5 -@ ns ns1 -@ ns ns2 -ns1 1d1s a 10.0.0.1 -ns2 1w1D1h1m1S a 10.0.0.2 -""" - -_keep_output = False - -class ZoneTestCase(unittest.TestCase): - - def testFromFile1(self): - z = dns.zone.from_file('example', 'example') - ok = False - try: - z.to_file('example1.out', nl='\x0a') - ok = filecmp.cmp('example1.out', 'example1.good') - finally: - if not _keep_output: - os.unlink('example1.out') - self.failUnless(ok) - - def testFromFile2(self): - z = dns.zone.from_file('example', 'example', relativize=False) - ok = False - try: - z.to_file('example2.out', relativize=False, nl='\x0a') - ok = filecmp.cmp('example2.out', 'example2.good') - finally: - if not _keep_output: - os.unlink('example2.out') - self.failUnless(ok) - - def testFromText(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - f = cStringIO.StringIO() - names = z.nodes.keys() - names.sort() - for n in names: - print >> f, z[n].to_text(n) - self.failUnless(f.getvalue() == example_text_output) - - def testTorture1(self): - # - # Read a zone containing all our supported RR types, and - # for each RR in the zone, convert the rdata into wire format - # and then back out, and see if we get equal rdatas. - # - f = cStringIO.StringIO() - o = dns.name.from_text('example.') - z = dns.zone.from_file('example', o) - for (name, node) in z.iteritems(): - for rds in node: - for rd in rds: - f.seek(0) - f.truncate() - rd.to_wire(f, origin=o) - wire = f.getvalue() - rd2 = dns.rdata.from_wire(rds.rdclass, rds.rdtype, - wire, 0, len(wire), - origin = o) - self.failUnless(rd == rd2) - - def testEqual(self): - z1 = dns.zone.from_text(example_text, 'example.', relativize=True) - z2 = dns.zone.from_text(example_text_output, 'example.', - relativize=True) - self.failUnless(z1 == z2) - - def testNotEqual1(self): - z1 = dns.zone.from_text(example_text, 'example.', relativize=True) - z2 = dns.zone.from_text(something_quite_similar, 'example.', - relativize=True) - self.failUnless(z1 != z2) - - def testNotEqual2(self): - z1 = dns.zone.from_text(example_text, 'example.', relativize=True) - z2 = dns.zone.from_text(something_different, 'example.', - relativize=True) - self.failUnless(z1 != z2) - - def testNotEqual3(self): - z1 = dns.zone.from_text(example_text, 'example.', relativize=True) - z2 = dns.zone.from_text(something_different, 'example2.', - relativize=True) - self.failUnless(z1 != z2) - - def testFindRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rds = z.find_rdataset('@', 'soa') - exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') - self.failUnless(rds == exrds) - - def testFindRdataset2(self): - def bad(): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rds = z.find_rdataset('@', 'loc') - self.failUnlessRaises(KeyError, bad) - - def testFindRRset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rrs = z.find_rrset('@', 'soa') - exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') - self.failUnless(rrs == exrrs) - - def testFindRRset2(self): - def bad(): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rrs = z.find_rrset('@', 'loc') - self.failUnlessRaises(KeyError, bad) - - def testGetRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rds = z.get_rdataset('@', 'soa') - exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') - self.failUnless(rds == exrds) - - def testGetRdataset2(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rds = z.get_rdataset('@', 'loc') - self.failUnless(rds == None) - - def testGetRRset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rrs = z.get_rrset('@', 'soa') - exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') - self.failUnless(rrs == exrrs) - - def testGetRRset2(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rrs = z.get_rrset('@', 'loc') - self.failUnless(rrs == None) - - def testReplaceRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rdataset = dns.rdataset.from_text('in', 'ns', 300, 'ns3', 'ns4') - z.replace_rdataset('@', rdataset) - rds = z.get_rdataset('@', 'ns') - self.failUnless(rds is rdataset) - - def testReplaceRdataset2(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - rdataset = dns.rdataset.from_text('in', 'txt', 300, '"foo"') - z.replace_rdataset('@', rdataset) - rds = z.get_rdataset('@', 'txt') - self.failUnless(rds is rdataset) - - def testDeleteRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - z.delete_rdataset('@', 'ns') - rds = z.get_rdataset('@', 'ns') - self.failUnless(rds is None) - - def testDeleteRdataset2(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - z.delete_rdataset('ns1', 'a') - node = z.get_node('ns1') - self.failUnless(node is None) - - def testNodeFindRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - node = z['@'] - rds = node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) - exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') - self.failUnless(rds == exrds) - - def testNodeFindRdataset2(self): - def bad(): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - node = z['@'] - rds = node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) - self.failUnlessRaises(KeyError, bad) - - def testNodeGetRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - node = z['@'] - rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) - exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') - self.failUnless(rds == exrds) - - def testNodeGetRdataset2(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - node = z['@'] - rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) - self.failUnless(rds == None) - - def testNodeDeleteRdataset1(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - node = z['@'] - rds = node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) - rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) - self.failUnless(rds == None) - - def testNodeDeleteRdataset2(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - node = z['@'] - rds = node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) - rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) - self.failUnless(rds == None) - - def testIterateRdatasets(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - ns = [n for n, r in z.iterate_rdatasets('A')] - ns.sort() - self.failUnless(ns == [dns.name.from_text('ns1', None), - dns.name.from_text('ns2', None)]) - - def testIterateAllRdatasets(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - ns = [n for n, r in z.iterate_rdatasets()] - ns.sort() - self.failUnless(ns == [dns.name.from_text('@', None), - dns.name.from_text('@', None), - dns.name.from_text('bar.foo', None), - dns.name.from_text('ns1', None), - dns.name.from_text('ns2', None)]) - - def testIterateRdatas(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - l = list(z.iterate_rdatas('A')) - l.sort() - exl = [(dns.name.from_text('ns1', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, - '10.0.0.1')), - (dns.name.from_text('ns2', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, - '10.0.0.2'))] - self.failUnless(l == exl) - - def testIterateAllRdatas(self): - z = dns.zone.from_text(example_text, 'example.', relativize=True) - l = list(z.iterate_rdatas()) - l.sort() - exl = [(dns.name.from_text('@', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, - 'ns1')), - (dns.name.from_text('@', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, - 'ns2')), - (dns.name.from_text('@', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, - 'foo bar 1 2 3 4 5')), - (dns.name.from_text('bar.foo', None), - 300, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, - '0 blaz.foo')), - (dns.name.from_text('ns1', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, - '10.0.0.1')), - (dns.name.from_text('ns2', None), - 3600, - dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, - '10.0.0.2'))] - self.failUnless(l == exl) - - def testTTLs(self): - z = dns.zone.from_text(ttl_example_text, 'example.', relativize=True) - n = z['@'] - rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) - self.failUnless(rds.ttl == 3600) - n = z['ns1'] - rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) - self.failUnless(rds.ttl == 86401) - n = z['ns2'] - rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) - self.failUnless(rds.ttl == 694861) - - def testNoSOA(self): - def bad(): - z = dns.zone.from_text(no_soa_text, 'example.', - relativize=True) - self.failUnlessRaises(dns.zone.NoSOA, bad) - - def testNoNS(self): - def bad(): - z = dns.zone.from_text(no_ns_text, 'example.', - relativize=True) - self.failUnlessRaises(dns.zone.NoNS, bad) - - def testInclude(self): - z1 = dns.zone.from_text(include_text, 'example.', relativize=True, - allow_include=True) - z2 = dns.zone.from_file('example', 'example.', relativize=True) - self.failUnless(z1 == z2) - - def testBadDirective(self): - def bad(): - z = dns.zone.from_text(bad_directive_text, 'example.', - relativize=True) - self.failUnlessRaises(dns.exception.SyntaxError, bad) - -if __name__ == '__main__': - unittest.main() diff --git a/lib/dnspython/util/COPYRIGHT b/lib/dnspython/util/COPYRIGHT deleted file mode 100644 index 7390363fbb..0000000000 --- a/lib/dnspython/util/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) @YEARS@ Nominum, Inc. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose with or without fee is hereby granted, -provided that the above copyright notice and this permission notice -appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/lib/dnspython/util/copyrights b/lib/dnspython/util/copyrights deleted file mode 100644 index f73e9f6fe3..0000000000 --- a/lib/dnspython/util/copyrights +++ /dev/null @@ -1,116 +0,0 @@ -./.gitignore X 2009 -./ChangeLog X 2003,2004,2005,2006,2007 -./LICENSE X 2003,2004,2005,2006,2007 -./MANIFEST.in X 2003,2004,2005,2006,2007 -./Makefile MAKE 2003,2004,2005,2006,2007,2009,2011 -./README X 2003,2004,2005,2006,2007 -./TODO X 2003,2004,2005,2006,2007 -./dns/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2011 -./dns/dnssec.py PYTHON 2003,2004,2005,2006,2007,2009,2011 -./dns/e164.py PYTHON 2006,2007,2009,2011 -./dns/edns.py PYTHON 2009,2011 -./dns/entropy.py PYTHON 2009,2011 -./dns/exception.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/flags.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/hash.py PYTHON 2011 -./dns/inet.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/ipv4.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/ipv6.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/message.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/name.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/namedict.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/node.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/opcode.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/query.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rcode.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdata.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdataclass.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdataset.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdatatype.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/AFSDB.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/CERT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/CNAME.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/DLV.py PYTHON 2009,2010,2011 -./dns/rdtypes/ANY/DNAME.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/DNSKEY.py PYTHON 2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/DS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/GPOS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/HINFO.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/HIP.py PYTHON 2010,2011 -./dns/rdtypes/ANY/ISDN.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/LOC.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/MX.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/NS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/NSEC.py PYTHON 2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/NSEC3.py PYTHON 2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/NSEC3PARAM.py PYTHON 2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/PTR.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/RP.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/RRSIG.py PYTHON 2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/RT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/SOA.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/SPF.py PYTHON 2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/SSHFP.py PYTHON 2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/TXT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/X25.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/ANY/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/A.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/AAAA.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/APL.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/DHCID.py PYTHON 2006,2007,2009,2010,2011 -./dns/rdtypes/IN/IPSECKEY.py PYTHON 2006,2007,2009,2010,2011 -./dns/rdtypes/IN/KX.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/NAPTR.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/NSAP.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/NSAP_PTR.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/PX.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/SRV.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/WKS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/IN/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/dsbase.py PYTHON 2010,2011 -./dns/rdtypes/mxbase.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/nsbase.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/rdtypes/txtbase.py PYTHON 2006,2007,2009,2010,2011 -./dns/renderer.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/resolver.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/reversename.py PYTHON 2006,2007,2009,2010,2011 -./dns/rrset.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/set.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/tokenizer.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/tsig.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 -./dns/tsigkeyring.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/ttl.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/update.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/version.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./dns/wiredata.py PYTHON 2011 -./dns/zone.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./examples/ddns.py X 2006,2007 -./examples/e164.py X 2006,2007 -./examples/mx.py X 2003,2004,2005,2006,2007 -./examples/name.py X 2003,2004,2005,2006,2007 -./examples/reverse.py X 2003,2004,2005,2006,2007 -./examples/reverse_name.py X 2006,2007 -./examples/xfr.py X 2003,2004,2005,2006,2007 -./examples/zonediff.py X 2010,2011 -./setup.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/Makefile MAKE 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/bugs.py PYTHON 2006,2007,2009,2010,2011 -./tests/dnssec.py PYTHON 2011 -./tests/example X 2003,2004,2005,2006,2007 -./tests/example1.good X 2003,2004,2005,2006,2007 -./tests/example2.good X 2003,2004,2005,2006,2007 -./tests/flags.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/message.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/name.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/namedict.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/ntoaaton.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/rdtypeandclass.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/resolver.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/rrset.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/set.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/tokenizer.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/update.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./tests/zone.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 -./util/COPYRIGHT X 2003,2004,2005,2006,2007 -./util/copyrights X 2003,2004,2005,2006,2007 diff --git a/lib/wscript_build b/lib/wscript_build index 97c3cfa240..c7bf62124b 100644 --- a/lib/wscript_build +++ b/lib/wscript_build @@ -4,7 +4,6 @@ import os, Options # work out what python external libraries we need to install external_libs = { - "dns.resolver": "dnspython/dns", "subunit": "subunit/python/subunit", "testtools": "testtools/testtools"} diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 181e67fdf2..c2ee3ecb7f 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -47,7 +47,7 @@ from samba.auth import system_session from samba.samdb import SamDB from samba.dcerpc import netlogon, winbind -samba.ensure_external_module("dns", "dnspython") +samba.ensure_third_party_module("dns", "dnspython") import dns.resolver import dns.exception diff --git a/source4/scripting/bin/samba_upgradedns b/source4/scripting/bin/samba_upgradedns index 4d49770493..689c9a435c 100755 --- a/source4/scripting/bin/samba_upgradedns +++ b/source4/scripting/bin/samba_upgradedns @@ -67,7 +67,7 @@ from samba.provision.sambadns import ( create_named_txt ) from samba.dcerpc import security -samba.ensure_external_module("dns", "dnspython") +samba.ensure_third_party_module("dns", "dnspython") import dns.zone, dns.rdatatype __docformat__ = 'restructuredText' diff --git a/third_party/dnspython/.gitignore b/third_party/dnspython/.gitignore new file mode 100644 index 0000000000..5592c971b0 --- /dev/null +++ b/third_party/dnspython/.gitignore @@ -0,0 +1,7 @@ +build +dist +MANIFEST +html +html.zip +html.tar.gz +tests/*.out diff --git a/third_party/dnspython/ChangeLog b/third_party/dnspython/ChangeLog new file mode 100644 index 0000000000..71b7961490 --- /dev/null +++ b/third_party/dnspython/ChangeLog @@ -0,0 +1,1194 @@ +2011-08-22 Robert Halley + + * dns/resolver.py: Added LRUCache, which allows a maximum number + of nodes to be cached, and removes the least-recently used node + when adding a new node to a full cache. + +2011-07-13 Bob Halley + + * dns/resolver.py: dns.resolver.override_system_resolver() + overrides the socket module's versions of getaddrinfo(), + getnameinfo(), getfqdn(), gethostbyname(), gethostbyname_ex() and + gethostbyaddr() with an implementation which uses a dnspython stub + resolver instead of the system's stub resolver. This can be + useful in testing situations where you want to control the + resolution behavior of python code without having to change the + system's resolver settings (e.g. /etc/resolv.conf). + dns.resolver.restore_system_resolver() undoes the change. + +2011-07-08 Bob Halley + + * dns/ipv4.py: dnspython now provides its own, stricter, versions + of IPv4 inet_ntoa() and inet_aton() instead of using the OS's + versions. + + * dns/ipv6.py: inet_aton() now bounds checks embedded IPv4 addresses + more strictly. Also, now only dns.exception.SyntaxError can be + raised on bad input. + +2011-04-05 Bob Halley + + * Old DNSSEC types (KEY, NXT, and SIG) have been removed. + + * Bounds checking of slices in rdata wire processing is now more + strict, and bounds errors (e.g. we got less data than was + expected) now raise dns.exception.FormError rather than + IndexError. + +2011-03-28 Bob Halley + + * (Version 1.9.4 released) + +2011-03-24 Bob Halley + + * dns/rdata.py (Rdata._wire_cmp): We need to specify no + compression and an origin to _wire_cmp() in case names in the + rdata are relative names. + + * dns/rdtypes/ANY/SIG.py (SIG._cmp): Add missing 'import struct'. + Thanks to Arfrever Frehtes Taifersar Arahesis for reporting the + problem. + +2011-03-24 Bob Halley + + * (Version 1.9.3 released) + +2011-03-22 Bob Halley + + * dns/resolver.py: a boolean parameter, 'raise_on_no_answer', has + been added to the query() methods. In no-error, no-data + situations, this parameter determines whether NoAnswer should be + raised or not. If True, NoAnswer is raised. If False, then an + Answer() object with a None rrset will be returned. + + * dns/resolver.py: Answer() objects now have a canonical_name field. + +2011-01-11 Bob Halley + + * Dnspython was erroneously doing case-insensitive comparisons + of the names in NSEC and RRSIG RRs. Thanks to Casey Deccio for + reporting this bug. + +2010-12-17 Bob Halley + + * dns/message.py (_WireReader._get_section): use "is" and not "==" + when testing what section an RR is in. Thanks to James Raftery + for reporting this bug. + +2010-12-10 Bob Halley + + * dns/resolver.py (Resolver.query): disallow metaqueries. + + * dns/rdata.py (Rdata.__hash__): Added a __hash__ method for rdata. + +2010-11-23 Bob Halley + + * (Version 1.9.2 released) + +2010-11-23 Bob Halley + + * dns/dnssec.py (_need_pycrypto): DSA and RSA are modules, not + functions, and I didn't notice because the test suite masked + the bug! *sigh* + +2010-11-22 Bob Halley + + * (Version 1.9.1 released) + +2010-11-22 Bob Halley + + * dns/dnssec.py: the "from" style import used to get DSA from + PyCrypto trashed a DSA constant. Now a normal import is used + to avoid namespace contamination. + +2010-11-20 Bob Halley + + * (Version 1.9.0 released) + +2010-11-07 Bob Halley + + * dns/dnssec.py: Added validate() to do basic DNSSEC validation + (requires PyCrypto). Thanks to Brian Wellington for the patch. + + * dns/hash.py: Hash compatibility handling is now its own module. + +2010-10-31 Bob Halley + + * dns/resolver.py (zone_for_name): A query name resulting in a + CNAME or DNAME response to a node which had an SOA was incorrectly + treated as a zone origin. In these cases, we should just look + higher. Thanks to Gert Berger for reporting this problem. + + * Added zonediff.py to examples. This program compares two zones + and shows the differences either in diff-like plain text, or + HTML. Thanks to Dennis Kaarsemaker for contributing this + useful program. + +2010-10-27 Bob Halley + + * Incorporate a patch to use poll() instead of select() by + default on platforms which support it. Thanks to + Peter Schüller and Spotify for the contribution. + +2010-10-17 Bob Halley + + * Python prior to 2.5.2 doesn't compute the correct values for + HMAC-SHA384 and HMAC-SHA512. We now detect attempts to use + them and raise NotImplemented if the Python version is too old. + Thanks to Kevin Chen for reporting the problem. + + * Various routines that took the string forms of rdata types and + classes did not permit the strings to be Unicode strings. + Thanks to Ryan Workman for reporting the issue. + + * dns/tsig.py: Added symbolic constants for the algorithm strings. + E.g. you can now say dns.tsig.HMAC_MD5 instead of + "HMAC-MD5.SIG-ALG.REG.INT". Thanks to Cillian Sharkey for + suggesting this improvement. + + * dns/tsig.py (get_algorithm): fix hashlib compatibility; thanks to + Kevin Chen for the patch. + + * dns/dnssec.py: Added key_id() and make_ds(). + + * dns/message.py: message.py needs to import dns.edns since it uses + it. + +2010-05-04 Bob Halley + + * dns/rrset.py (RRset.__init__): "covers" was not passed to the + superclass __init__(). Thanks to Shanmuga Rajan for reporting + the problem. + +2010-03-10 Bob Halley + + * The TSIG algorithm value was passed to use_tsig() incorrectly + in some cases. Thanks to 'ducciovigolo' for reporting the problem. + +2010-01-26 Bob Halley + + * (Version 1.8.0 released) + +2010-01-13 Bob Halley + + * dns/dnssec.py: Added RSASHA256 and RSASHA512 codepoints; added + other missing codepoints to _algorithm_by_text. + +2010-01-12 Bob Halley + + * Escapes in masterfiles now work correctly. Previously they were + only working correctly when the text involved was part of a domain + name. + + * dns/tokenizer.py: The tokenizer's get() method now returns Token + objects, not (type, text) tuples. + +2009-11-13 Bob Halley + + * Support has been added for hmac-sha1, hmac-sha224, hmac-sha256, + hmac-sha384 and hmac-sha512. Thanks to Kevin Chen for a + thoughtful, high quality patch. + + * dns/update.py (Update::present): A zero TTL was not added if + present() was called with a single rdata, causing _add() to be + unhappy. Thanks to Eugene Kim for reporting the problem and + submitting a patch. + + * dns/entropy.py: Use os.urandom() if present. Don't seed until + someone wants randomness. + +2009-09-16 Bob Halley + + * dns/entropy.py: The entropy module needs locking in order to be + used safely in a multithreaded environment. Thanks to Beda Kosata + for reporting the problem. + +2009-07-27 Bob Halley + + * dns/query.py (xfr): The socket was not set to nonblocking mode. + Thanks to Erik Romijn for reporting this problem. + +2009-07-23 Bob Halley + + * dns/rdtypes/IN/SRV.py (SRV._cmp): SRV records were compared + incorrectly due to a cut-and-paste error. Thanks to Tommie + Gannert for reporting this bug. + + * dns/e164.py (query): The resolver parameter was not used. + Thanks to Matías Bellone for reporting this bug. + +2009-06-23 Bob Halley + + * dns/entropy.py (EntropyPool.__init__): open /dev/random unbuffered; + there's no need to consume more randomness than we need. Thanks + to Brian Wellington for the patch. + +2009-06-19 Bob Halley + + * (Version 1.7.1 released) + +2009-06-19 Bob Halley + + * DLV.py was omitted from the kit + + * Negative prerequisites were not handled correctly in _get_section(). + +2009-06-19 Bob Halley + + * (Version 1.7.0 released) + +2009-06-19 Bob Halley + + * On Windows, the resolver set the domain incorrectly. Thanks + to Brandon Carpenter for reporting this bug. + + * Added a to_digestable() method to rdata classes; it returns the + digestable form (i.e. DNSSEC canonical form) of the rdata. For + most rdata types this is the same uncompressed wire form. For + certain older DNS RR types, however, domain names in the rdata + are downcased. + + * Added support for the HIP RR type. + +2009-06-18 Bob Halley + + * Added support for the DLV RR type. + + * Added various DNSSEC related constants (e.g. algorithm identifiers, + flag values). + + * dns/tsig.py: Added support for BADTRUNC result code. + + * dns/query.py (udp): When checking that addresses are the same, + use the binary form of the address in the comparison. This + ensures that we don't treat addresses as different if they have + equivalent but differing textual representations. E.g. "1:00::1" + and "1::1" represent the same address but are not textually equal. + Thanks to Kim Davies for reporting this bug. + + * The resolver's query() method now has an optional 'source' parameter, + allowing the source IP address to be specified. Thanks to + Alexander Lind for suggesting the change and sending a patch. + + * Added NSEC3 and NSEC3PARAM support. + +2009-06-17 Bob Halley + + * Fixed NSEC.to_text(), which was only printing the last window. + Thanks to Brian Wellington for finding the problem and fixing it. + +2009-03-30 Bob Halley + + * dns/query.py (xfr): Allow UDP IXFRs. Use "one_rr_per_rrset" mode when + doing IXFR. + +2009-03-30 Bob Halley + + * Add "one_rr_per_rrset" mode switch to methods which parse + messages from wire format (e.g. dns.message.from_wire(), + dns.query.udp(), dns.query.tcp()). If set, each RR read is + placed in its own RRset (instead of being coalesced). + +2009-03-30 Bob Halley + + * Added EDNS option support. + +2008-10-16 Bob Halley + + * dns/rdtypes/ANY/DS.py: The from_text() parser for DS RRs did not + allow multiple Base64 chunks. Thanks to Rakesh Banka for + finding this bug and submitting a patch. + +2008-10-08 Bob Halley + + * Add entropy module. + + * When validating TSIGs, we need to use the absolute name. + +2008-06-03 Bob Halley + + * dns/message.py (Message.set_rcode): The mask used preserved the + extended rcode, instead of everything else in ednsflags. + + * dns/message.py (Message.use_edns): ednsflags was not kept + coherent with the specified edns version. + +2008-02-06 Bob Halley + + * dns/ipv6.py (inet_aton): We could raise an exception other than + dns.exception.SyntaxError in some cases. + + * dns/tsig.py: Raise an exception when the peer has set a non-zero + TSIG error. + +2007-11-25 Bob Halley + + * (Version 1.6.0 released) + +2007-11-25 Bob Halley + + * dns/query.py (_wait_for): if select() raises an exception due to + EINTR, we should just select() again. + +2007-06-13 Bob Halley + + * dns/inet.py: Added is_multicast(). + + * dns/query.py (udp): If the queried address is a multicast address, then + don't check that the address of the response is the same as the address + queried. + +2007-05-24 Bob Halley + + * dns/rdtypes/IN/NAPTR.py: NAPTR comparisons didn't compare the + preference field due to a typo. + +2007-02-07 Bob Halley + + * dns/resolver.py: Integrate code submitted by Paul Marks to + determine whether a Windows NIC is enabled. The way dnspython + used to do this does not work on Windows Vista. + +2006-12-10 Bob Halley + + * (Version 1.5.0 released) + +2006-11-03 Bob Halley + + * dns/rdtypes/IN/DHCID.py: Added support for the DHCID RR type. + +2006-11-02 Bob Halley + + * dns/query.py (udp): Messages from unexpected sources can now be + ignored by setting ignore_unexpected to True. + +2006-10-31 Bob Halley + + * dns/query.py (udp): When raising UnexpectedSource, add more + detail about what went wrong to the exception. + +2006-09-22 Bob Halley + + * dns/message.py (Message.use_edns): add reasonable defaults for + the ednsflags, payload, and request_payload parameters. + + * dns/message.py (Message.want_dnssec): add a convenience method for + enabling/disabling the "DNSSEC desired" flag in requests. + + * dns/message.py (make_query): add "use_edns" and "want_dnssec" + parameters. + +2006-08-17 Bob Halley + + * dns/resolver.py (Resolver.read_resolv_conf): If /etc/resolv.conf + doesn't exist, just use the default resolver configuration (i.e. + the same thing we would have used if resolv.conf had existed and + been empty). + +2006-07-26 Bob Halley + + * dns/resolver.py (Resolver._config_win32_fromkey): fix + cut-and-paste error where we passed the wrong variable to + self._config_win32_search(). Thanks to David Arnold for finding + the bug and submitting a patch. + +2006-07-20 Bob Halley + + * dns/resolver.py (Answer): Add more support for the sequence + protocol, forwarding requests to the answer object's rrset. + E.g. "for a in answer" is equivalent to "for a in answer.rrset", + "answer[i]" is equivalent to "answer.rrset[i]", and + "answer[i:j]" is equivalent to "answer.rrset[i:j]". + +2006-07-19 Bob Halley + + * dns/query.py (xfr): Add IXFR support. + +2006-06-22 Bob Halley + + * dns/rdtypes/IN/IPSECKEY.py: Added support for the IPSECKEY RR type. + +2006-06-21 Bob Halley + + * dns/rdtypes/ANY/SPF.py: Added support for the SPF RR type. + +2006-06-02 Bob Halley + + * (Version 1.4.0 released) + +2006-04-25 Bob Halley + + * dns/rrset.py (RRset.to_rdataset): Added a convenience method + to convert an rrset into an rdataset. + +2006-03-27 Bob Halley + + * Added dns.e164.query(). This function can be used to look for + NAPTR RRs for a specified number in several domains, e.g.: + + dns.e164.query('16505551212', + ['e164.dnspython.org.', 'e164.arpa.']) + +2006-03-26 Bob Halley + + * dns/resolver.py (Resolver.query): The resolver deleted from + a list while iterating it, which makes the iterator unhappy. + +2006-03-17 Bob Halley + + * dns/resolver.py (Resolver.query): The resolver needlessly + delayed responses for successful queries. + +2006-01-18 Bob Halley + + * dns/rdata.py: added a validate() method to the rdata class. If + you change an rdata by assigning to its fields, it is a good + idea to call validate() when you are done making changes. + For example, if 'r' is an MX record and then you execute: + + r.preference = 100000 # invalid, because > 65535 + r.validate() + + The validation will fail and an exception will be raised. + +2006-01-11 Bob Halley + + * dns/ttl.py: TTLs are now bounds checked to be within the closed + interval [0, 2^31 - 1]. + + * The BIND 8 TTL syntax is now accepted in the SOA refresh, retry, + expire, and minimum fields, and in the original_ttl field of + SIG and RRSIG records. + +2006-01-04 Bob Halley + + * dns/resolver.py: The windows registry irritatingly changes the + list element delimiter in between ' ' and ',' (and vice-versa) + in various versions of windows. We now cope by always looking + for either one (' ' first). + +2005-12-27 Bob Halley + + * dns/e164.py: Added routines to convert between E.164 numbers and + their ENUM domain name equivalents. + + * dns/reversename.py: Added routines to convert between IPv4 and + IPv6 addresses and their DNS reverse-map equivalents. + +2005-12-18 Bob Halley + + * dns/rdtypes/ANY/LOC.py (_tuple_to_float): The sign was lost when + converting a tuple into a float, which broke conversions of + south latitudes and west longitudes. + +2005-11-17 Bob Halley + + * dns/zone.py: The 'origin' parameter to from_text() and from_file() + is now optional. If not specified, dnspython will use the + first $ORIGIN in the text as the zone's origin. + + * dns/zone.py: Sanity checks of the zone's origin node can now + be disabled. + +2005-11-12 Bob Halley + + * dns/name.py: Preliminary Unicode support has been added for + domain names. Running dns.name.from_text() on a Unicode string + will now encode each label using the IDN ACE encoding. The + to_unicode() method may be used to convert a dns.name.Name with + IDN ACE labels back into a Unicode string. This functionality + requires Python 2.3 or greater. + +2005-10-31 Bob Halley + + * (Version 1.3.5 released) + +2005-10-12 Bob Halley + + * dns/zone.py: Zone.iterate_rdatasets() and Zone.iterate_rdatas() + did not have a default rdtype of dns.rdatatype.ANY as their + docstrings said they did. They do now. + +2005-10-06 Bob Halley + + * dns/name.py: Added the parent() method, which returns the + parent of a name. + +2005-10-01 Bob Halley + + * dns/resolver.py: Added zone_for_name() helper, which returns + the name of the zone which contains the specified name. + + * dns/resolver.py: Added get_default_resolver(), which returns + the default resolver, initializing it if necessary. + +2005-09-29 Bob Halley + + * dns/resolver.py (Resolver._compute_timeout): If time goes + backwards a little bit, ignore it. + +2005-07-31 Bob Halley + + * (Version 1.3.4 released) + +2005-07-31 Bob Halley + + * dns/message.py (make_response): Trying to respond to a response + threw a NameError while trying to throw a FormErr since it used + the wrong name for the FormErr exception. + + * dns/query.py (_connect): We needed to ignore EALREADY too. + + * dns/query.py: Optional "source" and "source_port" parameters + have been added to udp(), tcp(), and xfr(). Thanks to Ralf + Weber for suggesting the change and providing a patch. + +2005-06-05 Bob Halley + + * dns/query.py: The requirement that the "where" parameter be + an IPv4 or IPv6 address is now documented. + +2005-06-04 Bob Halley + + * dns/resolver.py: The resolver now does exponential backoff + each time it runs through all of the nameservers. + + * dns/resolver.py: rcodes which indicate a nameserver is likely + to be a "permanent failure" for a query cause the nameserver + to be removed from the mix for that query. + +2005-01-30 Bob Halley + + * (Version 1.3.3 released) + +2004-10-25 Bob Halley + + * dns/rdtypes/ANY/TXT.py (TXT.from_text): The masterfile parser + incorrectly rejected TXT records where a value was not quoted. + +2004-10-11 Bob Halley + + * dns/message.py: Added make_response(), which creates a skeletal + response for the specified query. Added opcode() and set_opcode() + convenience methods to the Message class. Added the request_payload + attribute to the Message class. + +2004-10-10 Bob Halley + + * dns/zone.py (from_xfr): dns.zone.from_xfr() in relativization + mode incorrectly set zone.origin to the empty name. + +2004-09-02 Bob Halley + + * dns/name.py (Name.to_wire): The 'file' parameter to + Name.to_wire() is now optional; if omitted, the wire form will + be returned as the value of the function. + +2004-08-14 Bob Halley + + * dns/message.py (Message.find_rrset): find_rrset() now uses an + index, vastly improving the from_wire() performance of large + messages such as zone transfers. + +2004-08-07 Bob Halley + + * (Version 1.3.2 released) + +2004-08-04 Bob Halley + + * dns/query.py: sending queries to a nameserver via IPv6 now + works. + + * dns/inet.py (af_for_address): Add af_for_address(), which looks + at a textual-form address and attempts to determine which address + family it is. + + * dns/query.py: the default for the 'af' parameter of the udp(), + tcp(), and xfr() functions has been changed from AF_INET to None, + which causes dns.inet.af_for_address() to be used to determine the + address family. If dns.inet.af_for_address() can't figure it out, + we fall back to AF_INET and hope for the best. + +2004-07-31 Bob Halley + + * dns/rdtypes/ANY/NSEC.py (NSEC.from_text): The NSEC text format + does not allow specifying types by number, so we shouldn't either. + + * dns/renderer.py: the renderer module didn't import random, + causing an exception to be raised if a query id wasn't provided + when a Renderer was created. + + * dns/resolver.py (Resolver.query): the resolver wasn't catching + dns.exception.Timeout, so a timeout erroneously caused the whole + resolution to fail instead of just going on to the next server. + +2004-06-16 Bob Halley + + * dns/rdtypes/ANY/LOC.py (LOC.from_text): LOC milliseconds values + were converted incorrectly if the length of the milliseconds + string was less than 3. + +2004-06-06 Bob Halley + + * (Version 1.3.1 released) + +2004-05-22 Bob Halley + + * dns/update.py (Update.delete): We erroneously specified a + "deleting" value of dns.rdatatype.NONE instead of + dns.rdataclass.NONE when the thing being deleted was either an + Rdataset instance or an Rdata instance. + + * dns/rdtypes/ANY/SSHFP.py: Added support for the proposed SSHFP + RR type. + +2004-05-14 Bob Halley + + * dns/rdata.py (from_text): The masterfile reader did not + accept the unknown RR syntax when used with a known RR type. + +2004-05-08 Bob Halley + + * dns/name.py (from_text): dns.name.from_text() did not raise + an exception if a backslash escape ended prematurely. + +2004-04-09 Bob Halley + + * dns/zone.py (_MasterReader._rr_line): The masterfile reader + erroneously treated lines starting with leading whitespace but + not having any RR definition as an error. It now treats + them like a blank line (which is not an error). + +2004-04-01 Bob Halley + + * (Version 1.3.0 released) + +2004-03-19 Bob Halley + + * Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. + +2004-01-16 Bob Halley + + * dns/query.py (_connect): Windows returns EWOULDBLOCK instead + of EINPROGRESS when trying to connect a nonblocking socket. + +2003-11-13 Bob Halley + + * dns/rdtypes/ANY/LOC.py (LOC.to_wire): We encoded and decoded LOC + incorrectly, since we were interpreting the values of altitiude, + size, hprec, and vprec in meters instead of centimeters. + + * dns/rdtypes/IN/WKS.py (WKS.from_wire): The WKS protocol value is + encoded with just one octet, not two! + +2003-11-09 Bob Halley + + * dns/resolver.py (Cache.maybe_clean): The cleaner deleted items + from the dictionary while iterating it, causing a RuntimeError + to be raised. Thanks to Mark R. Levinson for the bug report, + regression test, and fix. + +2003-11-07 Bob Halley + + * (Version 1.2.0 released) + +2003-11-03 Bob Halley + + * dns/zone.py (_MasterReader.read): The saved_state now includes + the default TTL. + +2003-11-01 Bob Halley + + * dns/tokenizer.py (Tokenizer.get): The tokenizer didn't + handle escaped delimiters. + +2003-10-27 Bob Halley + + * dns/resolver.py (Resolver.read_resolv_conf): If no nameservers + are configured in /etc/resolv.conf, the default nameserver + list should be ['127.0.0.1']. + +2003-09-08 Bob Halley + + * dns/resolver.py (Resolver._config_win32_fromkey): We didn't + catch WindowsError, which can happen if a key is not defined + in the registry. + +2003-09-06 Bob Halley + + * (Version 1.2.0b1 released) + +2003-09-05 Bob Halley + + * dns/query.py: Timeout support has been overhauled to provide + timeouts under Python 2.2 as well as 2.3, and to provide more + accurate expiration. + +2003-08-30 Bob Halley + + * dns/zone.py: dns.exception.SyntaxError is raised for unknown + master file directives. + +2003-08-28 Bob Halley + + * dns/zone.py: $INCLUDE processing is now enabled/disabled using + the allow_include parameter. The default is to process $INCLUDE + for from_file(), and to disallow $INCLUDE for from_text(). The + master reader now calls zone.check_origin_node() by default after + the zone has been read. find_rdataset() called get_node() instead + of find_node(), which result in an incorrect exception. The + relativization state of a zone is now remembered and applied + consistently when looking up names. from_xfr() now supports + relativization like the _MasterReader. + +2003-08-22 Bob Halley + + * dns/zone.py: The _MasterReader now understands $INCLUDE. + +2003-08-12 Bob Halley + + * dns/zone.py: The _MasterReader now specifies the file and line + number when a syntax error occurs. The BIND 8 TTL format is now + understood when loading a zone, though it will never be emitted. + The from_file() function didn't pass the zone_factory parameter + to from_text(). + +2003-08-10 Bob Halley + + * (Version 1.1.0 released) + +2003-08-07 Bob Halley + + * dns/update.py (Update._add): A typo meant that _add would + fail if the thing being added was an Rdata object (as + opposed to an Rdataset or the textual form of an Rdata). + +2003-08-05 Bob Halley + + * dns/set.py: the simple Set class has been moved to its + own module, and augmented to support more set operations. + +2003-08-04 Bob Halley + + * Node and all rdata types have been "slotted". This speeds + things up a little and reduces memory usage noticeably. + +2003-08-02 Bob Halley + + * (Version 1.1.0c1 released) + +2003-08-02 Bob Halley + + * dns/rdataset.py: SimpleSets now support more set options. + + * dns/message.py: Added the get_rrset() method. from_file() now + allows Unicode filenames and turns on universal newline support if + it opens the file itself. + + * dns/node.py: Added the delete_rdataset() and replace_rdataset() + methods. + + * dns/zone.py: Added the delete_node(), delete_rdataset(), and + replace_rdataset() methods. from_file() now allows Unicode + filenames and turns on universal newline support if it opens the + file itself. Added a to_file() method. + +2003-08-01 Bob Halley + + * dns/opcode.py: Opcode from/to text converters now understand + numeric opcodes. The to_text() method will return a numeric opcode + string if it doesn't know a text name for the opcode. + + * dns/message.py: Added set_rcode(). Fixed code where ednsflags + wasn't treated as a long. + + * dns/rcode.py: ednsflags wasn't treated as a long. Rcode from/to + text converters now understand numeric rcodes. The to_text() + method will return a numeric rcode string if it doesn't know + a text name for the rcode. + + * examples/reverse.py: Added a new example program that builds a + reverse (address-to-name) mapping table from the name-to-address + mapping specified by A RRs in zone files. + + * dns/node.py: Added get_rdataset() method. + + * dns/zone.py: Added get_rdataset() and get_rrset() methods. Added + iterate_rdatas(). + +2003-07-31 Bob Halley + + * dns/zone.py: Added the iterate_rdatasets() method which returns + a generator which yields (name, rdataset) tuples for all the + rdatasets in the zone matching the specified rdatatype. + +2003-07-30 Bob Halley + + * (Version 1.1.0b2 released) + +2003-07-30 Bob Halley + + * dns/zone.py: Added find_rrset() and find_rdataset() convenience + methods. They let you retrieve rdata with the specified name + and type in one call. + + * dns/node.py: Nodes no longer have names; owner names are + associated with nodes in the Zone object's nodes dictionary. + + * dns/zone.py: Zone objects now implement more of the standard + mapping interface. __iter__ has been changed to iterate the keys + rather than values to match the standard mapping interface's + behavior. + +2003-07-20 Bob Halley + + * dns/ipv6.py (inet_ntoa): Handle embedded IPv4 addresses. + +2003-07-19 Bob Halley + + * (Version 1.1.0b1 released) + +2003-07-18 Bob Halley + + * dns/tsig.py: The TSIG validation of TCP streams where not + every message is signed now works correctly. + + * dns/zone.py: Zones can now be compared for equality and + inequality. If the other object in the comparison is also + a zone, then "the right thing" happens; i.e. the zones are + equal iff.: they have the same rdclass, origin, and nodes. + +2003-07-17 Bob Halley + + * dns/message.py (Message.use_tsig): The method now allows for + greater control over the various fields in the generated signature + (e.g. fudge). + (_WireReader._get_section): UnknownTSIGKey is now raised if an + unknown key is encountered, or if a signed message has no keyring. + +2003-07-16 Bob Halley + + * dns/tokenizer.py (Tokenizer._get_char): get_char and unget_char + have been renamed to _get_char and _unget_char since they are not + useful to clients of the tokenizer. + +2003-07-15 Bob Halley + + * dns/zone.py (_MasterReader._rr_line): owner names were being + unconditionally relativized; it makes much more sense for them + to be relativized according to the relativization setting of + the reader. + +2003-07-12 Bob Halley + + * dns/resolver.py (Resolver.read_resolv_conf): The resolv.conf + parser did not allow blank / whitespace-only lines, nor did it + allow comments. Both are now supported. + +2003-07-11 Bob Halley + + * dns/name.py (Name.to_digestable): to_digestable() now + requires an origin to be specified if the name is relative. + It will raise NeedAbsoluteNameOrOrigin if the name is + relative and there is either no origin or the origin is + itself relative. + (Name.split): returned the wrong answer if depth was 0 or depth + was the length of the name. split() now does bounds checking + on depth, and raises ValueError if depth < 0 or depth > the length + of the name. + +2003-07-10 Bob Halley + + * dns/ipv6.py (inet_ntoa): The routine now minimizes its output + strings. E.g. the IPv6 address + "0000:0000:0000:0000:0000:0000:0000:0001" is minimized to "::1". + We do not, however, make any effort to display embedded IPv4 + addresses in the dot-quad notation. + +2003-07-09 Bob Halley + + * dns/inet.py: We now supply our own AF_INET and AF_INET6 + constants since AF_INET6 may not always be available. If the + socket module has AF_INET6, we will use it. If not, we will + use our own value for the constant. + + * dns/query.py: the functions now take an optional af argument + specifying the address family to use when creating the socket. + + * dns/rdatatype.py (is_metatype): a typo caused the function + return true only for type OPT. + + * dns/message.py: message section list elements are now RRsets + instead of Nodes. This API change makes processing messages + easier for many applications. + +2003-07-07 Bob Halley + + * dns/rrset.py: added. An RRset is a named rdataset. + + * dns/rdataset.py (Rdataset.__eq__): rdatasets may now be compared + for equality and inequality with other objects. Rdataset instance + variables are now slotted. + + * dns/message.py: The wire format and text format readers are now + classes. Variables related to reader state have been moved out + of the message class. + +2003-07-06 Bob Halley + + * dns/name.py (from_text): '@' was not interpreted as the empty + name. + + * dns/zone.py: the master file reader derelativized names in rdata + relative to the zone's origin, not relative to the current origin. + The reader now deals with relativization in two steps. The rdata + is read and derelativized using the current origin. The rdata's + relativity is then chosen using the zone origin and the relativize + boolean. Here's an example. + + $ORIGIN foo.example. + $TTL 300 + bar MX 0 blaz + + If the zone origin is example., and relativization is on, then + This fragment will become: + + bar.foo.example. 300 IN MX 0 blaz.foo.example. + + after the first step (derelativization to current origin), and + + bar.foo 300 IN MX 0 blaz.foo + + after the second step (relativiation to zone origin). + + * dns/namedict.py: added. + + * dns/zone.py: The master file reader has been made into its + own class. Reader-related instance variables have been moved + form the zone class into the reader class. + + * dns/zone.py: Add node_factory class attribute. An application + can now subclass Zone and Node and have a zone whose nodes are of + the subclassed Node type. The from_text(), from_file(), and + from_xfr() algorithms now take an optional zone_factory argument. + This allows the algorithms to be used to create zones whose class + is a subclass of Zone. + + +2003-07-04 Bob Halley + + * dns/renderer.py: added new wire format rendering module and + converted message.py to use it. Applications which want + fine-grained control over the conversion to wire format may call + the renderer directy, instead of having it called on their behalf + by the message code. + +2003-07-02 Bob Halley + + * dns/name.py (_validate_labels): The NameTooLong test was + incorrect. + + * dns/message.py (Message.to_wire): dns.exception.TooBig is + now raised if the wire encoding exceeds the specified + maximum size. + +2003-07-01 Bob Halley + + * dns/message.py: EDNS encoding was broken. from_text() + didn't parse rcodes, flags, or eflags correctly. Comparing + messages with other types of objects didn't work. + +2003-06-30 Bob Halley + + * (Version 1.0.0 released) + +2003-06-30 Bob Halley + + * dns/rdata.py: Rdatas now implement rich comparisons instead of + __cmp__. + + * dns/name.py: Names now implement rich comparisons instead of + __cmp__. + + * dns/inet.py (inet_ntop): Always use our code, since the code + in the socket module doesn't support AF_INET6 conversions if + IPv6 sockets are not available on the system. + + * dns/resolver.py (Answer.__init__): A dangling CNAME chain was + not raising NoAnswer. + + * Added a simple resolver Cache class. + + * Added an expiration attribute to answer instances. + +2003-06-24 Bob Halley + + * (Version 1.0.0b3 released) + +2003-06-24 Bob Halley + + * Renamed module "DNS" to "dns" to avoid conflicting with + PyDNS. + +2003-06-23 Bob Halley + + * The from_text() relativization controls now work the same way as + the to_text() controls. + + * DNS/rdata.py: The parsing of generic rdata was broken. + +2003-06-21 Bob Halley + + * (Version 1.0.0b2 released) + +2003-06-21 Bob Halley + + * The Python 2.2 socket.inet_aton() doesn't seem to like + '255.255.255.255'. We work around this. + + * Fixed bugs in rdata to_wire() and from_wire() routines of a few + types. These bugs were discovered by running the tests/zone.py + Torture1 test. + + * Added implementation of type APL. + +2003-06-20 Bob Halley + + * DNS/rdtypes/IN/AAAA.py: Use our own versions of inet_ntop and + inet_pton if the socket module doesn't provide them for us. + + * The resolver now does a better job handling exceptions. In + particular, it no longer eats all exceptions; rather it handles + those exceptions it understands, and leaves the rest uncaught. + + * Exceptions have been pulled into their own module. Almost all + exceptions raised by the code are now subclasses of + DNS.exception.DNSException. All form errors are subclasses of + DNS.exception.FormError (which is itself a subclass of + DNS.exception.DNSException). + +2003-06-19 Bob Halley + + * Added implementations of types DS, NXT, SIG, and WKS. + + * __cmp__ for type A and AAAA could produce incorrect results. + +2003-06-18 Bob Halley + + * Started test suites for zone.py and tokenizer.py. + + * Added implementation of type KEY. + + * DNS/rdata.py(_base64ify): \n could be emitted erroneously. + + * DNS/rdtypes/ANY/SOA.py (SOA.from_text): The SOA RNAME field could + be set to the value of MNAME in common cases. + + * DNS/rdtypes/ANY/X25.py: __init__ was broken. + + * DNS/zone.py (from_text): $TTL handling erroneously caused the + next line to be eaten. + + * DNS/tokenizer.py (Tokenizer.get): parsing was broken for empty + quoted strings. Quoted strings didn't handle \ddd escapes. Such + escapes are appear not to comply with RFC 1035, but BIND allows + them and they seem useful, so we allow them too. + + * DNS/rdtypes/ANY/ISDN.py (ISDN.from_text): parsing was + broken for ISDN RRs without subaddresses. + + * DNS/zone.py (from_file): from_file() didn't work because + some required parameters were not passed to from_text(). + +2003-06-17 Bob Halley + + * (Version 1.0.0b1 released) + +2003-06-17 Bob Halley + + * Added implementation of type PX. + +2003-06-16 Bob Halley + + * Added implementation of types CERT, GPOS, LOC, NSAP, NSAP-PTR. + + * DNS/rdatatype.py (_by_value): A cut-and-paste error had broken + NSAP and NSAP-PTR. + +2003-06-12 Bob Halley + + * Created a tests directory and started adding tests. + + * Added "and its documentation" to the permission grant in the + license. + +2003-06-12 Bob Halley + + * DNS/name.py (Name.is_wild): is_wild() erroneously raised IndexError + if the name was empty. + +2003-06-10 Bob Halley + + * Added implementations of types AFSDB, X25, and ISDN. + + * The documentation associated with the various rdata types has been + improved. In particular, instance variables are now described. + +2003-06-09 Bob Halley + + * Added implementations of types HINFO, RP, and RT. + + * DNS/message.py (make_query): Document that make_query() sets + flags to DNS.flags.RD, and chooses a random query id. + +2003-06-05 Bob Halley + + * (Version 1.0.0a2 released) + +2003-06-05 Bob Halley + + * DNS/node.py: removed __getitem__ and __setitem__, since + they are not used by the codebase and were not useful in + general either. + + * DNS/message.py (from_file): from_file() now allows a + filename to be specified instead of a file object. + + * DNS/rdataset.py: The is_compatible() method of the + DNS.rdataset.Rdataset class was deleted. + +2003-06-04 Bob Halley + + * DNS/name.py (class Name): Names are now immutable. + + * DNS/name.py: the is_comparable() method has been removed, since + names are always comparable. + + * DNS/resolver.py (Resolver.query): A query could run for up + to the lifetime + the timeout. This has been corrected and the + query will now only run up to the lifetime. + +2003-06-03 Bob Halley + + * DNS/resolver.py: removed the 'new' function since it is not the + style of the library to have such a function. Call + DNS.resolver.Resolver() to make a new resolver. + +2003-06-03 Bob Halley + + * DNS/resolver.py (Resolver._config_win32_fromkey): The DhcpServer + list is space separated, not comma separated. + +2003-06-03 Bob Halley + + * DNS/update.py: Added an update module to make generating updates + easier. + +2003-06-03 Bob Halley + + * Commas were missing in some of the __all__ entries in various + __init__.py files. + +2003-05-30 Bob Halley + + * (Version 1.0.0a1 released) diff --git a/third_party/dnspython/LICENSE b/third_party/dnspython/LICENSE new file mode 100644 index 0000000000..633c18c1e7 --- /dev/null +++ b/third_party/dnspython/LICENSE @@ -0,0 +1,14 @@ +Copyright (C) 2001-2003 Nominum, Inc. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose with or without fee is hereby granted, +provided that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/third_party/dnspython/MANIFEST.in b/third_party/dnspython/MANIFEST.in new file mode 100644 index 0000000000..d58fb8ba04 --- /dev/null +++ b/third_party/dnspython/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE ChangeLog TODO +recursive-include examples *.txt *.py +recursive-include tests *.txt *.py Makefile *.good example diff --git a/third_party/dnspython/Makefile b/third_party/dnspython/Makefile new file mode 100644 index 0000000000..1f62c96c24 --- /dev/null +++ b/third_party/dnspython/Makefile @@ -0,0 +1,56 @@ +# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# $Id: Makefile,v 1.16 2004/03/19 00:17:27 halley Exp $ + +PYTHON=python + +all: + ${PYTHON} ./setup.py build + +install: + ${PYTHON} ./setup.py install + +clean: + ${PYTHON} ./setup.py clean --all + find . -name '*.pyc' -exec rm {} \; + find . -name '*.pyo' -exec rm {} \; + rm -f TAGS + +distclean: clean docclean + rm -rf build dist + rm -f MANIFEST + +doc: + epydoc -n dnspython -u http://www.dnspython.org \ + dns/*.py dns/rdtypes/*.py dns/rdtypes/ANY/*.py \ + dns/rdtypes/IN/*.py + +dockits: doc + mv html dnspython-html + tar czf html.tar.gz dnspython-html + zip -r html.zip dnspython-html + mv dnspython-html html + +docclean: + rm -rf html.tar.gz html.zip html + +kits: + ${PYTHON} ./setup.py sdist --formats=gztar,zip +# ${PYTHON} ./setup.py bdist_wininst +# ${PYTHON} ./setup.py bdist_rpm + +tags: + find . -name '*.py' -print | etags - diff --git a/third_party/dnspython/README b/third_party/dnspython/README new file mode 100644 index 0000000000..367e7a2b76 --- /dev/null +++ b/third_party/dnspython/README @@ -0,0 +1,442 @@ +dnspython + +INTRODUCTION + +dnspython is a DNS toolkit for Python. It supports almost all record +types. It can be used for queries, zone transfers, and dynamic +updates. It supports TSIG authenticated messages and EDNS0. + +dnspython provides both high and low level access to DNS. The high +level classes perform queries for data of a given name, type, and +class, and return an answer set. The low level classes allow direct +manipulation of DNS zones, messages, names, and records. + +To see a few of the ways dnspython can be used, look in the examples/ +directory. + +dnspython originated at Nominum where it was developed to facilitate +the testing of DNS software. Nominum has generously allowed it to be +open sourced under a BSD-style license, and helps support its future +development by continuing to employ the author :). + + +ABOUT THIS RELEASE + +This is dnspython 1.10.0 + +New since 1.9.4: + + XXX TBS. + +Bugs fixed since 1.9.4: + + XXX TBS. + +New since 1.9.3: + + Nothing. + +Bugs fixed since 1.9.3: + + The rdata _wire_cmp() routine now handles relative names. + + The SIG RR implementation was missing 'import struct'. + +New since 1.9.2: + + A boolean parameter, 'raise_on_no_answer', has been added to + the query() methods. In no-error, no-data situations, this + parameter determines whether NoAnswer should be raised or not. + If True, NoAnswer is raised. If False, then an Answer() + object with a None rrset will be returned. + + Resolver Answer() objects now have a canonical_name field. + + Rdata now have a __hash__ method. + +Bugs fixed since 1.9.2: + + Dnspython was erroneously doing case-insensitive comparisons + of the names in NSEC and RRSIG RRs. + + We now use "is" and not "==" when testing what section an RR + is in. + + The resolver now disallows metaqueries. + +New since 1.9.1: + + Nothing. + +Bugs fixed since 1.9.1: + + The dns.dnssec module didn't work at all due to missing + imports that escaped detection in testing because the test + suite also did the imports. The third time is the charm! + +New since 1.9.0: + + Nothing. + +Bugs fixed since 1.9.0: + + The dns.dnssec module didn't work with DSA due to namespace + contamination from a "from"-style import. + +New since 1.8.0: + + dnspython now uses poll() instead of select() when available. + + Basic DNSSEC validation can be done using dns.dnsec.validate() + and dns.dnssec.validate_rrsig() if you have PyCrypto 2.3 or + later installed. Complete secure resolution is not yet + available. + + Added key_id() to the DNSSEC module, which computes the DNSSEC + key id of a DNSKEY rdata. + + Added make_ds() to the DNSSEC module, which returns the DS RR + for a given DNSKEY rdata. + + dnspython now raises an exception if HMAC-SHA284 or + HMAC-SHA512 are used with a Python older than 2.5.2. (Older + Pythons do not compute the correct value.) + + Symbolic constants are now available for TSIG algorithm names. + +Bugs fixed since 1.8.0 + + dns.resolver.zone_for_name() didn't handle a query response + with a CNAME or DNAME correctly in some cases. + + When specifying rdata types and classes as text, Unicode + strings may now be used. + + Hashlib compatibility issues have been fixed. + + dns.message now imports dns.edns. + + The TSIG algorithm value was passed incorrectly to use_tsig() + in some cases. + +New since 1.7.1: + + Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384 + and hmac-sha512 has been contributed by Kevin Chen. + + The tokenizer's tokens are now Token objects instead of (type, + value) tuples. + +Bugs fixed since 1.7.1: + + Escapes in masterfiles now work correctly. Previously they + were only working correctly when the text involved was part of + a domain name. + + When constructing a DDNS update, if the present() method was + used with a single rdata, a zero TTL was not added. + + The entropy pool needed locking to be thread safe. + + The entropy pool's reading of /dev/random could cause + dnspython to block. + + The entropy pool did buffered reads, potentially consuming more + randomness than we needed. + + The entropy pool did not seed with high quality randomness on + Windows. + + SRV records were compared incorrectly. + + In the e164 query function, the resolver parameter was not + used. + +New since 1.7.0: + + Nothing + +Bugs fixed since 1.7.0: + + The 1.7.0 kitting process inadventently omitted the code for the + DLV RR. + + Negative DDNS prerequisites are now handled correctly. + +New since 1.6.0: + + Rdatas now have a to_digestable() method, which returns the + DNSSEC canonical form of the rdata, suitable for use in + signature computations. + + The NSEC3, NSEC3PARAM, DLV, and HIP RR types are now supported. + + An entropy module has been added and is used to randomize query ids. + + EDNS0 options are now supported. + + UDP IXFR is now supported. + + The wire format parser now has a 'one_rr_per_rrset' mode, which + suppresses the usual coalescing of all RRs of a given type into a + single RRset. + + Various helpful DNSSEC-related constants are now defined. + + The resolver's query() method now has an optional 'source' parameter, + allowing the source IP address to be specified. + +Bugs fixed since 1.6.0: + + On Windows, the resolver set the domain incorrectly. + + DS RR parsing only allowed one Base64 chunk. + + TSIG validation didn't always use absolute names. + + NSEC.to_text() only printed the last window. + + We did not canonicalize IPv6 addresses before comparing them; we + would thus treat equivalent but different textual forms, e.g. + "1:00::1" and "1::1" as being non-equivalent. + + If the peer set a TSIG error, we didn't raise an exception. + + Some EDNS bugs in the message code have been fixed (see the ChangeLog + for details). + +New since 1.5.0: + Added dns.inet.is_multicast(). + +Bugs fixed since 1.5.0: + + If select() raises an exception due to EINTR, we should just + select() again. + + If the queried address is a multicast address, then don't + check that the address of the response is the same as the + address queried. + + NAPTR comparisons didn't compare the preference field due to a + typo. + + Testing of whether a Windows NIC is enabled now works on Vista + thanks to code contributed by Paul Marks. + +New since 1.4.0: + + Answer objects now support more of the python sequence + protocol, forwarding the requests to the answer rrset. + E.g. "for a in answer" is equivalent to "for a in + answer.rrset", "answer[i]" is equivalent to "answer.rrset[i]", + and "answer[i:j]" is equivalent to "answer.rrset[i:j]". + + Making requests using EDNS, including indicating DNSSEC awareness, + is now easier. For example, you can now say: + + q = dns.message.make_query('www.dnspython.org', 'MX', + want_dnssec=True) + + dns.query.xfr() can now be used for IXFR. + + Support has been added for the DHCID, IPSECKEY, and SPF RR types. + + UDP messages from unexpected sources can now be ignored by + setting ignore_unexpected to True when calling dns.query.udp. + +Bugs fixed since 1.4.0: + + If /etc/resolv.conf didn't exist, we raised an exception + instead of simply using the default resolver configuration. + + In dns.resolver.Resolver._config_win32_fromkey(), we were + passing the wrong variable to self._config_win32_search(). + +New since 1.3.5: + + You can now convert E.164 numbers to/from their ENUM name + forms: + + >>> import dns.e164 + >>> n = dns.e164.from_e164("+1 555 1212") + >>> n + + >>> dns.e164.to_e164(n) + '+15551212' + + You can now convert IPv4 and IPv6 address to/from their + corresponding DNS reverse map names: + + >>> import dns.reversename + >>> n = dns.reversename.from_address("127.0.0.1") + >>> n + + >>> dns.reversename.to_address(n) + '127.0.0.1' + + You can now convert between Unicode strings and their IDN ACE + form: + + >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.') + >>> n + + >>> n.to_unicode() + u'les-\xe9l\xe8ves.example.' + + The origin parameter to dns.zone.from_text() and dns.zone.to_text() + is now optional. If not specified, the origin will be taken from + the first $ORIGIN statement in the master file. + + Sanity checking of a zone can be disabled; this is useful when + working with files which are zone fragments. + +Bugs fixed since 1.3.5: + + The correct delimiter was not used when retrieving the + list of nameservers from the registry in certain versions of + windows. + + The floating-point version of latitude and longitude in LOC RRs + (float_latitude and float_longitude) had incorrect signs for + south latitudes and west longitudes. + + BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. + SOA fields refresh, retry, expire, and minimum; SIG/RRSIG + field original_ttl). + + TTLs are now bounds checked when their text form is parsed, + and their values must be in the closed interval [0, 2^31 - 1]. + +New since 1.3.4: + + In the resolver, if time goes backward a little bit, ignore + it. + + zone_for_name() has been added to the resolver module. It + returns the zone which is authoritative for the specified + name, which is handy for dynamic update. E.g. + + import dns.resolver + print dns.resolver.zone_for_name('www.dnspython.org') + + will output "dnspython.org." and + + print dns.resolver.zone_for_name('a.b.c.d.e.f.example.') + + will output ".". + + The default resolver can be fetched with the + get_default_resolver() method. + + You can now get the parent (immediate superdomain) of a name + by using the parent() method. + + Zone.iterate_rdatasets() and Zone.iterate_rdatas() now have + a default rdtype of dns.rdatatype.ANY like the documentation + says. + + A Dynamic DNS example, ddns.py, has been added. + +New since 1.3.3: + + The source address and port may now be specified when calling + dns.query.{udp,tcp,xfr}. + + The resolver now does exponential backoff each time it runs + through all of the nameservers. + + Rcodes which indicate a nameserver is likely to be a + "permanent failure" for a query cause the nameserver to be removed + from the mix for that query. + +New since 1.3.2: + + dns.message.Message.find_rrset() now uses an index, vastly + improving the from_wire() performance of large messages such + as zone transfers. + + Added dns.message.make_response(), which creates a skeletal + response for the specified query. + + Added opcode() and set_opcode() convenience methods to the + dns.message.Message class. Added the request_payload + attribute to the Message class. + + The 'file' parameter of dns.name.Name.to_wire() is now + optional; if omitted, the wire form will be returned as the + value of the function. + + dns.zone.from_xfr() in relativization mode incorrectly set + zone.origin to the empty name. + + The masterfile parser incorrectly rejected TXT records where a + value was not quoted. + +New since 1.3.1: + + The NSEC format doesn't allow specifying types by number, so + we shouldn't either. (Using the unknown type format is still + OK though.) + + The resolver wasn't catching dns.exception.Timeout, so a timeout + erroneously caused the whole resolution to fail instead of just + going on to the next server. + + The renderer module didn't import random, causing an exception + to be raised if a query id wasn't provided when a Renderer was + created. + + The conversion of LOC milliseconds values from text to binary was + incorrect if the length of the milliseconds string was not 3. + +New since 1.3.0: + + Added support for the SSHFP type. + +New since 1.2.0: + + Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. + +This release fixes all known bugs. + +See the ChangeLog file for more detailed information on changes since +the prior release. + + +REQUIREMENTS + +Python 2.4 or later. + + +INSTALLATION + +To build and install dnspython, type + + python setup.py install + + +HOME PAGE + +For the latest in releases, documentation, and information, visit the +dnspython home page at + + http://www.dnspython.org/ + + + +DOCUMENTATION + +Documentation is sparse at the moment. Use pydoc, or read the HTML +documentation at the dnspython home page, or download the HTML +documentation. + + +BUG REPORTS + +Bug reports may be sent to bugs@dnspython.org + + +MAILING LISTS + +A number of mailing lists are available. Visit the dnspython home +page to subscribe or unsubscribe. diff --git a/third_party/dnspython/TODO b/third_party/dnspython/TODO new file mode 100644 index 0000000000..59ce1be1ce --- /dev/null +++ b/third_party/dnspython/TODO @@ -0,0 +1,17 @@ +Tutorial documentation + +More examples + +It would be nice to have a tokenizer that used regular expressions +because it would be faster. + +Teach the resolver about DNAME (right now it relies on the server adding +synthesized CNAMEs) + +Add TKEY support. + +TSIG works, but needs cleaning up -- probably better encapsulation of +TSIG state to make things much simpler and easier to use. + +Pickling support. + diff --git a/third_party/dnspython/dns/__init__.py b/third_party/dnspython/dns/__init__.py new file mode 100644 index 0000000000..c848e48581 --- /dev/null +++ b/third_party/dnspython/dns/__init__.py @@ -0,0 +1,54 @@ +# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""dnspython DNS toolkit""" + +__all__ = [ + 'dnssec', + 'e164', + 'edns', + 'entropy', + 'exception', + 'flags', + 'hash', + 'inet', + 'ipv4', + 'ipv6', + 'message', + 'name', + 'namedict', + 'node', + 'opcode', + 'query', + 'rcode', + 'rdata', + 'rdataclass', + 'rdataset', + 'rdatatype', + 'renderer', + 'resolver', + 'reversename', + 'rrset', + 'set', + 'tokenizer', + 'tsig', + 'tsigkeyring', + 'ttl', + 'rdtypes', + 'update', + 'version', + 'wiredata', + 'zone', +] diff --git a/third_party/dnspython/dns/dnssec.py b/third_party/dnspython/dns/dnssec.py new file mode 100644 index 0000000000..dd6a27a536 --- /dev/null +++ b/third_party/dnspython/dns/dnssec.py @@ -0,0 +1,372 @@ +# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Common DNSSEC-related functions and constants.""" + +import cStringIO +import struct +import time + +import dns.exception +import dns.hash +import dns.name +import dns.node +import dns.rdataset +import dns.rdata +import dns.rdatatype +import dns.rdataclass + +class UnsupportedAlgorithm(dns.exception.DNSException): + """Raised if an algorithm is not supported.""" + pass + +class ValidationFailure(dns.exception.DNSException): + """The DNSSEC signature is invalid.""" + pass + +RSAMD5 = 1 +DH = 2 +DSA = 3 +ECC = 4 +RSASHA1 = 5 +DSANSEC3SHA1 = 6 +RSASHA1NSEC3SHA1 = 7 +RSASHA256 = 8 +RSASHA512 = 10 +INDIRECT = 252 +PRIVATEDNS = 253 +PRIVATEOID = 254 + +_algorithm_by_text = { + 'RSAMD5' : RSAMD5, + 'DH' : DH, + 'DSA' : DSA, + 'ECC' : ECC, + 'RSASHA1' : RSASHA1, + 'DSANSEC3SHA1' : DSANSEC3SHA1, + 'RSASHA1NSEC3SHA1' : RSASHA1NSEC3SHA1, + 'RSASHA256' : RSASHA256, + 'RSASHA512' : RSASHA512, + 'INDIRECT' : INDIRECT, + 'PRIVATEDNS' : PRIVATEDNS, + 'PRIVATEOID' : PRIVATEOID, + } + +# We construct the inverse mapping programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mapping not to be true inverse. + +_algorithm_by_value = dict([(y, x) for x, y in _algorithm_by_text.iteritems()]) + +def algorithm_from_text(text): + """Convert text into a DNSSEC algorithm value + @rtype: int""" + + value = _algorithm_by_text.get(text.upper()) + if value is None: + value = int(text) + return value + +def algorithm_to_text(value): + """Convert a DNSSEC algorithm value to text + @rtype: string""" + + text = _algorithm_by_value.get(value) + if text is None: + text = str(value) + return text + +def _to_rdata(record, origin): + s = cStringIO.StringIO() + record.to_wire(s, origin=origin) + return s.getvalue() + +def key_id(key, origin=None): + rdata = _to_rdata(key, origin) + if key.algorithm == RSAMD5: + return (ord(rdata[-3]) << 8) + ord(rdata[-2]) + else: + total = 0 + for i in range(len(rdata) // 2): + total += (ord(rdata[2 * i]) << 8) + ord(rdata[2 * i + 1]) + if len(rdata) % 2 != 0: + total += ord(rdata[len(rdata) - 1]) << 8 + total += ((total >> 16) & 0xffff); + return total & 0xffff + +def make_ds(name, key, algorithm, origin=None): + if algorithm.upper() == 'SHA1': + dsalg = 1 + hash = dns.hash.get('SHA1')() + elif algorithm.upper() == 'SHA256': + dsalg = 2 + hash = dns.hash.get('SHA256')() + else: + raise UnsupportedAlgorithm, 'unsupported algorithm "%s"' % algorithm + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, origin) + hash.update(name.canonicalize().to_wire()) + hash.update(_to_rdata(key, origin)) + digest = hash.digest() + + dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest + return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, + len(dsrdata)) + +def _find_key(keys, rrsig): + value = keys.get(rrsig.signer) + if value is None: + return None + if isinstance(value, dns.node.Node): + try: + rdataset = node.find_rdataset(dns.rdataclass.IN, + dns.rdatatype.DNSKEY) + except KeyError: + return None + else: + rdataset = value + for rdata in rdataset: + if rdata.algorithm == rrsig.algorithm and \ + key_id(rdata) == rrsig.key_tag: + return rdata + return None + +def _is_rsa(algorithm): + return algorithm in (RSAMD5, RSASHA1, + RSASHA1NSEC3SHA1, RSASHA256, + RSASHA512) + +def _is_dsa(algorithm): + return algorithm in (DSA, DSANSEC3SHA1) + +def _is_md5(algorithm): + return algorithm == RSAMD5 + +def _is_sha1(algorithm): + return algorithm in (DSA, RSASHA1, + DSANSEC3SHA1, RSASHA1NSEC3SHA1) + +def _is_sha256(algorithm): + return algorithm == RSASHA256 + +def _is_sha512(algorithm): + return algorithm == RSASHA512 + +def _make_hash(algorithm): + if _is_md5(algorithm): + return dns.hash.get('MD5')() + if _is_sha1(algorithm): + return dns.hash.get('SHA1')() + if _is_sha256(algorithm): + return dns.hash.get('SHA256')() + if _is_sha512(algorithm): + return dns.hash.get('SHA512')() + raise ValidationFailure, 'unknown hash for algorithm %u' % algorithm + +def _make_algorithm_id(algorithm): + if _is_md5(algorithm): + oid = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05] + elif _is_sha1(algorithm): + oid = [0x2b, 0x0e, 0x03, 0x02, 0x1a] + elif _is_sha256(algorithm): + oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01] + elif _is_sha512(algorithm): + oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03] + else: + raise ValidationFailure, 'unknown algorithm %u' % algorithm + olen = len(oid) + dlen = _make_hash(algorithm).digest_size + idbytes = [0x30] + [8 + olen + dlen] + \ + [0x30, olen + 4] + [0x06, olen] + oid + \ + [0x05, 0x00] + [0x04, dlen] + return ''.join(map(chr, idbytes)) + +def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): + """Validate an RRset against a single signature rdata + + The owner name of the rrsig is assumed to be the same as the owner name + of the rrset. + + @param rrset: The RRset to validate + @type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) + tuple + @param rrsig: The signature rdata + @type rrsig: dns.rrset.Rdata + @param keys: The key dictionary. + @type keys: a dictionary keyed by dns.name.Name with node or rdataset values + @param origin: The origin to use for relative names + @type origin: dns.name.Name or None + @param now: The time to use when validating the signatures. The default + is the current time. + @type now: int + """ + + if isinstance(origin, (str, unicode)): + origin = dns.name.from_text(origin, dns.name.root) + + key = _find_key(keys, rrsig) + if not key: + raise ValidationFailure, 'unknown key' + + # For convenience, allow the rrset to be specified as a (name, rdataset) + # tuple as well as a proper rrset + if isinstance(rrset, tuple): + rrname = rrset[0] + rdataset = rrset[1] + else: + rrname = rrset.name + rdataset = rrset + + if now is None: + now = time.time() + if rrsig.expiration < now: + raise ValidationFailure, 'expired' + if rrsig.inception > now: + raise ValidationFailure, 'not yet valid' + + hash = _make_hash(rrsig.algorithm) + + if _is_rsa(rrsig.algorithm): + keyptr = key.key + (bytes,) = struct.unpack('!B', keyptr[0:1]) + keyptr = keyptr[1:] + if bytes == 0: + (bytes,) = struct.unpack('!H', keyptr[0:2]) + keyptr = keyptr[2:] + rsa_e = keyptr[0:bytes] + rsa_n = keyptr[bytes:] + keylen = len(rsa_n) * 8 + pubkey = Crypto.PublicKey.RSA.construct( + (Crypto.Util.number.bytes_to_long(rsa_n), + Crypto.Util.number.bytes_to_long(rsa_e))) + sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),) + elif _is_dsa(rrsig.algorithm): + keyptr = key.key + (t,) = struct.unpack('!B', keyptr[0:1]) + keyptr = keyptr[1:] + octets = 64 + t * 8 + dsa_q = keyptr[0:20] + keyptr = keyptr[20:] + dsa_p = keyptr[0:octets] + keyptr = keyptr[octets:] + dsa_g = keyptr[0:octets] + keyptr = keyptr[octets:] + dsa_y = keyptr[0:octets] + pubkey = Crypto.PublicKey.DSA.construct( + (Crypto.Util.number.bytes_to_long(dsa_y), + Crypto.Util.number.bytes_to_long(dsa_g), + Crypto.Util.number.bytes_to_long(dsa_p), + Crypto.Util.number.bytes_to_long(dsa_q))) + (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:]) + sig = (Crypto.Util.number.bytes_to_long(dsa_r), + Crypto.Util.number.bytes_to_long(dsa_s)) + else: + raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm + + hash.update(_to_rdata(rrsig, origin)[:18]) + hash.update(rrsig.signer.to_digestable(origin)) + + if rrsig.labels < len(rrname) - 1: + suffix = rrname.split(rrsig.labels + 1)[1] + rrname = dns.name.from_text('*', suffix) + rrnamebuf = rrname.to_digestable(origin) + rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, + rrsig.original_ttl) + rrlist = sorted(rdataset); + for rr in rrlist: + hash.update(rrnamebuf) + hash.update(rrfixed) + rrdata = rr.to_digestable(origin) + rrlen = struct.pack('!H', len(rrdata)) + hash.update(rrlen) + hash.update(rrdata) + + digest = hash.digest() + + if _is_rsa(rrsig.algorithm): + # PKCS1 algorithm identifier goop + digest = _make_algorithm_id(rrsig.algorithm) + digest + padlen = keylen // 8 - len(digest) - 3 + digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest + elif _is_dsa(rrsig.algorithm): + pass + else: + # Raise here for code clarity; this won't actually ever happen + # since if the algorithm is really unknown we'd already have + # raised an exception above + raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm + + if not pubkey.verify(digest, sig): + raise ValidationFailure, 'verify failure' + +def _validate(rrset, rrsigset, keys, origin=None, now=None): + """Validate an RRset + + @param rrset: The RRset to validate + @type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) + tuple + @param rrsigset: The signature RRset + @type rrsigset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) + tuple + @param keys: The key dictionary. + @type keys: a dictionary keyed by dns.name.Name with node or rdataset values + @param origin: The origin to use for relative names + @type origin: dns.name.Name or None + @param now: The time to use when validating the signatures. The default + is the current time. + @type now: int + """ + + if isinstance(origin, (str, unicode)): + origin = dns.name.from_text(origin, dns.name.root) + + if isinstance(rrset, tuple): + rrname = rrset[0] + else: + rrname = rrset.name + + if isinstance(rrsigset, tuple): + rrsigname = rrsigset[0] + rrsigrdataset = rrsigset[1] + else: + rrsigname = rrsigset.name + rrsigrdataset = rrsigset + + rrname = rrname.choose_relativity(origin) + rrsigname = rrname.choose_relativity(origin) + if rrname != rrsigname: + raise ValidationFailure, "owner names do not match" + + for rrsig in rrsigrdataset: + try: + _validate_rrsig(rrset, rrsig, keys, origin, now) + return + except ValidationFailure, e: + pass + raise ValidationFailure, "no RRSIGs validated" + +def _need_pycrypto(*args, **kwargs): + raise NotImplementedError, "DNSSEC validation requires pycrypto" + +try: + import Crypto.PublicKey.RSA + import Crypto.PublicKey.DSA + import Crypto.Util.number + validate = _validate + validate_rrsig = _validate_rrsig +except ImportError: + validate = _need_pycrypto + validate_rrsig = _need_pycrypto diff --git a/third_party/dnspython/dns/e164.py b/third_party/dnspython/dns/e164.py new file mode 100644 index 0000000000..d6dcd1b186 --- /dev/null +++ b/third_party/dnspython/dns/e164.py @@ -0,0 +1,79 @@ +# Copyright (C) 2006, 2007, 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS E.164 helpers + +@var public_enum_domain: The DNS public ENUM domain, e164.arpa. +@type public_enum_domain: dns.name.Name object +""" + +import dns.exception +import dns.name +import dns.resolver + +public_enum_domain = dns.name.from_text('e164.arpa.') + +def from_e164(text, origin=public_enum_domain): + """Convert an E.164 number in textual form into a Name object whose + value is the ENUM domain name for that number. + @param text: an E.164 number in textual form. + @type text: str + @param origin: The domain in which the number should be constructed. + The default is e164.arpa. + @type: dns.name.Name object or None + @rtype: dns.name.Name object + """ + parts = [d for d in text if d.isdigit()] + parts.reverse() + return dns.name.from_text('.'.join(parts), origin=origin) + +def to_e164(name, origin=public_enum_domain, want_plus_prefix=True): + """Convert an ENUM domain name into an E.164 number. + @param name: the ENUM domain name. + @type name: dns.name.Name object. + @param origin: A domain containing the ENUM domain name. The + name is relativized to this domain before being converted to text. + @type: dns.name.Name object or None + @param want_plus_prefix: if True, add a '+' to the beginning of the + returned number. + @rtype: str + """ + if not origin is None: + name = name.relativize(origin) + dlabels = [d for d in name.labels if (d.isdigit() and len(d) == 1)] + if len(dlabels) != len(name.labels): + raise dns.exception.SyntaxError('non-digit labels in ENUM domain name') + dlabels.reverse() + text = ''.join(dlabels) + if want_plus_prefix: + text = '+' + text + return text + +def query(number, domains, resolver=None): + """Look for NAPTR RRs for the specified number in the specified domains. + + e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) + """ + if resolver is None: + resolver = dns.resolver.get_default_resolver() + for domain in domains: + if isinstance(domain, (str, unicode)): + domain = dns.name.from_text(domain) + qname = dns.e164.from_e164(number, domain) + try: + return resolver.query(qname, 'NAPTR') + except dns.resolver.NXDOMAIN: + pass + raise dns.resolver.NXDOMAIN diff --git a/third_party/dnspython/dns/edns.py b/third_party/dnspython/dns/edns.py new file mode 100644 index 0000000000..f8b6009d3f --- /dev/null +++ b/third_party/dnspython/dns/edns.py @@ -0,0 +1,142 @@ +# Copyright (C) 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""EDNS Options""" + +NSID = 3 + +class Option(object): + """Base class for all EDNS option types. + """ + + def __init__(self, otype): + """Initialize an option. + @param rdtype: The rdata type + @type rdtype: int + """ + self.otype = otype + + def to_wire(self, file): + """Convert an option to wire format. + """ + raise NotImplementedError + + def from_wire(cls, otype, wire, current, olen): + """Build an EDNS option object from wire format + + @param otype: The option type + @type otype: int + @param wire: The wire-format message + @type wire: string + @param current: The offet in wire of the beginning of the rdata. + @type current: int + @param olen: The length of the wire-format option data + @type olen: int + @rtype: dns.ends.Option instance""" + raise NotImplementedError + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + """Compare an ENDS option with another option of the same type. + Return < 0 if self < other, 0 if self == other, and > 0 if self > other. + """ + raise NotImplementedError + + def __eq__(self, other): + if not isinstance(other, Option): + return False + if self.otype != other.otype: + return False + return self._cmp(other) == 0 + + def __ne__(self, other): + if not isinstance(other, Option): + return False + if self.otype != other.otype: + return False + return self._cmp(other) != 0 + + def __lt__(self, other): + if not isinstance(other, Option) or \ + self.otype != other.otype: + return NotImplemented + return self._cmp(other) < 0 + + def __le__(self, other): + if not isinstance(other, Option) or \ + self.otype != other.otype: + return NotImplemented + return self._cmp(other) <= 0 + + def __ge__(self, other): + if not isinstance(other, Option) or \ + self.otype != other.otype: + return NotImplemented + return self._cmp(other) >= 0 + + def __gt__(self, other): + if not isinstance(other, Option) or \ + self.otype != other.otype: + return NotImplemented + return self._cmp(other) > 0 + + +class GenericOption(Option): + """Generate Rdata Class + + This class is used for EDNS option types for which we have no better + implementation. + """ + + def __init__(self, otype, data): + super(GenericOption, self).__init__(otype) + self.data = data + + def to_wire(self, file): + file.write(self.data) + + def from_wire(cls, otype, wire, current, olen): + return cls(otype, wire[current : current + olen]) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + return cmp(self.data, other.data) + +_type_to_class = { +} + +def get_option_class(otype): + cls = _type_to_class.get(otype) + if cls is None: + cls = GenericOption + return cls + +def option_from_wire(otype, wire, current, olen): + """Build an EDNS option object from wire format + + @param otype: The option type + @type otype: int + @param wire: The wire-format message + @type wire: string + @param current: The offet in wire of the beginning of the rdata. + @type current: int + @param olen: The length of the wire-format option data + @type olen: int + @rtype: dns.ends.Option instance""" + + cls = get_option_class(otype) + return cls.from_wire(otype, wire, current, olen) diff --git a/third_party/dnspython/dns/entropy.py b/third_party/dnspython/dns/entropy.py new file mode 100644 index 0000000000..d380cf8c0d --- /dev/null +++ b/third_party/dnspython/dns/entropy.py @@ -0,0 +1,123 @@ +# Copyright (C) 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import os +import time +try: + import threading as _threading +except ImportError: + import dummy_threading as _threading + +class EntropyPool(object): + def __init__(self, seed=None): + self.pool_index = 0 + self.digest = None + self.next_byte = 0 + self.lock = _threading.Lock() + try: + import hashlib + self.hash = hashlib.sha1() + self.hash_len = 20 + except: + try: + import sha + self.hash = sha.new() + self.hash_len = 20 + except: + import md5 + self.hash = md5.new() + self.hash_len = 16 + self.pool = '\0' * self.hash_len + if not seed is None: + self.stir(seed) + self.seeded = True + else: + self.seeded = False + + def stir(self, entropy, already_locked=False): + if not already_locked: + self.lock.acquire() + try: + bytes = [ord(c) for c in self.pool] + for c in entropy: + if self.pool_index == self.hash_len: + self.pool_index = 0 + b = ord(c) & 0xff + bytes[self.pool_index] ^= b + self.pool_index += 1 + self.pool = ''.join([chr(c) for c in bytes]) + finally: + if not already_locked: + self.lock.release() + + def _maybe_seed(self): + if not self.seeded: + try: + seed = os.urandom(16) + except: + try: + r = file('/dev/urandom', 'r', 0) + try: + seed = r.read(16) + finally: + r.close() + except: + seed = str(time.time()) + self.seeded = True + self.stir(seed, True) + + def random_8(self): + self.lock.acquire() + self._maybe_seed() + try: + if self.digest is None or self.next_byte == self.hash_len: + self.hash.update(self.pool) + self.digest = self.hash.digest() + self.stir(self.digest, True) + self.next_byte = 0 + value = ord(self.digest[self.next_byte]) + self.next_byte += 1 + finally: + self.lock.release() + return value + + def random_16(self): + return self.random_8() * 256 + self.random_8() + + def random_32(self): + return self.random_16() * 65536 + self.random_16() + + def random_between(self, first, last): + size = last - first + 1 + if size > 4294967296L: + raise ValueError('too big') + if size > 65536: + rand = self.random_32 + max = 4294967295L + elif size > 256: + rand = self.random_16 + max = 65535 + else: + rand = self.random_8 + max = 255 + return (first + size * rand() // (max + 1)) + +pool = EntropyPool() + +def random_16(): + return pool.random_16() + +def between(first, last): + return pool.random_between(first, last) diff --git a/third_party/dnspython/dns/exception.py b/third_party/dnspython/dns/exception.py new file mode 100644 index 0000000000..db6ef6e6e3 --- /dev/null +++ b/third_party/dnspython/dns/exception.py @@ -0,0 +1,40 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Common DNS Exceptions.""" + +class DNSException(Exception): + """Abstract base class shared by all dnspython exceptions.""" + pass + +class FormError(DNSException): + """DNS message is malformed.""" + pass + +class SyntaxError(DNSException): + """Text input is malformed.""" + pass + +class UnexpectedEnd(SyntaxError): + """Raised if text input ends unexpectedly.""" + pass + +class TooBig(DNSException): + """The message is too big.""" + pass + +class Timeout(DNSException): + """The operation timed out.""" + pass diff --git a/third_party/dnspython/dns/flags.py b/third_party/dnspython/dns/flags.py new file mode 100644 index 0000000000..35a8305e80 --- /dev/null +++ b/third_party/dnspython/dns/flags.py @@ -0,0 +1,106 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Message Flags.""" + +# Standard DNS flags + +QR = 0x8000 +AA = 0x0400 +TC = 0x0200 +RD = 0x0100 +RA = 0x0080 +AD = 0x0020 +CD = 0x0010 + +# EDNS flags + +DO = 0x8000 + +_by_text = { + 'QR' : QR, + 'AA' : AA, + 'TC' : TC, + 'RD' : RD, + 'RA' : RA, + 'AD' : AD, + 'CD' : CD +} + +_edns_by_text = { + 'DO' : DO +} + + +# We construct the inverse mappings programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mappings not to be true inverses. + +_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) + +_edns_by_value = dict([(y, x) for x, y in _edns_by_text.iteritems()]) + +def _order_flags(table): + order = list(table.iteritems()) + order.sort() + order.reverse() + return order + +_flags_order = _order_flags(_by_value) + +_edns_flags_order = _order_flags(_edns_by_value) + +def _from_text(text, table): + flags = 0 + tokens = text.split() + for t in tokens: + flags = flags | table[t.upper()] + return flags + +def _to_text(flags, table, order): + text_flags = [] + for k, v in order: + if flags & k != 0: + text_flags.append(v) + return ' '.join(text_flags) + +def from_text(text): + """Convert a space-separated list of flag text values into a flags + value. + @rtype: int""" + + return _from_text(text, _by_text) + +def to_text(flags): + """Convert a flags value into a space-separated list of flag text + values. + @rtype: string""" + + return _to_text(flags, _by_value, _flags_order) + + +def edns_from_text(text): + """Convert a space-separated list of EDNS flag text values into a EDNS + flags value. + @rtype: int""" + + return _from_text(text, _edns_by_text) + +def edns_to_text(flags): + """Convert an EDNS flags value into a space-separated list of EDNS flag + text values. + @rtype: string""" + + return _to_text(flags, _edns_by_value, _edns_flags_order) diff --git a/third_party/dnspython/dns/hash.py b/third_party/dnspython/dns/hash.py new file mode 100644 index 0000000000..0c708036b1 --- /dev/null +++ b/third_party/dnspython/dns/hash.py @@ -0,0 +1,67 @@ +# Copyright (C) 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Hashing backwards compatibility wrapper""" + +import sys + +_hashes = None + +def _need_later_python(alg): + def func(*args, **kwargs): + raise NotImplementedError("TSIG algorithm " + alg + + " requires Python 2.5.2 or later") + return func + +def _setup(): + global _hashes + _hashes = {} + try: + import hashlib + _hashes['MD5'] = hashlib.md5 + _hashes['SHA1'] = hashlib.sha1 + _hashes['SHA224'] = hashlib.sha224 + _hashes['SHA256'] = hashlib.sha256 + if sys.hexversion >= 0x02050200: + _hashes['SHA384'] = hashlib.sha384 + _hashes['SHA512'] = hashlib.sha512 + else: + _hashes['SHA384'] = _need_later_python('SHA384') + _hashes['SHA512'] = _need_later_python('SHA512') + + if sys.hexversion < 0x02050000: + # hashlib doesn't conform to PEP 247: API for + # Cryptographic Hash Functions, which hmac before python + # 2.5 requires, so add the necessary items. + class HashlibWrapper: + def __init__(self, basehash): + self.basehash = basehash + self.digest_size = self.basehash().digest_size + + def new(self, *args, **kwargs): + return self.basehash(*args, **kwargs) + + for name in _hashes: + _hashes[name] = HashlibWrapper(_hashes[name]) + + except ImportError: + import md5, sha + _hashes['MD5'] = md5 + _hashes['SHA1'] = sha + +def get(algorithm): + if _hashes is None: + _setup() + return _hashes[algorithm.upper()] diff --git a/third_party/dnspython/dns/inet.py b/third_party/dnspython/dns/inet.py new file mode 100644 index 0000000000..3b7e88f7b1 --- /dev/null +++ b/third_party/dnspython/dns/inet.py @@ -0,0 +1,108 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Generic Internet address helper functions.""" + +import socket + +import dns.ipv4 +import dns.ipv6 + + +# We assume that AF_INET is always defined. + +AF_INET = socket.AF_INET + +# AF_INET6 might not be defined in the socket module, but we need it. +# We'll try to use the socket module's value, and if it doesn't work, +# we'll use our own value. + +try: + AF_INET6 = socket.AF_INET6 +except AttributeError: + AF_INET6 = 9999 + +def inet_pton(family, text): + """Convert the textual form of a network address into its binary form. + + @param family: the address family + @type family: int + @param text: the textual address + @type text: string + @raises NotImplementedError: the address family specified is not + implemented. + @rtype: string + """ + + if family == AF_INET: + return dns.ipv4.inet_aton(text) + elif family == AF_INET6: + return dns.ipv6.inet_aton(text) + else: + raise NotImplementedError + +def inet_ntop(family, address): + """Convert the binary form of a network address into its textual form. + + @param family: the address family + @type family: int + @param address: the binary address + @type address: string + @raises NotImplementedError: the address family specified is not + implemented. + @rtype: string + """ + if family == AF_INET: + return dns.ipv4.inet_ntoa(address) + elif family == AF_INET6: + return dns.ipv6.inet_ntoa(address) + else: + raise NotImplementedError + +def af_for_address(text): + """Determine the address family of a textual-form network address. + + @param text: the textual address + @type text: string + @raises ValueError: the address family cannot be determined from the input. + @rtype: int + """ + try: + junk = dns.ipv4.inet_aton(text) + return AF_INET + except: + try: + junk = dns.ipv6.inet_aton(text) + return AF_INET6 + except: + raise ValueError + +def is_multicast(text): + """Is the textual-form network address a multicast address? + + @param text: the textual address + @raises ValueError: the address family cannot be determined from the input. + @rtype: bool + """ + try: + first = ord(dns.ipv4.inet_aton(text)[0]) + return (first >= 224 and first <= 239) + except: + try: + first = ord(dns.ipv6.inet_aton(text)[0]) + return (first == 255) + except: + raise ValueError + diff --git a/third_party/dnspython/dns/ipv4.py b/third_party/dnspython/dns/ipv4.py new file mode 100644 index 0000000000..e117966e5f --- /dev/null +++ b/third_party/dnspython/dns/ipv4.py @@ -0,0 +1,42 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""IPv4 helper functions.""" + +import struct + +import dns.exception + +def inet_ntoa(address): + if len(address) != 4: + raise dns.exception.SyntaxError + return '%u.%u.%u.%u' % (ord(address[0]), ord(address[1]), + ord(address[2]), ord(address[3])) + +def inet_aton(text): + parts = text.split('.') + if len(parts) != 4: + raise dns.exception.SyntaxError + for part in parts: + if not part.isdigit(): + raise dns.exception.SyntaxError + if len(part) > 1 and part[0] == '0': + # No leading zeros + raise dns.exception.SyntaxError + try: + bytes = [int(part) for part in parts] + return struct.pack('BBBB', *bytes) + except: + raise dns.exception.SyntaxError diff --git a/third_party/dnspython/dns/ipv6.py b/third_party/dnspython/dns/ipv6.py new file mode 100644 index 0000000000..69db34a7de --- /dev/null +++ b/third_party/dnspython/dns/ipv6.py @@ -0,0 +1,163 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""IPv6 helper functions.""" + +import re + +import dns.exception +import dns.ipv4 + +_leading_zero = re.compile(r'0+([0-9a-f]+)') + +def inet_ntoa(address): + """Convert a network format IPv6 address into text. + + @param address: the binary address + @type address: string + @rtype: string + @raises ValueError: the address isn't 16 bytes long + """ + + if len(address) != 16: + raise ValueError("IPv6 addresses are 16 bytes long") + hex = address.encode('hex_codec') + chunks = [] + i = 0 + l = len(hex) + while i < l: + chunk = hex[i : i + 4] + # strip leading zeros. we do this with an re instead of + # with lstrip() because lstrip() didn't support chars until + # python 2.2.2 + m = _leading_zero.match(chunk) + if not m is None: + chunk = m.group(1) + chunks.append(chunk) + i += 4 + # + # Compress the longest subsequence of 0-value chunks to :: + # + best_start = 0 + best_len = 0 + start = -1 + last_was_zero = False + for i in xrange(8): + if chunks[i] != '0': + if last_was_zero: + end = i + current_len = end - start + if current_len > best_len: + best_start = start + best_len = current_len + last_was_zero = False + elif not last_was_zero: + start = i + last_was_zero = True + if last_was_zero: + end = 8 + current_len = end - start + if current_len > best_len: + best_start = start + best_len = current_len + if best_len > 0: + if best_start == 0 and \ + (best_len == 6 or + best_len == 5 and chunks[5] == 'ffff'): + # We have an embedded IPv4 address + if best_len == 6: + prefix = '::' + else: + prefix = '::ffff:' + hex = prefix + dns.ipv4.inet_ntoa(address[12:]) + else: + hex = ':'.join(chunks[:best_start]) + '::' + \ + ':'.join(chunks[best_start + best_len:]) + else: + hex = ':'.join(chunks) + return hex + +_v4_ending = re.compile(r'(.*):(\d+\.\d+\.\d+\.\d+)$') +_colon_colon_start = re.compile(r'::.*') +_colon_colon_end = re.compile(r'.*::$') + +def inet_aton(text): + """Convert a text format IPv6 address into network format. + + @param text: the textual address + @type text: string + @rtype: string + @raises dns.exception.SyntaxError: the text was not properly formatted + """ + + # + # Our aim here is not something fast; we just want something that works. + # + + if text == '::': + text = '0::' + # + # Get rid of the icky dot-quad syntax if we have it. + # + m = _v4_ending.match(text) + if not m is None: + b = dns.ipv4.inet_aton(m.group(2)) + text = "%s:%02x%02x:%02x%02x" % (m.group(1), ord(b[0]), ord(b[1]), + ord(b[2]), ord(b[3])) + # + # Try to turn '::' into ':'; if no match try to + # turn '::' into ':' + # + m = _colon_colon_start.match(text) + if not m is None: + text = text[1:] + else: + m = _colon_colon_end.match(text) + if not m is None: + text = text[:-1] + # + # Now canonicalize into 8 chunks of 4 hex digits each + # + chunks = text.split(':') + l = len(chunks) + if l > 8: + raise dns.exception.SyntaxError + seen_empty = False + canonical = [] + for c in chunks: + if c == '': + if seen_empty: + raise dns.exception.SyntaxError + seen_empty = True + for i in xrange(0, 8 - l + 1): + canonical.append('0000') + else: + lc = len(c) + if lc > 4: + raise dns.exception.SyntaxError + if lc != 4: + c = ('0' * (4 - lc)) + c + canonical.append(c) + if l < 8 and not seen_empty: + raise dns.exception.SyntaxError + text = ''.join(canonical) + + # + # Finally we can go to binary. + # + try: + return text.decode('hex_codec') + except TypeError: + raise dns.exception.SyntaxError diff --git a/third_party/dnspython/dns/message.py b/third_party/dnspython/dns/message.py new file mode 100644 index 0000000000..cf29133934 --- /dev/null +++ b/third_party/dnspython/dns/message.py @@ -0,0 +1,1088 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Messages""" + +import cStringIO +import random +import struct +import sys +import time + +import dns.edns +import dns.exception +import dns.flags +import dns.name +import dns.opcode +import dns.entropy +import dns.rcode +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.rrset +import dns.renderer +import dns.tsig +import dns.wiredata + +class ShortHeader(dns.exception.FormError): + """Raised if the DNS packet passed to from_wire() is too short.""" + pass + +class TrailingJunk(dns.exception.FormError): + """Raised if the DNS packet passed to from_wire() has extra junk + at the end of it.""" + pass + +class UnknownHeaderField(dns.exception.DNSException): + """Raised if a header field name is not recognized when converting from + text into a message.""" + pass + +class BadEDNS(dns.exception.FormError): + """Raised if an OPT record occurs somewhere other than the start of + the additional data section.""" + pass + +class BadTSIG(dns.exception.FormError): + """Raised if a TSIG record occurs somewhere other than the end of + the additional data section.""" + pass + +class UnknownTSIGKey(dns.exception.DNSException): + """Raised if we got a TSIG but don't know the key.""" + pass + +class Message(object): + """A DNS message. + + @ivar id: The query id; the default is a randomly chosen id. + @type id: int + @ivar flags: The DNS flags of the message. @see: RFC 1035 for an + explanation of these flags. + @type flags: int + @ivar question: The question section. + @type question: list of dns.rrset.RRset objects + @ivar answer: The answer section. + @type answer: list of dns.rrset.RRset objects + @ivar authority: The authority section. + @type authority: list of dns.rrset.RRset objects + @ivar additional: The additional data section. + @type additional: list of dns.rrset.RRset objects + @ivar edns: The EDNS level to use. The default is -1, no Edns. + @type edns: int + @ivar ednsflags: The EDNS flags + @type ednsflags: long + @ivar payload: The EDNS payload size. The default is 0. + @type payload: int + @ivar options: The EDNS options + @type options: list of dns.edns.Option objects + @ivar request_payload: The associated request's EDNS payload size. + @type request_payload: int + @ivar keyring: The TSIG keyring to use. The default is None. + @type keyring: dict + @ivar keyname: The TSIG keyname to use. The default is None. + @type keyname: dns.name.Name object + @ivar keyalgorithm: The TSIG algorithm to use; defaults to + dns.tsig.default_algorithm. Constants for TSIG algorithms are defined + in dns.tsig, and the currently implemented algorithms are + HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and + HMAC_SHA512. + @type keyalgorithm: string + @ivar request_mac: The TSIG MAC of the request message associated with + this message; used when validating TSIG signatures. @see: RFC 2845 for + more information on TSIG fields. + @type request_mac: string + @ivar fudge: TSIG time fudge; default is 300 seconds. + @type fudge: int + @ivar original_id: TSIG original id; defaults to the message's id + @type original_id: int + @ivar tsig_error: TSIG error code; default is 0. + @type tsig_error: int + @ivar other_data: TSIG other data. + @type other_data: string + @ivar mac: The TSIG MAC for this message. + @type mac: string + @ivar xfr: Is the message being used to contain the results of a DNS + zone transfer? The default is False. + @type xfr: bool + @ivar origin: The origin of the zone in messages which are used for + zone transfers or for DNS dynamic updates. The default is None. + @type origin: dns.name.Name object + @ivar tsig_ctx: The TSIG signature context associated with this + message. The default is None. + @type tsig_ctx: hmac.HMAC object + @ivar had_tsig: Did the message decoded from wire format have a TSIG + signature? + @type had_tsig: bool + @ivar multi: Is this message part of a multi-message sequence? The + default is false. This variable is used when validating TSIG signatures + on messages which are part of a zone transfer. + @type multi: bool + @ivar first: Is this message standalone, or the first of a multi + message sequence? This variable is used when validating TSIG signatures + on messages which are part of a zone transfer. + @type first: bool + @ivar index: An index of rrsets in the message. The index key is + (section, name, rdclass, rdtype, covers, deleting). Indexing can be + disabled by setting the index to None. + @type index: dict + """ + + def __init__(self, id=None): + if id is None: + self.id = dns.entropy.random_16() + else: + self.id = id + self.flags = 0 + self.question = [] + self.answer = [] + self.authority = [] + self.additional = [] + self.edns = -1 + self.ednsflags = 0 + self.payload = 0 + self.options = [] + self.request_payload = 0 + self.keyring = None + self.keyname = None + self.keyalgorithm = dns.tsig.default_algorithm + self.request_mac = '' + self.other_data = '' + self.tsig_error = 0 + self.fudge = 300 + self.original_id = self.id + self.mac = '' + self.xfr = False + self.origin = None + self.tsig_ctx = None + self.had_tsig = False + self.multi = False + self.first = True + self.index = {} + + def __repr__(self): + return '' + + def __str__(self): + return self.to_text() + + def to_text(self, origin=None, relativize=True, **kw): + """Convert the message to text. + + The I{origin}, I{relativize}, and any other keyword + arguments are passed to the rrset to_wire() method. + + @rtype: string + """ + + s = cStringIO.StringIO() + print >> s, 'id %d' % self.id + print >> s, 'opcode %s' % \ + dns.opcode.to_text(dns.opcode.from_flags(self.flags)) + rc = dns.rcode.from_flags(self.flags, self.ednsflags) + print >> s, 'rcode %s' % dns.rcode.to_text(rc) + print >> s, 'flags %s' % dns.flags.to_text(self.flags) + if self.edns >= 0: + print >> s, 'edns %s' % self.edns + if self.ednsflags != 0: + print >> s, 'eflags %s' % \ + dns.flags.edns_to_text(self.ednsflags) + print >> s, 'payload', self.payload + is_update = dns.opcode.is_update(self.flags) + if is_update: + print >> s, ';ZONE' + else: + print >> s, ';QUESTION' + for rrset in self.question: + print >> s, rrset.to_text(origin, relativize, **kw) + if is_update: + print >> s, ';PREREQ' + else: + print >> s, ';ANSWER' + for rrset in self.answer: + print >> s, rrset.to_text(origin, relativize, **kw) + if is_update: + print >> s, ';UPDATE' + else: + print >> s, ';AUTHORITY' + for rrset in self.authority: + print >> s, rrset.to_text(origin, relativize, **kw) + print >> s, ';ADDITIONAL' + for rrset in self.additional: + print >> s, rrset.to_text(origin, relativize, **kw) + # + # We strip off the final \n so the caller can print the result without + # doing weird things to get around eccentricities in Python print + # formatting + # + return s.getvalue()[:-1] + + def __eq__(self, other): + """Two messages are equal if they have the same content in the + header, question, answer, and authority sections. + @rtype: bool""" + if not isinstance(other, Message): + return False + if self.id != other.id: + return False + if self.flags != other.flags: + return False + for n in self.question: + if n not in other.question: + return False + for n in other.question: + if n not in self.question: + return False + for n in self.answer: + if n not in other.answer: + return False + for n in other.answer: + if n not in self.answer: + return False + for n in self.authority: + if n not in other.authority: + return False + for n in other.authority: + if n not in self.authority: + return False + return True + + def __ne__(self, other): + """Are two messages not equal? + @rtype: bool""" + return not self.__eq__(other) + + def is_response(self, other): + """Is other a response to self? + @rtype: bool""" + if other.flags & dns.flags.QR == 0 or \ + self.id != other.id or \ + dns.opcode.from_flags(self.flags) != \ + dns.opcode.from_flags(other.flags): + return False + if dns.rcode.from_flags(other.flags, other.ednsflags) != \ + dns.rcode.NOERROR: + return True + if dns.opcode.is_update(self.flags): + return True + for n in self.question: + if n not in other.question: + return False + for n in other.question: + if n not in self.question: + return False + return True + + def section_number(self, section): + if section is self.question: + return 0 + elif section is self.answer: + return 1 + elif section is self.authority: + return 2 + elif section is self.additional: + return 3 + else: + raise ValueError('unknown section') + + def find_rrset(self, section, name, rdclass, rdtype, + covers=dns.rdatatype.NONE, deleting=None, create=False, + force_unique=False): + """Find the RRset with the given attributes in the specified section. + + @param section: the section of the message to look in, e.g. + self.answer. + @type section: list of dns.rrset.RRset objects + @param name: the name of the RRset + @type name: dns.name.Name object + @param rdclass: the class of the RRset + @type rdclass: int + @param rdtype: the type of the RRset + @type rdtype: int + @param covers: the covers value of the RRset + @type covers: int + @param deleting: the deleting value of the RRset + @type deleting: int + @param create: If True, create the RRset if it is not found. + The created RRset is appended to I{section}. + @type create: bool + @param force_unique: If True and create is also True, create a + new RRset regardless of whether a matching RRset exists already. + @type force_unique: bool + @raises KeyError: the RRset was not found and create was False + @rtype: dns.rrset.RRset object""" + + key = (self.section_number(section), + name, rdclass, rdtype, covers, deleting) + if not force_unique: + if not self.index is None: + rrset = self.index.get(key) + if not rrset is None: + return rrset + else: + for rrset in section: + if rrset.match(name, rdclass, rdtype, covers, deleting): + return rrset + if not create: + raise KeyError + rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) + section.append(rrset) + if not self.index is None: + self.index[key] = rrset + return rrset + + def get_rrset(self, section, name, rdclass, rdtype, + covers=dns.rdatatype.NONE, deleting=None, create=False, + force_unique=False): + """Get the RRset with the given attributes in the specified section. + + If the RRset is not found, None is returned. + + @param section: the section of the message to look in, e.g. + self.answer. + @type section: list of dns.rrset.RRset objects + @param name: the name of the RRset + @type name: dns.name.Name object + @param rdclass: the class of the RRset + @type rdclass: int + @param rdtype: the type of the RRset + @type rdtype: int + @param covers: the covers value of the RRset + @type covers: int + @param deleting: the deleting value of the RRset + @type deleting: int + @param create: If True, create the RRset if it is not found. + The created RRset is appended to I{section}. + @type create: bool + @param force_unique: If True and create is also True, create a + new RRset regardless of whether a matching RRset exists already. + @type force_unique: bool + @rtype: dns.rrset.RRset object or None""" + + try: + rrset = self.find_rrset(section, name, rdclass, rdtype, covers, + deleting, create, force_unique) + except KeyError: + rrset = None + return rrset + + def to_wire(self, origin=None, max_size=0, **kw): + """Return a string containing the message in DNS compressed wire + format. + + Additional keyword arguments are passed to the rrset to_wire() + method. + + @param origin: The origin to be appended to any relative names. + @type origin: dns.name.Name object + @param max_size: The maximum size of the wire format output; default + is 0, which means 'the message's request payload, if nonzero, or + 65536'. + @type max_size: int + @raises dns.exception.TooBig: max_size was exceeded + @rtype: string + """ + + if max_size == 0: + if self.request_payload != 0: + max_size = self.request_payload + else: + max_size = 65535 + if max_size < 512: + max_size = 512 + elif max_size > 65535: + max_size = 65535 + r = dns.renderer.Renderer(self.id, self.flags, max_size, origin) + for rrset in self.question: + r.add_question(rrset.name, rrset.rdtype, rrset.rdclass) + for rrset in self.answer: + r.add_rrset(dns.renderer.ANSWER, rrset, **kw) + for rrset in self.authority: + r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw) + if self.edns >= 0: + r.add_edns(self.edns, self.ednsflags, self.payload, self.options) + for rrset in self.additional: + r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw) + r.write_header() + if not self.keyname is None: + r.add_tsig(self.keyname, self.keyring[self.keyname], + self.fudge, self.original_id, self.tsig_error, + self.other_data, self.request_mac, + self.keyalgorithm) + self.mac = r.mac + return r.get_wire() + + def use_tsig(self, keyring, keyname=None, fudge=300, + original_id=None, tsig_error=0, other_data='', + algorithm=dns.tsig.default_algorithm): + """When sending, a TSIG signature using the specified keyring + and keyname should be added. + + @param keyring: The TSIG keyring to use; defaults to None. + @type keyring: dict + @param keyname: The name of the TSIG key to use; defaults to None. + The key must be defined in the keyring. If a keyring is specified + but a keyname is not, then the key used will be the first key in the + keyring. Note that the order of keys in a dictionary is not defined, + so applications should supply a keyname when a keyring is used, unless + they know the keyring contains only one key. + @type keyname: dns.name.Name or string + @param fudge: TSIG time fudge; default is 300 seconds. + @type fudge: int + @param original_id: TSIG original id; defaults to the message's id + @type original_id: int + @param tsig_error: TSIG error code; default is 0. + @type tsig_error: int + @param other_data: TSIG other data. + @type other_data: string + @param algorithm: The TSIG algorithm to use; defaults to + dns.tsig.default_algorithm + """ + + self.keyring = keyring + if keyname is None: + self.keyname = self.keyring.keys()[0] + else: + if isinstance(keyname, (str, unicode)): + keyname = dns.name.from_text(keyname) + self.keyname = keyname + self.keyalgorithm = algorithm + self.fudge = fudge + if original_id is None: + self.original_id = self.id + else: + self.original_id = original_id + self.tsig_error = tsig_error + self.other_data = other_data + + def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None): + """Configure EDNS behavior. + @param edns: The EDNS level to use. Specifying None, False, or -1 + means 'do not use EDNS', and in this case the other parameters are + ignored. Specifying True is equivalent to specifying 0, i.e. 'use + EDNS0'. + @type edns: int or bool or None + @param ednsflags: EDNS flag values. + @type ednsflags: int + @param payload: The EDNS sender's payload field, which is the maximum + size of UDP datagram the sender can handle. + @type payload: int + @param request_payload: The EDNS payload size to use when sending + this message. If not specified, defaults to the value of payload. + @type request_payload: int or None + @param options: The EDNS options + @type options: None or list of dns.edns.Option objects + @see: RFC 2671 + """ + if edns is None or edns is False: + edns = -1 + if edns is True: + edns = 0 + if request_payload is None: + request_payload = payload + if edns < 0: + ednsflags = 0 + payload = 0 + request_payload = 0 + options = [] + else: + # make sure the EDNS version in ednsflags agrees with edns + ednsflags &= 0xFF00FFFFL + ednsflags |= (edns << 16) + if options is None: + options = [] + self.edns = edns + self.ednsflags = ednsflags + self.payload = payload + self.options = options + self.request_payload = request_payload + + def want_dnssec(self, wanted=True): + """Enable or disable 'DNSSEC desired' flag in requests. + @param wanted: Is DNSSEC desired? If True, EDNS is enabled if + required, and then the DO bit is set. If False, the DO bit is + cleared if EDNS is enabled. + @type wanted: bool + """ + if wanted: + if self.edns < 0: + self.use_edns() + self.ednsflags |= dns.flags.DO + elif self.edns >= 0: + self.ednsflags &= ~dns.flags.DO + + def rcode(self): + """Return the rcode. + @rtype: int + """ + return dns.rcode.from_flags(self.flags, self.ednsflags) + + def set_rcode(self, rcode): + """Set the rcode. + @param rcode: the rcode + @type rcode: int + """ + (value, evalue) = dns.rcode.to_flags(rcode) + self.flags &= 0xFFF0 + self.flags |= value + self.ednsflags &= 0x00FFFFFFL + self.ednsflags |= evalue + if self.ednsflags != 0 and self.edns < 0: + self.edns = 0 + + def opcode(self): + """Return the opcode. + @rtype: int + """ + return dns.opcode.from_flags(self.flags) + + def set_opcode(self, opcode): + """Set the opcode. + @param opcode: the opcode + @type opcode: int + """ + self.flags &= 0x87FF + self.flags |= dns.opcode.to_flags(opcode) + +class _WireReader(object): + """Wire format reader. + + @ivar wire: the wire-format message. + @type wire: string + @ivar message: The message object being built + @type message: dns.message.Message object + @ivar current: When building a message object from wire format, this + variable contains the offset from the beginning of wire of the next octet + to be read. + @type current: int + @ivar updating: Is the message a dynamic update? + @type updating: bool + @ivar one_rr_per_rrset: Put each RR into its own RRset? + @type one_rr_per_rrset: bool + @ivar zone_rdclass: The class of the zone in messages which are + DNS dynamic updates. + @type zone_rdclass: int + """ + + def __init__(self, wire, message, question_only=False, + one_rr_per_rrset=False): + self.wire = dns.wiredata.maybe_wrap(wire) + self.message = message + self.current = 0 + self.updating = False + self.zone_rdclass = dns.rdataclass.IN + self.question_only = question_only + self.one_rr_per_rrset = one_rr_per_rrset + + def _get_question(self, qcount): + """Read the next I{qcount} records from the wire data and add them to + the question section. + @param qcount: the number of questions in the message + @type qcount: int""" + + if self.updating and qcount > 1: + raise dns.exception.FormError + + for i in xrange(0, qcount): + (qname, used) = dns.name.from_wire(self.wire, self.current) + if not self.message.origin is None: + qname = qname.relativize(self.message.origin) + self.current = self.current + used + (rdtype, rdclass) = \ + struct.unpack('!HH', + self.wire[self.current:self.current + 4]) + self.current = self.current + 4 + self.message.find_rrset(self.message.question, qname, + rdclass, rdtype, create=True, + force_unique=True) + if self.updating: + self.zone_rdclass = rdclass + + def _get_section(self, section, count): + """Read the next I{count} records from the wire data and add them to + the specified section. + @param section: the section of the message to which to add records + @type section: list of dns.rrset.RRset objects + @param count: the number of records to read + @type count: int""" + + if self.updating or self.one_rr_per_rrset: + force_unique = True + else: + force_unique = False + seen_opt = False + for i in xrange(0, count): + rr_start = self.current + (name, used) = dns.name.from_wire(self.wire, self.current) + absolute_name = name + if not self.message.origin is None: + name = name.relativize(self.message.origin) + self.current = self.current + used + (rdtype, rdclass, ttl, rdlen) = \ + struct.unpack('!HHIH', + self.wire[self.current:self.current + 10]) + self.current = self.current + 10 + if rdtype == dns.rdatatype.OPT: + if not section is self.message.additional or seen_opt: + raise BadEDNS + self.message.payload = rdclass + self.message.ednsflags = ttl + self.message.edns = (ttl & 0xff0000) >> 16 + self.message.options = [] + current = self.current + optslen = rdlen + while optslen > 0: + (otype, olen) = \ + struct.unpack('!HH', + self.wire[current:current + 4]) + current = current + 4 + opt = dns.edns.option_from_wire(otype, self.wire, current, olen) + self.message.options.append(opt) + current = current + olen + optslen = optslen - 4 - olen + seen_opt = True + elif rdtype == dns.rdatatype.TSIG: + if not (section is self.message.additional and + i == (count - 1)): + raise BadTSIG + if self.message.keyring is None: + raise UnknownTSIGKey('got signed message without keyring') + secret = self.message.keyring.get(absolute_name) + if secret is None: + raise UnknownTSIGKey("key '%s' unknown" % name) + self.message.tsig_ctx = \ + dns.tsig.validate(self.wire, + absolute_name, + secret, + int(time.time()), + self.message.request_mac, + rr_start, + self.current, + rdlen, + self.message.tsig_ctx, + self.message.multi, + self.message.first) + self.message.had_tsig = True + else: + if ttl < 0: + ttl = 0 + if self.updating and \ + (rdclass == dns.rdataclass.ANY or + rdclass == dns.rdataclass.NONE): + deleting = rdclass + rdclass = self.zone_rdclass + else: + deleting = None + if deleting == dns.rdataclass.ANY or \ + (deleting == dns.rdataclass.NONE and \ + section is self.message.answer): + covers = dns.rdatatype.NONE + rd = None + else: + rd = dns.rdata.from_wire(rdclass, rdtype, self.wire, + self.current, rdlen, + self.message.origin) + covers = rd.covers() + if self.message.xfr and rdtype == dns.rdatatype.SOA: + force_unique = True + rrset = self.message.find_rrset(section, name, + rdclass, rdtype, covers, + deleting, True, force_unique) + if not rd is None: + rrset.add(rd, ttl) + self.current = self.current + rdlen + + def read(self): + """Read a wire format DNS message and build a dns.message.Message + object.""" + + l = len(self.wire) + if l < 12: + raise ShortHeader + (self.message.id, self.message.flags, qcount, ancount, + aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12]) + self.current = 12 + if dns.opcode.is_update(self.message.flags): + self.updating = True + self._get_question(qcount) + if self.question_only: + return + self._get_section(self.message.answer, ancount) + self._get_section(self.message.authority, aucount) + self._get_section(self.message.additional, adcount) + if self.current != l: + raise TrailingJunk + if self.message.multi and self.message.tsig_ctx and \ + not self.message.had_tsig: + self.message.tsig_ctx.update(self.wire) + + +def from_wire(wire, keyring=None, request_mac='', xfr=False, origin=None, + tsig_ctx = None, multi = False, first = True, + question_only = False, one_rr_per_rrset = False): + """Convert a DNS wire format message into a message + object. + + @param keyring: The keyring to use if the message is signed. + @type keyring: dict + @param request_mac: If the message is a response to a TSIG-signed request, + I{request_mac} should be set to the MAC of that request. + @type request_mac: string + @param xfr: Is this message part of a zone transfer? + @type xfr: bool + @param origin: If the message is part of a zone transfer, I{origin} + should be the origin name of the zone. + @type origin: dns.name.Name object + @param tsig_ctx: The ongoing TSIG context, used when validating zone + transfers. + @type tsig_ctx: hmac.HMAC object + @param multi: Is this message part of a multiple message sequence? + @type multi: bool + @param first: Is this message standalone, or the first of a multi + message sequence? + @type first: bool + @param question_only: Read only up to the end of the question section? + @type question_only: bool + @param one_rr_per_rrset: Put each RR into its own RRset + @type one_rr_per_rrset: bool + @raises ShortHeader: The message is less than 12 octets long. + @raises TrailingJunk: There were octets in the message past the end + of the proper DNS message. + @raises BadEDNS: An OPT record was in the wrong section, or occurred more + than once. + @raises BadTSIG: A TSIG record was not the last record of the additional + data section. + @rtype: dns.message.Message object""" + + m = Message(id=0) + m.keyring = keyring + m.request_mac = request_mac + m.xfr = xfr + m.origin = origin + m.tsig_ctx = tsig_ctx + m.multi = multi + m.first = first + + reader = _WireReader(wire, m, question_only, one_rr_per_rrset) + reader.read() + + return m + + +class _TextReader(object): + """Text format reader. + + @ivar tok: the tokenizer + @type tok: dns.tokenizer.Tokenizer object + @ivar message: The message object being built + @type message: dns.message.Message object + @ivar updating: Is the message a dynamic update? + @type updating: bool + @ivar zone_rdclass: The class of the zone in messages which are + DNS dynamic updates. + @type zone_rdclass: int + @ivar last_name: The most recently read name when building a message object + from text format. + @type last_name: dns.name.Name object + """ + + def __init__(self, text, message): + self.message = message + self.tok = dns.tokenizer.Tokenizer(text) + self.last_name = None + self.zone_rdclass = dns.rdataclass.IN + self.updating = False + + def _header_line(self, section): + """Process one line from the text format header section.""" + + token = self.tok.get() + what = token.value + if what == 'id': + self.message.id = self.tok.get_int() + elif what == 'flags': + while True: + token = self.tok.get() + if not token.is_identifier(): + self.tok.unget(token) + break + self.message.flags = self.message.flags | \ + dns.flags.from_text(token.value) + if dns.opcode.is_update(self.message.flags): + self.updating = True + elif what == 'edns': + self.message.edns = self.tok.get_int() + self.message.ednsflags = self.message.ednsflags | \ + (self.message.edns << 16) + elif what == 'eflags': + if self.message.edns < 0: + self.message.edns = 0 + while True: + token = self.tok.get() + if not token.is_identifier(): + self.tok.unget(token) + break + self.message.ednsflags = self.message.ednsflags | \ + dns.flags.edns_from_text(token.value) + elif what == 'payload': + self.message.payload = self.tok.get_int() + if self.message.edns < 0: + self.message.edns = 0 + elif what == 'opcode': + text = self.tok.get_string() + self.message.flags = self.message.flags | \ + dns.opcode.to_flags(dns.opcode.from_text(text)) + elif what == 'rcode': + text = self.tok.get_string() + self.message.set_rcode(dns.rcode.from_text(text)) + else: + raise UnknownHeaderField + self.tok.get_eol() + + def _question_line(self, section): + """Process one line from the text format question section.""" + + token = self.tok.get(want_leading = True) + if not token.is_whitespace(): + self.last_name = dns.name.from_text(token.value, None) + name = self.last_name + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except: + rdclass = dns.rdataclass.IN + # Type + rdtype = dns.rdatatype.from_text(token.value) + self.message.find_rrset(self.message.question, name, + rdclass, rdtype, create=True, + force_unique=True) + if self.updating: + self.zone_rdclass = rdclass + self.tok.get_eol() + + def _rr_line(self, section): + """Process one line from the text format answer, authority, or + additional data sections. + """ + + deleting = None + # Name + token = self.tok.get(want_leading = True) + if not token.is_whitespace(): + self.last_name = dns.name.from_text(token.value, None) + name = self.last_name + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + # TTL + try: + ttl = int(token.value, 0) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except: + ttl = 0 + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE: + deleting = rdclass + rdclass = self.zone_rdclass + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except: + rdclass = dns.rdataclass.IN + # Type + rdtype = dns.rdatatype.from_text(token.value) + token = self.tok.get() + if not token.is_eol_or_eof(): + self.tok.unget(token) + rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None) + covers = rd.covers() + else: + rd = None + covers = dns.rdatatype.NONE + rrset = self.message.find_rrset(section, name, + rdclass, rdtype, covers, + deleting, True, self.updating) + if not rd is None: + rrset.add(rd, ttl) + + def read(self): + """Read a text format DNS message and build a dns.message.Message + object.""" + + line_method = self._header_line + section = None + while 1: + token = self.tok.get(True, True) + if token.is_eol_or_eof(): + break + if token.is_comment(): + u = token.value.upper() + if u == 'HEADER': + line_method = self._header_line + elif u == 'QUESTION' or u == 'ZONE': + line_method = self._question_line + section = self.message.question + elif u == 'ANSWER' or u == 'PREREQ': + line_method = self._rr_line + section = self.message.answer + elif u == 'AUTHORITY' or u == 'UPDATE': + line_method = self._rr_line + section = self.message.authority + elif u == 'ADDITIONAL': + line_method = self._rr_line + section = self.message.additional + self.tok.get_eol() + continue + self.tok.unget(token) + line_method(section) + + +def from_text(text): + """Convert the text format message into a message object. + + @param text: The text format message. + @type text: string + @raises UnknownHeaderField: + @raises dns.exception.SyntaxError: + @rtype: dns.message.Message object""" + + # 'text' can also be a file, but we don't publish that fact + # since it's an implementation detail. The official file + # interface is from_file(). + + m = Message() + + reader = _TextReader(text, m) + reader.read() + + return m + +def from_file(f): + """Read the next text format message from the specified file. + + @param f: file or string. If I{f} is a string, it is treated + as the name of a file to open. + @raises UnknownHeaderField: + @raises dns.exception.SyntaxError: + @rtype: dns.message.Message object""" + + if sys.hexversion >= 0x02030000: + # allow Unicode filenames; turn on universal newline support + str_type = basestring + opts = 'rU' + else: + str_type = str + opts = 'r' + if isinstance(f, str_type): + f = file(f, opts) + want_close = True + else: + want_close = False + + try: + m = from_text(f) + finally: + if want_close: + f.close() + return m + +def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None, + want_dnssec=False): + """Make a query message. + + The query name, type, and class may all be specified either + as objects of the appropriate type, or as strings. + + The query will have a randomly choosen query id, and its DNS flags + will be set to dns.flags.RD. + + @param qname: The query name. + @type qname: dns.name.Name object or string + @param rdtype: The desired rdata type. + @type rdtype: int + @param rdclass: The desired rdata class; the default is class IN. + @type rdclass: int + @param use_edns: The EDNS level to use; the default is None (no EDNS). + See the description of dns.message.Message.use_edns() for the possible + values for use_edns and their meanings. + @type use_edns: int or bool or None + @param want_dnssec: Should the query indicate that DNSSEC is desired? + @type want_dnssec: bool + @rtype: dns.message.Message object""" + + if isinstance(qname, (str, unicode)): + qname = dns.name.from_text(qname) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if isinstance(rdclass, (str, unicode)): + rdclass = dns.rdataclass.from_text(rdclass) + m = Message() + m.flags |= dns.flags.RD + m.find_rrset(m.question, qname, rdclass, rdtype, create=True, + force_unique=True) + m.use_edns(use_edns) + m.want_dnssec(want_dnssec) + return m + +def make_response(query, recursion_available=False, our_payload=8192): + """Make a message which is a response for the specified query. + The message returned is really a response skeleton; it has all + of the infrastructure required of a response, but none of the + content. + + The response's question section is a shallow copy of the query's + question section, so the query's question RRsets should not be + changed. + + @param query: the query to respond to + @type query: dns.message.Message object + @param recursion_available: should RA be set in the response? + @type recursion_available: bool + @param our_payload: payload size to advertise in EDNS responses; default + is 8192. + @type our_payload: int + @rtype: dns.message.Message object""" + + if query.flags & dns.flags.QR: + raise dns.exception.FormError('specified query message is not a query') + response = dns.message.Message(query.id) + response.flags = dns.flags.QR | (query.flags & dns.flags.RD) + if recursion_available: + response.flags |= dns.flags.RA + response.set_opcode(query.opcode()) + response.question = list(query.question) + if query.edns >= 0: + response.use_edns(0, 0, our_payload, query.payload) + if not query.keyname is None: + response.keyname = query.keyname + response.keyring = query.keyring + response.request_mac = query.mac + return response diff --git a/third_party/dnspython/dns/name.py b/third_party/dnspython/dns/name.py new file mode 100644 index 0000000000..ed3ffeec57 --- /dev/null +++ b/third_party/dnspython/dns/name.py @@ -0,0 +1,702 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Names. + +@var root: The DNS root name. +@type root: dns.name.Name object +@var empty: The empty DNS name. +@type empty: dns.name.Name object +""" + +import cStringIO +import struct +import sys + +if sys.hexversion >= 0x02030000: + import encodings.idna + +import dns.exception +import dns.wiredata + +NAMERELN_NONE = 0 +NAMERELN_SUPERDOMAIN = 1 +NAMERELN_SUBDOMAIN = 2 +NAMERELN_EQUAL = 3 +NAMERELN_COMMONANCESTOR = 4 + +class EmptyLabel(dns.exception.SyntaxError): + """Raised if a label is empty.""" + pass + +class BadEscape(dns.exception.SyntaxError): + """Raised if an escaped code in a text format name is invalid.""" + pass + +class BadPointer(dns.exception.FormError): + """Raised if a compression pointer points forward instead of backward.""" + pass + +class BadLabelType(dns.exception.FormError): + """Raised if the label type of a wire format name is unknown.""" + pass + +class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): + """Raised if an attempt is made to convert a non-absolute name to + wire when there is also a non-absolute (or missing) origin.""" + pass + +class NameTooLong(dns.exception.FormError): + """Raised if a name is > 255 octets long.""" + pass + +class LabelTooLong(dns.exception.SyntaxError): + """Raised if a label is > 63 octets long.""" + pass + +class AbsoluteConcatenation(dns.exception.DNSException): + """Raised if an attempt is made to append anything other than the + empty name to an absolute name.""" + pass + +class NoParent(dns.exception.DNSException): + """Raised if an attempt is made to get the parent of the root name + or the empty name.""" + pass + +_escaped = { + '"' : True, + '(' : True, + ')' : True, + '.' : True, + ';' : True, + '\\' : True, + '@' : True, + '$' : True + } + +def _escapify(label): + """Escape the characters in label which need it. + @returns: the escaped string + @rtype: string""" + text = '' + for c in label: + if c in _escaped: + text += '\\' + c + elif ord(c) > 0x20 and ord(c) < 0x7F: + text += c + else: + text += '\\%03d' % ord(c) + return text + +def _validate_labels(labels): + """Check for empty labels in the middle of a label sequence, + labels that are too long, and for too many labels. + @raises NameTooLong: the name as a whole is too long + @raises LabelTooLong: an individual label is too long + @raises EmptyLabel: a label is empty (i.e. the root label) and appears + in a position other than the end of the label sequence""" + + l = len(labels) + total = 0 + i = -1 + j = 0 + for label in labels: + ll = len(label) + total += ll + 1 + if ll > 63: + raise LabelTooLong + if i < 0 and label == '': + i = j + j += 1 + if total > 255: + raise NameTooLong + if i >= 0 and i != l - 1: + raise EmptyLabel + +class Name(object): + """A DNS name. + + The dns.name.Name class represents a DNS name as a tuple of labels. + Instances of the class are immutable. + + @ivar labels: The tuple of labels in the name. Each label is a string of + up to 63 octets.""" + + __slots__ = ['labels'] + + def __init__(self, labels): + """Initialize a domain name from a list of labels. + @param labels: the labels + @type labels: any iterable whose values are strings + """ + + super(Name, self).__setattr__('labels', tuple(labels)) + _validate_labels(self.labels) + + def __setattr__(self, name, value): + raise TypeError("object doesn't support attribute assignment") + + def is_absolute(self): + """Is the most significant label of this name the root label? + @rtype: bool + """ + + return len(self.labels) > 0 and self.labels[-1] == '' + + def is_wild(self): + """Is this name wild? (I.e. Is the least significant label '*'?) + @rtype: bool + """ + + return len(self.labels) > 0 and self.labels[0] == '*' + + def __hash__(self): + """Return a case-insensitive hash of the name. + @rtype: int + """ + + h = 0L + for label in self.labels: + for c in label: + h += ( h << 3 ) + ord(c.lower()) + return int(h % sys.maxint) + + def fullcompare(self, other): + """Compare two names, returning a 3-tuple (relation, order, nlabels). + + I{relation} describes the relation ship beween the names, + and is one of: dns.name.NAMERELN_NONE, + dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN, + dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR + + I{order} is < 0 if self < other, > 0 if self > other, and == + 0 if self == other. A relative name is always less than an + absolute name. If both names have the same relativity, then + the DNSSEC order relation is used to order them. + + I{nlabels} is the number of significant labels that the two names + have in common. + """ + + sabs = self.is_absolute() + oabs = other.is_absolute() + if sabs != oabs: + if sabs: + return (NAMERELN_NONE, 1, 0) + else: + return (NAMERELN_NONE, -1, 0) + l1 = len(self.labels) + l2 = len(other.labels) + ldiff = l1 - l2 + if ldiff < 0: + l = l1 + else: + l = l2 + + order = 0 + nlabels = 0 + namereln = NAMERELN_NONE + while l > 0: + l -= 1 + l1 -= 1 + l2 -= 1 + label1 = self.labels[l1].lower() + label2 = other.labels[l2].lower() + if label1 < label2: + order = -1 + if nlabels > 0: + namereln = NAMERELN_COMMONANCESTOR + return (namereln, order, nlabels) + elif label1 > label2: + order = 1 + if nlabels > 0: + namereln = NAMERELN_COMMONANCESTOR + return (namereln, order, nlabels) + nlabels += 1 + order = ldiff + if ldiff < 0: + namereln = NAMERELN_SUPERDOMAIN + elif ldiff > 0: + namereln = NAMERELN_SUBDOMAIN + else: + namereln = NAMERELN_EQUAL + return (namereln, order, nlabels) + + def is_subdomain(self, other): + """Is self a subdomain of other? + + The notion of subdomain includes equality. + @rtype: bool + """ + + (nr, o, nl) = self.fullcompare(other) + if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL: + return True + return False + + def is_superdomain(self, other): + """Is self a superdomain of other? + + The notion of subdomain includes equality. + @rtype: bool + """ + + (nr, o, nl) = self.fullcompare(other) + if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL: + return True + return False + + def canonicalize(self): + """Return a name which is equal to the current name, but is in + DNSSEC canonical form. + @rtype: dns.name.Name object + """ + + return Name([x.lower() for x in self.labels]) + + def __eq__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] == 0 + else: + return False + + def __ne__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] != 0 + else: + return True + + def __lt__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] < 0 + else: + return NotImplemented + + def __le__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] <= 0 + else: + return NotImplemented + + def __ge__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] >= 0 + else: + return NotImplemented + + def __gt__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] > 0 + else: + return NotImplemented + + def __repr__(self): + return '' + + def __str__(self): + return self.to_text(False) + + def to_text(self, omit_final_dot = False): + """Convert name to text format. + @param omit_final_dot: If True, don't emit the final dot (denoting the + root label) for absolute names. The default is False. + @rtype: string + """ + + if len(self.labels) == 0: + return '@' + if len(self.labels) == 1 and self.labels[0] == '': + return '.' + if omit_final_dot and self.is_absolute(): + l = self.labels[:-1] + else: + l = self.labels + s = '.'.join(map(_escapify, l)) + return s + + def to_unicode(self, omit_final_dot = False): + """Convert name to Unicode text format. + + IDN ACE lables are converted to Unicode. + + @param omit_final_dot: If True, don't emit the final dot (denoting the + root label) for absolute names. The default is False. + @rtype: string + """ + + if len(self.labels) == 0: + return u'@' + if len(self.labels) == 1 and self.labels[0] == '': + return u'.' + if omit_final_dot and self.is_absolute(): + l = self.labels[:-1] + else: + l = self.labels + s = u'.'.join([encodings.idna.ToUnicode(_escapify(x)) for x in l]) + return s + + def to_digestable(self, origin=None): + """Convert name to a format suitable for digesting in hashes. + + The name is canonicalized and converted to uncompressed wire format. + + @param origin: If the name is relative and origin is not None, then + origin will be appended to it. + @type origin: dns.name.Name object + @raises NeedAbsoluteNameOrOrigin: All names in wire format are + absolute. If self is a relative name, then an origin must be supplied; + if it is missing, then this exception is raised + @rtype: string + """ + + if not self.is_absolute(): + if origin is None or not origin.is_absolute(): + raise NeedAbsoluteNameOrOrigin + labels = list(self.labels) + labels.extend(list(origin.labels)) + else: + labels = self.labels + dlabels = ["%s%s" % (chr(len(x)), x.lower()) for x in labels] + return ''.join(dlabels) + + def to_wire(self, file = None, compress = None, origin = None): + """Convert name to wire format, possibly compressing it. + + @param file: the file where the name is emitted (typically + a cStringIO file). If None, a string containing the wire name + will be returned. + @type file: file or None + @param compress: The compression table. If None (the default) names + will not be compressed. + @type compress: dict + @param origin: If the name is relative and origin is not None, then + origin will be appended to it. + @type origin: dns.name.Name object + @raises NeedAbsoluteNameOrOrigin: All names in wire format are + absolute. If self is a relative name, then an origin must be supplied; + if it is missing, then this exception is raised + """ + + if file is None: + file = cStringIO.StringIO() + want_return = True + else: + want_return = False + + if not self.is_absolute(): + if origin is None or not origin.is_absolute(): + raise NeedAbsoluteNameOrOrigin + labels = list(self.labels) + labels.extend(list(origin.labels)) + else: + labels = self.labels + i = 0 + for label in labels: + n = Name(labels[i:]) + i += 1 + if not compress is None: + pos = compress.get(n) + else: + pos = None + if not pos is None: + value = 0xc000 + pos + s = struct.pack('!H', value) + file.write(s) + break + else: + if not compress is None and len(n) > 1: + pos = file.tell() + if pos < 0xc000: + compress[n] = pos + l = len(label) + file.write(chr(l)) + if l > 0: + file.write(label) + if want_return: + return file.getvalue() + + def __len__(self): + """The length of the name (in labels). + @rtype: int + """ + + return len(self.labels) + + def __getitem__(self, index): + return self.labels[index] + + def __getslice__(self, start, stop): + return self.labels[start:stop] + + def __add__(self, other): + return self.concatenate(other) + + def __sub__(self, other): + return self.relativize(other) + + def split(self, depth): + """Split a name into a prefix and suffix at depth. + + @param depth: the number of labels in the suffix + @type depth: int + @raises ValueError: the depth was not >= 0 and <= the length of the + name. + @returns: the tuple (prefix, suffix) + @rtype: tuple + """ + + l = len(self.labels) + if depth == 0: + return (self, dns.name.empty) + elif depth == l: + return (dns.name.empty, self) + elif depth < 0 or depth > l: + raise ValueError('depth must be >= 0 and <= the length of the name') + return (Name(self[: -depth]), Name(self[-depth :])) + + def concatenate(self, other): + """Return a new name which is the concatenation of self and other. + @rtype: dns.name.Name object + @raises AbsoluteConcatenation: self is absolute and other is + not the empty name + """ + + if self.is_absolute() and len(other) > 0: + raise AbsoluteConcatenation + labels = list(self.labels) + labels.extend(list(other.labels)) + return Name(labels) + + def relativize(self, origin): + """If self is a subdomain of origin, return a new name which is self + relative to origin. Otherwise return self. + @rtype: dns.name.Name object + """ + + if not origin is None and self.is_subdomain(origin): + return Name(self[: -len(origin)]) + else: + return self + + def derelativize(self, origin): + """If self is a relative name, return a new name which is the + concatenation of self and origin. Otherwise return self. + @rtype: dns.name.Name object + """ + + if not self.is_absolute(): + return self.concatenate(origin) + else: + return self + + def choose_relativity(self, origin=None, relativize=True): + """Return a name with the relativity desired by the caller. If + origin is None, then self is returned. Otherwise, if + relativize is true the name is relativized, and if relativize is + false the name is derelativized. + @rtype: dns.name.Name object + """ + + if origin: + if relativize: + return self.relativize(origin) + else: + return self.derelativize(origin) + else: + return self + + def parent(self): + """Return the parent of the name. + @rtype: dns.name.Name object + @raises NoParent: the name is either the root name or the empty name, + and thus has no parent. + """ + if self == root or self == empty: + raise NoParent + return Name(self.labels[1:]) + +root = Name(['']) +empty = Name([]) + +def from_unicode(text, origin = root): + """Convert unicode text into a Name object. + + Lables are encoded in IDN ACE form. + + @rtype: dns.name.Name object + """ + + if not isinstance(text, unicode): + raise ValueError("input to from_unicode() must be a unicode string") + if not (origin is None or isinstance(origin, Name)): + raise ValueError("origin must be a Name or None") + labels = [] + label = u'' + escaping = False + edigits = 0 + total = 0 + if text == u'@': + text = u'' + if text: + if text == u'.': + return Name(['']) # no Unicode "u" on this constant! + for c in text: + if escaping: + if edigits == 0: + if c.isdigit(): + total = int(c) + edigits += 1 + else: + label += c + escaping = False + else: + if not c.isdigit(): + raise BadEscape + total *= 10 + total += int(c) + edigits += 1 + if edigits == 3: + escaping = False + label += chr(total) + elif c == u'.' or c == u'\u3002' or \ + c == u'\uff0e' or c == u'\uff61': + if len(label) == 0: + raise EmptyLabel + labels.append(encodings.idna.ToASCII(label)) + label = u'' + elif c == u'\\': + escaping = True + edigits = 0 + total = 0 + else: + label += c + if escaping: + raise BadEscape + if len(label) > 0: + labels.append(encodings.idna.ToASCII(label)) + else: + labels.append('') + if (len(labels) == 0 or labels[-1] != '') and not origin is None: + labels.extend(list(origin.labels)) + return Name(labels) + +def from_text(text, origin = root): + """Convert text into a Name object. + @rtype: dns.name.Name object + """ + + if not isinstance(text, str): + if isinstance(text, unicode) and sys.hexversion >= 0x02030000: + return from_unicode(text, origin) + else: + raise ValueError("input to from_text() must be a string") + if not (origin is None or isinstance(origin, Name)): + raise ValueError("origin must be a Name or None") + labels = [] + label = '' + escaping = False + edigits = 0 + total = 0 + if text == '@': + text = '' + if text: + if text == '.': + return Name(['']) + for c in text: + if escaping: + if edigits == 0: + if c.isdigit(): + total = int(c) + edigits += 1 + else: + label += c + escaping = False + else: + if not c.isdigit(): + raise BadEscape + total *= 10 + total += int(c) + edigits += 1 + if edigits == 3: + escaping = False + label += chr(total) + elif c == '.': + if len(label) == 0: + raise EmptyLabel + labels.append(label) + label = '' + elif c == '\\': + escaping = True + edigits = 0 + total = 0 + else: + label += c + if escaping: + raise BadEscape + if len(label) > 0: + labels.append(label) + else: + labels.append('') + if (len(labels) == 0 or labels[-1] != '') and not origin is None: + labels.extend(list(origin.labels)) + return Name(labels) + +def from_wire(message, current): + """Convert possibly compressed wire format into a Name. + @param message: the entire DNS message + @type message: string + @param current: the offset of the beginning of the name from the start + of the message + @type current: int + @raises dns.name.BadPointer: a compression pointer did not point backwards + in the message + @raises dns.name.BadLabelType: an invalid label type was encountered. + @returns: a tuple consisting of the name that was read and the number + of bytes of the wire format message which were consumed reading it + @rtype: (dns.name.Name object, int) tuple + """ + + if not isinstance(message, str): + raise ValueError("input to from_wire() must be a byte string") + message = dns.wiredata.maybe_wrap(message) + labels = [] + biggest_pointer = current + hops = 0 + count = ord(message[current]) + current += 1 + cused = 1 + while count != 0: + if count < 64: + labels.append(message[current : current + count].unwrap()) + current += count + if hops == 0: + cused += count + elif count >= 192: + current = (count & 0x3f) * 256 + ord(message[current]) + if hops == 0: + cused += 1 + if current >= biggest_pointer: + raise BadPointer + biggest_pointer = current + hops += 1 + else: + raise BadLabelType + count = ord(message[current]) + current += 1 + if hops == 0: + cused += 1 + labels.append('') + return (Name(labels), cused) diff --git a/third_party/dnspython/dns/namedict.py b/third_party/dnspython/dns/namedict.py new file mode 100644 index 0000000000..9f5a0efb7c --- /dev/null +++ b/third_party/dnspython/dns/namedict.py @@ -0,0 +1,59 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS name dictionary""" + +import dns.name + +class NameDict(dict): + + """A dictionary whose keys are dns.name.Name objects. + @ivar max_depth: the maximum depth of the keys that have ever been + added to the dictionary. + @type max_depth: int + """ + + def __init__(self, *args, **kwargs): + super(NameDict, self).__init__(*args, **kwargs) + self.max_depth = 0 + + def __setitem__(self, key, value): + if not isinstance(key, dns.name.Name): + raise ValueError('NameDict key must be a name') + depth = len(key) + if depth > self.max_depth: + self.max_depth = depth + super(NameDict, self).__setitem__(key, value) + + def get_deepest_match(self, name): + """Find the deepest match to I{name} in the dictionary. + + The deepest match is the longest name in the dictionary which is + a superdomain of I{name}. + + @param name: the name + @type name: dns.name.Name object + @rtype: (key, value) tuple + """ + + depth = len(name) + if depth > self.max_depth: + depth = self.max_depth + for i in xrange(-depth, 0): + n = dns.name.Name(name[i:]) + if self.has_key(n): + return (n, self[n]) + v = self[dns.name.empty] + return (dns.name.empty, v) diff --git a/third_party/dnspython/dns/node.py b/third_party/dnspython/dns/node.py new file mode 100644 index 0000000000..7625c66d3a --- /dev/null +++ b/third_party/dnspython/dns/node.py @@ -0,0 +1,172 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS nodes. A node is a set of rdatasets.""" + +import StringIO + +import dns.rdataset +import dns.rdatatype +import dns.renderer + +class Node(object): + """A DNS node. + + A node is a set of rdatasets + + @ivar rdatasets: the node's rdatasets + @type rdatasets: list of dns.rdataset.Rdataset objects""" + + __slots__ = ['rdatasets'] + + def __init__(self): + """Initialize a DNS node. + """ + + self.rdatasets = []; + + def to_text(self, name, **kw): + """Convert a node to text format. + + Each rdataset at the node is printed. Any keyword arguments + to this method are passed on to the rdataset's to_text() method. + @param name: the owner name of the rdatasets + @type name: dns.name.Name object + @rtype: string + """ + + s = StringIO.StringIO() + for rds in self.rdatasets: + print >> s, rds.to_text(name, **kw) + return s.getvalue()[:-1] + + def __repr__(self): + return '' + + def __eq__(self, other): + """Two nodes are equal if they have the same rdatasets. + + @rtype: bool + """ + # + # This is inefficient. Good thing we don't need to do it much. + # + for rd in self.rdatasets: + if rd not in other.rdatasets: + return False + for rd in other.rdatasets: + if rd not in self.rdatasets: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def __len__(self): + return len(self.rdatasets) + + def __iter__(self): + return iter(self.rdatasets) + + def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE, + create=False): + """Find an rdataset matching the specified properties in the + current node. + + @param rdclass: The class of the rdataset + @type rdclass: int + @param rdtype: The type of the rdataset + @type rdtype: int + @param covers: The covered type. Usually this value is + dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or + dns.rdatatype.RRSIG, then the covers value will be the rdata + type the SIG/RRSIG covers. The library treats the SIG and RRSIG + types as if they were a family of + types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much + easier to work with than if RRSIGs covering different rdata + types were aggregated into a single RRSIG rdataset. + @type covers: int + @param create: If True, create the rdataset if it is not found. + @type create: bool + @raises KeyError: An rdataset of the desired type and class does + not exist and I{create} is not True. + @rtype: dns.rdataset.Rdataset object + """ + + for rds in self.rdatasets: + if rds.match(rdclass, rdtype, covers): + return rds + if not create: + raise KeyError + rds = dns.rdataset.Rdataset(rdclass, rdtype) + self.rdatasets.append(rds) + return rds + + def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE, + create=False): + """Get an rdataset matching the specified properties in the + current node. + + None is returned if an rdataset of the specified type and + class does not exist and I{create} is not True. + + @param rdclass: The class of the rdataset + @type rdclass: int + @param rdtype: The type of the rdataset + @type rdtype: int + @param covers: The covered type. + @type covers: int + @param create: If True, create the rdataset if it is not found. + @type create: bool + @rtype: dns.rdataset.Rdataset object or None + """ + + try: + rds = self.find_rdataset(rdclass, rdtype, covers, create) + except KeyError: + rds = None + return rds + + def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE): + """Delete the rdataset matching the specified properties in the + current node. + + If a matching rdataset does not exist, it is not an error. + + @param rdclass: The class of the rdataset + @type rdclass: int + @param rdtype: The type of the rdataset + @type rdtype: int + @param covers: The covered type. + @type covers: int + """ + + rds = self.get_rdataset(rdclass, rdtype, covers) + if not rds is None: + self.rdatasets.remove(rds) + + def replace_rdataset(self, replacement): + """Replace an rdataset. + + It is not an error if there is no rdataset matching I{replacement}. + + Ownership of the I{replacement} object is transferred to the node; + in other words, this method does not store a copy of I{replacement} + at the node, it stores I{replacement} itself. + """ + + self.delete_rdataset(replacement.rdclass, replacement.rdtype, + replacement.covers) + self.rdatasets.append(replacement) diff --git a/third_party/dnspython/dns/opcode.py b/third_party/dnspython/dns/opcode.py new file mode 100644 index 0000000000..3258c34cd9 --- /dev/null +++ b/third_party/dnspython/dns/opcode.py @@ -0,0 +1,104 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Opcodes.""" + +import dns.exception + +QUERY = 0 +IQUERY = 1 +STATUS = 2 +NOTIFY = 4 +UPDATE = 5 + +_by_text = { + 'QUERY' : QUERY, + 'IQUERY' : IQUERY, + 'STATUS' : STATUS, + 'NOTIFY' : NOTIFY, + 'UPDATE' : UPDATE +} + +# We construct the inverse mapping programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mapping not to be true inverse. + +_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) + + +class UnknownOpcode(dns.exception.DNSException): + """Raised if an opcode is unknown.""" + pass + +def from_text(text): + """Convert text into an opcode. + + @param text: the textual opcode + @type text: string + @raises UnknownOpcode: the opcode is unknown + @rtype: int + """ + + if text.isdigit(): + value = int(text) + if value >= 0 and value <= 15: + return value + value = _by_text.get(text.upper()) + if value is None: + raise UnknownOpcode + return value + +def from_flags(flags): + """Extract an opcode from DNS message flags. + + @param flags: int + @rtype: int + """ + + return (flags & 0x7800) >> 11 + +def to_flags(value): + """Convert an opcode to a value suitable for ORing into DNS message + flags. + @rtype: int + """ + + return (value << 11) & 0x7800 + +def to_text(value): + """Convert an opcode to text. + + @param value: the opcdoe + @type value: int + @raises UnknownOpcode: the opcode is unknown + @rtype: string + """ + + text = _by_value.get(value) + if text is None: + text = str(value) + return text + +def is_update(flags): + """True if the opcode in flags is UPDATE. + + @param flags: DNS flags + @type flags: int + @rtype: bool + """ + + if (from_flags(flags) == UPDATE): + return True + return False diff --git a/third_party/dnspython/dns/query.py b/third_party/dnspython/dns/query.py new file mode 100644 index 0000000000..addee4e3f2 --- /dev/null +++ b/third_party/dnspython/dns/query.py @@ -0,0 +1,492 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Talk to a DNS server.""" + +from __future__ import generators + +import errno +import select +import socket +import struct +import sys +import time + +import dns.exception +import dns.inet +import dns.name +import dns.message +import dns.rdataclass +import dns.rdatatype + +class UnexpectedSource(dns.exception.DNSException): + """Raised if a query response comes from an unexpected address or port.""" + pass + +class BadResponse(dns.exception.FormError): + """Raised if a query response does not respond to the question asked.""" + pass + +def _compute_expiration(timeout): + if timeout is None: + return None + else: + return time.time() + timeout + +def _poll_for(fd, readable, writable, error, timeout): + """ + @param fd: File descriptor (int). + @param readable: Whether to wait for readability (bool). + @param writable: Whether to wait for writability (bool). + @param expiration: Deadline timeout (expiration time, in seconds (float)). + + @return True on success, False on timeout + """ + event_mask = 0 + if readable: + event_mask |= select.POLLIN + if writable: + event_mask |= select.POLLOUT + if error: + event_mask |= select.POLLERR + + pollable = select.poll() + pollable.register(fd, event_mask) + + if timeout: + event_list = pollable.poll(long(timeout * 1000)) + else: + event_list = pollable.poll() + + return bool(event_list) + +def _select_for(fd, readable, writable, error, timeout): + """ + @param fd: File descriptor (int). + @param readable: Whether to wait for readability (bool). + @param writable: Whether to wait for writability (bool). + @param expiration: Deadline timeout (expiration time, in seconds (float)). + + @return True on success, False on timeout + """ + rset, wset, xset = [], [], [] + + if readable: + rset = [fd] + if writable: + wset = [fd] + if error: + xset = [fd] + + if timeout is None: + (rcount, wcount, xcount) = select.select(rset, wset, xset) + else: + (rcount, wcount, xcount) = select.select(rset, wset, xset, timeout) + + return bool((rcount or wcount or xcount)) + +def _wait_for(fd, readable, writable, error, expiration): + done = False + while not done: + if expiration is None: + timeout = None + else: + timeout = expiration - time.time() + if timeout <= 0.0: + raise dns.exception.Timeout + try: + if not _polling_backend(fd, readable, writable, error, timeout): + raise dns.exception.Timeout + except select.error, e: + if e.args[0] != errno.EINTR: + raise e + done = True + +def _set_polling_backend(fn): + """ + Internal API. Do not use. + """ + global _polling_backend + + _polling_backend = fn + +if hasattr(select, 'poll'): + # Prefer poll() on platforms that support it because it has no + # limits on the maximum value of a file descriptor (plus it will + # be more efficient for high values). + _polling_backend = _poll_for +else: + _polling_backend = _select_for + +def _wait_for_readable(s, expiration): + _wait_for(s, True, False, True, expiration) + +def _wait_for_writable(s, expiration): + _wait_for(s, False, True, True, expiration) + +def _addresses_equal(af, a1, a2): + # Convert the first value of the tuple, which is a textual format + # address into binary form, so that we are not confused by different + # textual representations of the same address + n1 = dns.inet.inet_pton(af, a1[0]) + n2 = dns.inet.inet_pton(af, a2[0]) + return n1 == n2 and a1[1:] == a2[1:] + +def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, + ignore_unexpected=False, one_rr_per_rrset=False): + """Return the response obtained after sending a query via UDP. + + @param q: the query + @type q: dns.message.Message + @param where: where to send the message + @type where: string containing an IPv4 or IPv6 address + @param timeout: The number of seconds to wait before the query times out. + If None, the default, wait forever. + @type timeout: float + @param port: The port to which to send the message. The default is 53. + @type port: int + @param af: the address family to use. The default is None, which + causes the address family to use to be inferred from the form of of where. + If the inference attempt fails, AF_INET is used. + @type af: int + @rtype: dns.message.Message object + @param source: source address. The default is the IPv4 wildcard address. + @type source: string + @param source_port: The port from which to send the message. + The default is 0. + @type source_port: int + @param ignore_unexpected: If True, ignore responses from unexpected + sources. The default is False. + @type ignore_unexpected: bool + @param one_rr_per_rrset: Put each RR into its own RRset + @type one_rr_per_rrset: bool + """ + + wire = q.to_wire() + if af is None: + try: + af = dns.inet.af_for_address(where) + except: + af = dns.inet.AF_INET + if af == dns.inet.AF_INET: + destination = (where, port) + if source is not None: + source = (source, source_port) + elif af == dns.inet.AF_INET6: + destination = (where, port, 0, 0) + if source is not None: + source = (source, source_port, 0, 0) + s = socket.socket(af, socket.SOCK_DGRAM, 0) + try: + expiration = _compute_expiration(timeout) + s.setblocking(0) + if source is not None: + s.bind(source) + _wait_for_writable(s, expiration) + s.sendto(wire, destination) + while 1: + _wait_for_readable(s, expiration) + (wire, from_address) = s.recvfrom(65535) + if _addresses_equal(af, from_address, destination) or \ + (dns.inet.is_multicast(where) and \ + from_address[1:] == destination[1:]): + break + if not ignore_unexpected: + raise UnexpectedSource('got a response from ' + '%s instead of %s' % (from_address, + destination)) + finally: + s.close() + r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, + one_rr_per_rrset=one_rr_per_rrset) + if not q.is_response(r): + raise BadResponse + return r + +def _net_read(sock, count, expiration): + """Read the specified number of bytes from sock. Keep trying until we + either get the desired amount, or we hit EOF. + A Timeout exception will be raised if the operation is not completed + by the expiration time. + """ + s = '' + while count > 0: + _wait_for_readable(sock, expiration) + n = sock.recv(count) + if n == '': + raise EOFError + count = count - len(n) + s = s + n + return s + +def _net_write(sock, data, expiration): + """Write the specified data to the socket. + A Timeout exception will be raised if the operation is not completed + by the expiration time. + """ + current = 0 + l = len(data) + while current < l: + _wait_for_writable(sock, expiration) + current += sock.send(data[current:]) + +def _connect(s, address): + try: + s.connect(address) + except socket.error: + (ty, v) = sys.exc_info()[:2] + if v[0] != errno.EINPROGRESS and \ + v[0] != errno.EWOULDBLOCK and \ + v[0] != errno.EALREADY: + raise v + +def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, + one_rr_per_rrset=False): + """Return the response obtained after sending a query via TCP. + + @param q: the query + @type q: dns.message.Message object + @param where: where to send the message + @type where: string containing an IPv4 or IPv6 address + @param timeout: The number of seconds to wait before the query times out. + If None, the default, wait forever. + @type timeout: float + @param port: The port to which to send the message. The default is 53. + @type port: int + @param af: the address family to use. The default is None, which + causes the address family to use to be inferred from the form of of where. + If the inference attempt fails, AF_INET is used. + @type af: int + @rtype: dns.message.Message object + @param source: source address. The default is the IPv4 wildcard address. + @type source: string + @param source_port: The port from which to send the message. + The default is 0. + @type source_port: int + @param one_rr_per_rrset: Put each RR into its own RRset + @type one_rr_per_rrset: bool + """ + + wire = q.to_wire() + if af is None: + try: + af = dns.inet.af_for_address(where) + except: + af = dns.inet.AF_INET + if af == dns.inet.AF_INET: + destination = (where, port) + if source is not None: + source = (source, source_port) + elif af == dns.inet.AF_INET6: + destination = (where, port, 0, 0) + if source is not None: + source = (source, source_port, 0, 0) + s = socket.socket(af, socket.SOCK_STREAM, 0) + try: + expiration = _compute_expiration(timeout) + s.setblocking(0) + if source is not None: + s.bind(source) + _connect(s, destination) + + l = len(wire) + + # copying the wire into tcpmsg is inefficient, but lets us + # avoid writev() or doing a short write that would get pushed + # onto the net + tcpmsg = struct.pack("!H", l) + wire + _net_write(s, tcpmsg, expiration) + ldata = _net_read(s, 2, expiration) + (l,) = struct.unpack("!H", ldata) + wire = _net_read(s, l, expiration) + finally: + s.close() + r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, + one_rr_per_rrset=one_rr_per_rrset) + if not q.is_response(r): + raise BadResponse + return r + +def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, + timeout=None, port=53, keyring=None, keyname=None, relativize=True, + af=None, lifetime=None, source=None, source_port=0, serial=0, + use_udp=False, keyalgorithm=dns.tsig.default_algorithm): + """Return a generator for the responses to a zone transfer. + + @param where: where to send the message + @type where: string containing an IPv4 or IPv6 address + @param zone: The name of the zone to transfer + @type zone: dns.name.Name object or string + @param rdtype: The type of zone transfer. The default is + dns.rdatatype.AXFR. + @type rdtype: int or string + @param rdclass: The class of the zone transfer. The default is + dns.rdatatype.IN. + @type rdclass: int or string + @param timeout: The number of seconds to wait for each response message. + If None, the default, wait forever. + @type timeout: float + @param port: The port to which to send the message. The default is 53. + @type port: int + @param keyring: The TSIG keyring to use + @type keyring: dict + @param keyname: The name of the TSIG key to use + @type keyname: dns.name.Name object or string + @param relativize: If True, all names in the zone will be relativized to + the zone origin. It is essential that the relativize setting matches + the one specified to dns.zone.from_xfr(). + @type relativize: bool + @param af: the address family to use. The default is None, which + causes the address family to use to be inferred from the form of of where. + If the inference attempt fails, AF_INET is used. + @type af: int + @param lifetime: The total number of seconds to spend doing the transfer. + If None, the default, then there is no limit on the time the transfer may + take. + @type lifetime: float + @rtype: generator of dns.message.Message objects. + @param source: source address. The default is the IPv4 wildcard address. + @type source: string + @param source_port: The port from which to send the message. + The default is 0. + @type source_port: int + @param serial: The SOA serial number to use as the base for an IXFR diff + sequence (only meaningful if rdtype == dns.rdatatype.IXFR). + @type serial: int + @param use_udp: Use UDP (only meaningful for IXFR) + @type use_udp: bool + @param keyalgorithm: The TSIG algorithm to use; defaults to + dns.tsig.default_algorithm + @type keyalgorithm: string + """ + + if isinstance(zone, (str, unicode)): + zone = dns.name.from_text(zone) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + q = dns.message.make_query(zone, rdtype, rdclass) + if rdtype == dns.rdatatype.IXFR: + rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA', + '. . %u 0 0 0 0' % serial) + q.authority.append(rrset) + if not keyring is None: + q.use_tsig(keyring, keyname, algorithm=keyalgorithm) + wire = q.to_wire() + if af is None: + try: + af = dns.inet.af_for_address(where) + except: + af = dns.inet.AF_INET + if af == dns.inet.AF_INET: + destination = (where, port) + if source is not None: + source = (source, source_port) + elif af == dns.inet.AF_INET6: + destination = (where, port, 0, 0) + if source is not None: + source = (source, source_port, 0, 0) + if use_udp: + if rdtype != dns.rdatatype.IXFR: + raise ValueError('cannot do a UDP AXFR') + s = socket.socket(af, socket.SOCK_DGRAM, 0) + else: + s = socket.socket(af, socket.SOCK_STREAM, 0) + s.setblocking(0) + if source is not None: + s.bind(source) + expiration = _compute_expiration(lifetime) + _connect(s, destination) + l = len(wire) + if use_udp: + _wait_for_writable(s, expiration) + s.send(wire) + else: + tcpmsg = struct.pack("!H", l) + wire + _net_write(s, tcpmsg, expiration) + done = False + soa_rrset = None + soa_count = 0 + if relativize: + origin = zone + oname = dns.name.empty + else: + origin = None + oname = zone + tsig_ctx = None + first = True + while not done: + mexpiration = _compute_expiration(timeout) + if mexpiration is None or mexpiration > expiration: + mexpiration = expiration + if use_udp: + _wait_for_readable(s, expiration) + (wire, from_address) = s.recvfrom(65535) + else: + ldata = _net_read(s, 2, mexpiration) + (l,) = struct.unpack("!H", ldata) + wire = _net_read(s, l, mexpiration) + r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, + xfr=True, origin=origin, tsig_ctx=tsig_ctx, + multi=True, first=first, + one_rr_per_rrset=(rdtype==dns.rdatatype.IXFR)) + tsig_ctx = r.tsig_ctx + first = False + answer_index = 0 + delete_mode = False + expecting_SOA = False + if soa_rrset is None: + if not r.answer or r.answer[0].name != oname: + raise dns.exception.FormError + rrset = r.answer[0] + if rrset.rdtype != dns.rdatatype.SOA: + raise dns.exception.FormError("first RRset is not an SOA") + answer_index = 1 + soa_rrset = rrset.copy() + if rdtype == dns.rdatatype.IXFR: + if soa_rrset[0].serial == serial: + # + # We're already up-to-date. + # + done = True + else: + expecting_SOA = True + # + # Process SOAs in the answer section (other than the initial + # SOA in the first message). + # + for rrset in r.answer[answer_index:]: + if done: + raise dns.exception.FormError("answers after final SOA") + if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: + if expecting_SOA: + if rrset[0].serial != serial: + raise dns.exception.FormError("IXFR base serial mismatch") + expecting_SOA = False + elif rdtype == dns.rdatatype.IXFR: + delete_mode = not delete_mode + if rrset == soa_rrset and not delete_mode: + done = True + elif expecting_SOA: + # + # We made an IXFR request and are expecting another + # SOA RR, but saw something else, so this must be an + # AXFR response. + # + rdtype = dns.rdatatype.AXFR + expecting_SOA = False + if done and q.keyring and not r.had_tsig: + raise dns.exception.FormError("missing TSIG") + yield r + s.close() diff --git a/third_party/dnspython/dns/rcode.py b/third_party/dnspython/dns/rcode.py new file mode 100644 index 0000000000..7807782aa4 --- /dev/null +++ b/third_party/dnspython/dns/rcode.py @@ -0,0 +1,119 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Result Codes.""" + +import dns.exception + +NOERROR = 0 +FORMERR = 1 +SERVFAIL = 2 +NXDOMAIN = 3 +NOTIMP = 4 +REFUSED = 5 +YXDOMAIN = 6 +YXRRSET = 7 +NXRRSET = 8 +NOTAUTH = 9 +NOTZONE = 10 +BADVERS = 16 + +_by_text = { + 'NOERROR' : NOERROR, + 'FORMERR' : FORMERR, + 'SERVFAIL' : SERVFAIL, + 'NXDOMAIN' : NXDOMAIN, + 'NOTIMP' : NOTIMP, + 'REFUSED' : REFUSED, + 'YXDOMAIN' : YXDOMAIN, + 'YXRRSET' : YXRRSET, + 'NXRRSET' : NXRRSET, + 'NOTAUTH' : NOTAUTH, + 'NOTZONE' : NOTZONE, + 'BADVERS' : BADVERS +} + +# We construct the inverse mapping programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mapping not to be a true inverse. + +_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) + + +class UnknownRcode(dns.exception.DNSException): + """Raised if an rcode is unknown.""" + pass + +def from_text(text): + """Convert text into an rcode. + + @param text: the texual rcode + @type text: string + @raises UnknownRcode: the rcode is unknown + @rtype: int + """ + + if text.isdigit(): + v = int(text) + if v >= 0 and v <= 4095: + return v + v = _by_text.get(text.upper()) + if v is None: + raise UnknownRcode + return v + +def from_flags(flags, ednsflags): + """Return the rcode value encoded by flags and ednsflags. + + @param flags: the DNS flags + @type flags: int + @param ednsflags: the EDNS flags + @type ednsflags: int + @raises ValueError: rcode is < 0 or > 4095 + @rtype: int + """ + + value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0) + if value < 0 or value > 4095: + raise ValueError('rcode must be >= 0 and <= 4095') + return value + +def to_flags(value): + """Return a (flags, ednsflags) tuple which encodes the rcode. + + @param value: the rcode + @type value: int + @raises ValueError: rcode is < 0 or > 4095 + @rtype: (int, int) tuple + """ + + if value < 0 or value > 4095: + raise ValueError('rcode must be >= 0 and <= 4095') + v = value & 0xf + ev = long(value & 0xff0) << 20 + return (v, ev) + +def to_text(value): + """Convert rcode into text. + + @param value: the rcode + @type value: int + @rtype: string + """ + + text = _by_value.get(value) + if text is None: + text = str(value) + return text diff --git a/third_party/dnspython/dns/rdata.py b/third_party/dnspython/dns/rdata.py new file mode 100644 index 0000000000..350bf790b8 --- /dev/null +++ b/third_party/dnspython/dns/rdata.py @@ -0,0 +1,478 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS rdata. + +@var _rdata_modules: A dictionary mapping a (rdclass, rdtype) tuple to +the module which implements that type. +@type _rdata_modules: dict +@var _module_prefix: The prefix to use when forming modules names. The +default is 'dns.rdtypes'. Changing this value will break the library. +@type _module_prefix: string +@var _hex_chunk: At most this many octets that will be represented in each +chunk of hexstring that _hexify() produces before whitespace occurs. +@type _hex_chunk: int""" + +import cStringIO + +import dns.exception +import dns.name +import dns.rdataclass +import dns.rdatatype +import dns.tokenizer +import dns.wiredata + +_hex_chunksize = 32 + +def _hexify(data, chunksize=None): + """Convert a binary string into its hex encoding, broken up into chunks + of I{chunksize} characters separated by a space. + + @param data: the binary string + @type data: string + @param chunksize: the chunk size. Default is L{dns.rdata._hex_chunksize} + @rtype: string + """ + + if chunksize is None: + chunksize = _hex_chunksize + hex = data.encode('hex_codec') + l = len(hex) + if l > chunksize: + chunks = [] + i = 0 + while i < l: + chunks.append(hex[i : i + chunksize]) + i += chunksize + hex = ' '.join(chunks) + return hex + +_base64_chunksize = 32 + +def _base64ify(data, chunksize=None): + """Convert a binary string into its base64 encoding, broken up into chunks + of I{chunksize} characters separated by a space. + + @param data: the binary string + @type data: string + @param chunksize: the chunk size. Default is + L{dns.rdata._base64_chunksize} + @rtype: string + """ + + if chunksize is None: + chunksize = _base64_chunksize + b64 = data.encode('base64_codec') + b64 = b64.replace('\n', '') + l = len(b64) + if l > chunksize: + chunks = [] + i = 0 + while i < l: + chunks.append(b64[i : i + chunksize]) + i += chunksize + b64 = ' '.join(chunks) + return b64 + +__escaped = { + '"' : True, + '\\' : True, + } + +def _escapify(qstring): + """Escape the characters in a quoted string which need it. + + @param qstring: the string + @type qstring: string + @returns: the escaped string + @rtype: string + """ + + text = '' + for c in qstring: + if c in __escaped: + text += '\\' + c + elif ord(c) >= 0x20 and ord(c) < 0x7F: + text += c + else: + text += '\\%03d' % ord(c) + return text + +def _truncate_bitmap(what): + """Determine the index of greatest byte that isn't all zeros, and + return the bitmap that contains all the bytes less than that index. + + @param what: a string of octets representing a bitmap. + @type what: string + @rtype: string + """ + + for i in xrange(len(what) - 1, -1, -1): + if what[i] != '\x00': + break + return ''.join(what[0 : i + 1]) + +class Rdata(object): + """Base class for all DNS rdata types. + """ + + __slots__ = ['rdclass', 'rdtype'] + + def __init__(self, rdclass, rdtype): + """Initialize an rdata. + @param rdclass: The rdata class + @type rdclass: int + @param rdtype: The rdata type + @type rdtype: int + """ + + self.rdclass = rdclass + self.rdtype = rdtype + + def covers(self): + """DNS SIG/RRSIG rdatas apply to a specific type; this type is + returned by the covers() function. If the rdata type is not + SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when + creating rdatasets, allowing the rdataset to contain only RRSIGs + of a particular type, e.g. RRSIG(NS). + @rtype: int + """ + + return dns.rdatatype.NONE + + def extended_rdatatype(self): + """Return a 32-bit type value, the least significant 16 bits of + which are the ordinary DNS type, and the upper 16 bits of which are + the "covered" type, if any. + @rtype: int + """ + + return self.covers() << 16 | self.rdtype + + def to_text(self, origin=None, relativize=True, **kw): + """Convert an rdata to text format. + @rtype: string + """ + raise NotImplementedError + + def to_wire(self, file, compress = None, origin = None): + """Convert an rdata to wire format. + @rtype: string + """ + + raise NotImplementedError + + def to_digestable(self, origin = None): + """Convert rdata to a format suitable for digesting in hashes. This + is also the DNSSEC canonical form.""" + f = cStringIO.StringIO() + self.to_wire(f, None, origin) + return f.getvalue() + + def validate(self): + """Check that the current contents of the rdata's fields are + valid. If you change an rdata by assigning to its fields, + it is a good idea to call validate() when you are done making + changes. + """ + dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text()) + + def __repr__(self): + covers = self.covers() + if covers == dns.rdatatype.NONE: + ctext = '' + else: + ctext = '(' + dns.rdatatype.to_text(covers) + ')' + return '' + + def __str__(self): + return self.to_text() + + def _cmp(self, other): + """Compare an rdata with another rdata of the same rdtype and + rdclass. Return < 0 if self < other in the DNSSEC ordering, + 0 if self == other, and > 0 if self > other. + """ + + raise NotImplementedError + + def __eq__(self, other): + if not isinstance(other, Rdata): + return False + if self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype: + return False + return self._cmp(other) == 0 + + def __ne__(self, other): + if not isinstance(other, Rdata): + return True + if self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype: + return True + return self._cmp(other) != 0 + + def __lt__(self, other): + if not isinstance(other, Rdata) or \ + self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype: + return NotImplemented + return self._cmp(other) < 0 + + def __le__(self, other): + if not isinstance(other, Rdata) or \ + self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype: + return NotImplemented + return self._cmp(other) <= 0 + + def __ge__(self, other): + if not isinstance(other, Rdata) or \ + self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype: + return NotImplemented + return self._cmp(other) >= 0 + + def __gt__(self, other): + if not isinstance(other, Rdata) or \ + self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype: + return NotImplemented + return self._cmp(other) > 0 + + def __hash__(self): + return hash(self.to_digestable(dns.name.root)) + + def _wire_cmp(self, other): + # A number of types compare rdata in wire form, so we provide + # the method here instead of duplicating it. + # + # We specifiy an arbitrary origin of '.' when doing the + # comparison, since the rdata may have relative names and we + # can't convert a relative name to wire without an origin. + b1 = cStringIO.StringIO() + self.to_wire(b1, None, dns.name.root) + b2 = cStringIO.StringIO() + other.to_wire(b2, None, dns.name.root) + return cmp(b1.getvalue(), b2.getvalue()) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + """Build an rdata object from text format. + + @param rdclass: The rdata class + @type rdclass: int + @param rdtype: The rdata type + @type rdtype: int + @param tok: The tokenizer + @type tok: dns.tokenizer.Tokenizer + @param origin: The origin to use for relative names + @type origin: dns.name.Name + @param relativize: should names be relativized? + @type relativize: bool + @rtype: dns.rdata.Rdata instance + """ + + raise NotImplementedError + + from_text = classmethod(from_text) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + """Build an rdata object from wire format + + @param rdclass: The rdata class + @type rdclass: int + @param rdtype: The rdata type + @type rdtype: int + @param wire: The wire-format message + @type wire: string + @param current: The offet in wire of the beginning of the rdata. + @type current: int + @param rdlen: The length of the wire-format rdata + @type rdlen: int + @param origin: The origin to use for relative names + @type origin: dns.name.Name + @rtype: dns.rdata.Rdata instance + """ + + raise NotImplementedError + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + """Convert any domain names in the rdata to the specified + relativization. + """ + + pass + + +class GenericRdata(Rdata): + """Generate Rdata Class + + This class is used for rdata types for which we have no better + implementation. It implements the DNS "unknown RRs" scheme. + """ + + __slots__ = ['data'] + + def __init__(self, rdclass, rdtype, data): + super(GenericRdata, self).__init__(rdclass, rdtype) + self.data = data + + def to_text(self, origin=None, relativize=True, **kw): + return r'\# %d ' % len(self.data) + _hexify(self.data) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + token = tok.get() + if not token.is_identifier() or token.value != '\#': + raise dns.exception.SyntaxError(r'generic rdata does not start with \#') + length = tok.get_int() + chunks = [] + while 1: + token = tok.get() + if token.is_eol_or_eof(): + break + chunks.append(token.value) + hex = ''.join(chunks) + data = hex.decode('hex_codec') + if len(data) != length: + raise dns.exception.SyntaxError('generic rdata hex data has wrong length') + return cls(rdclass, rdtype, data) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + file.write(self.data) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + return cls(rdclass, rdtype, wire[current : current + rdlen]) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + return cmp(self.data, other.data) + +_rdata_modules = {} +_module_prefix = 'dns.rdtypes' + +def get_rdata_class(rdclass, rdtype): + + def import_module(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + mod = _rdata_modules.get((rdclass, rdtype)) + rdclass_text = dns.rdataclass.to_text(rdclass) + rdtype_text = dns.rdatatype.to_text(rdtype) + rdtype_text = rdtype_text.replace('-', '_') + if not mod: + mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype)) + if not mod: + try: + mod = import_module('.'.join([_module_prefix, + rdclass_text, rdtype_text])) + _rdata_modules[(rdclass, rdtype)] = mod + except ImportError: + try: + mod = import_module('.'.join([_module_prefix, + 'ANY', rdtype_text])) + _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod + except ImportError: + mod = None + if mod: + cls = getattr(mod, rdtype_text) + else: + cls = GenericRdata + return cls + +def from_text(rdclass, rdtype, tok, origin = None, relativize = True): + """Build an rdata object from text format. + + This function attempts to dynamically load a class which + implements the specified rdata class and type. If there is no + class-and-type-specific implementation, the GenericRdata class + is used. + + Once a class is chosen, its from_text() class method is called + with the parameters to this function. + + If I{tok} is a string, then a tokenizer is created and the string + is used as its input. + + @param rdclass: The rdata class + @type rdclass: int + @param rdtype: The rdata type + @type rdtype: int + @param tok: The tokenizer or input text + @type tok: dns.tokenizer.Tokenizer or string + @param origin: The origin to use for relative names + @type origin: dns.name.Name + @param relativize: Should names be relativized? + @type relativize: bool + @rtype: dns.rdata.Rdata instance""" + + if isinstance(tok, str): + tok = dns.tokenizer.Tokenizer(tok) + cls = get_rdata_class(rdclass, rdtype) + if cls != GenericRdata: + # peek at first token + token = tok.get() + tok.unget(token) + if token.is_identifier() and \ + token.value == r'\#': + # + # Known type using the generic syntax. Extract the + # wire form from the generic syntax, and then run + # from_wire on it. + # + rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin, + relativize) + return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data), + origin) + return cls.from_text(rdclass, rdtype, tok, origin, relativize) + +def from_wire(rdclass, rdtype, wire, current, rdlen, origin = None): + """Build an rdata object from wire format + + This function attempts to dynamically load a class which + implements the specified rdata class and type. If there is no + class-and-type-specific implementation, the GenericRdata class + is used. + + Once a class is chosen, its from_wire() class method is called + with the parameters to this function. + + @param rdclass: The rdata class + @type rdclass: int + @param rdtype: The rdata type + @type rdtype: int + @param wire: The wire-format message + @type wire: string + @param current: The offet in wire of the beginning of the rdata. + @type current: int + @param rdlen: The length of the wire-format rdata + @type rdlen: int + @param origin: The origin to use for relative names + @type origin: dns.name.Name + @rtype: dns.rdata.Rdata instance""" + + wire = dns.wiredata.maybe_wrap(wire) + cls = get_rdata_class(rdclass, rdtype) + return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin) diff --git a/third_party/dnspython/dns/rdataclass.py b/third_party/dnspython/dns/rdataclass.py new file mode 100644 index 0000000000..7601e70c3a --- /dev/null +++ b/third_party/dnspython/dns/rdataclass.py @@ -0,0 +1,114 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Rdata Classes. + +@var _by_text: The rdata class textual name to value mapping +@type _by_text: dict +@var _by_value: The rdata class value to textual name mapping +@type _by_value: dict +@var _metaclasses: If an rdataclass is a metaclass, there will be a mapping +whose key is the rdatatype value and whose value is True in this dictionary. +@type _metaclasses: dict""" + +import re + +import dns.exception + +RESERVED0 = 0 +IN = 1 +CH = 3 +HS = 4 +NONE = 254 +ANY = 255 + +_by_text = { + 'RESERVED0' : RESERVED0, + 'IN' : IN, + 'CH' : CH, + 'HS' : HS, + 'NONE' : NONE, + 'ANY' : ANY + } + +# We construct the inverse mapping programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mapping not to be true inverse. + +_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) + +# Now that we've built the inverse map, we can add class aliases to +# the _by_text mapping. + +_by_text.update({ + 'INTERNET' : IN, + 'CHAOS' : CH, + 'HESIOD' : HS + }) + +_metaclasses = { + NONE : True, + ANY : True + } + +_unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I); + +class UnknownRdataclass(dns.exception.DNSException): + """Raised when a class is unknown.""" + pass + +def from_text(text): + """Convert text into a DNS rdata class value. + @param text: the text + @type text: string + @rtype: int + @raises dns.rdataclass.UnknownRdataclass: the class is unknown + @raises ValueError: the rdata class value is not >= 0 and <= 65535 + """ + + value = _by_text.get(text.upper()) + if value is None: + match = _unknown_class_pattern.match(text) + if match == None: + raise UnknownRdataclass + value = int(match.group(1)) + if value < 0 or value > 65535: + raise ValueError("class must be between >= 0 and <= 65535") + return value + +def to_text(value): + """Convert a DNS rdata class to text. + @param value: the rdata class value + @type value: int + @rtype: string + @raises ValueError: the rdata class value is not >= 0 and <= 65535 + """ + + if value < 0 or value > 65535: + raise ValueError("class must be between >= 0 and <= 65535") + text = _by_value.get(value) + if text is None: + text = 'CLASS' + `value` + return text + +def is_metaclass(rdclass): + """True if the class is a metaclass. + @param rdclass: the rdata class + @type rdclass: int + @rtype: bool""" + + if _metaclasses.has_key(rdclass): + return True + return False diff --git a/third_party/dnspython/dns/rdataset.py b/third_party/dnspython/dns/rdataset.py new file mode 100644 index 0000000000..dcd2b40191 --- /dev/null +++ b/third_party/dnspython/dns/rdataset.py @@ -0,0 +1,329 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" + +import random +import StringIO +import struct + +import dns.exception +import dns.rdatatype +import dns.rdataclass +import dns.rdata +import dns.set + +# define SimpleSet here for backwards compatibility +SimpleSet = dns.set.Set + +class DifferingCovers(dns.exception.DNSException): + """Raised if an attempt is made to add a SIG/RRSIG whose covered type + is not the same as that of the other rdatas in the rdataset.""" + pass + +class IncompatibleTypes(dns.exception.DNSException): + """Raised if an attempt is made to add rdata of an incompatible type.""" + pass + +class Rdataset(dns.set.Set): + """A DNS rdataset. + + @ivar rdclass: The class of the rdataset + @type rdclass: int + @ivar rdtype: The type of the rdataset + @type rdtype: int + @ivar covers: The covered type. Usually this value is + dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or + dns.rdatatype.RRSIG, then the covers value will be the rdata + type the SIG/RRSIG covers. The library treats the SIG and RRSIG + types as if they were a family of + types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much + easier to work with than if RRSIGs covering different rdata + types were aggregated into a single RRSIG rdataset. + @type covers: int + @ivar ttl: The DNS TTL (Time To Live) value + @type ttl: int + """ + + __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl'] + + def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE): + """Create a new rdataset of the specified class and type. + + @see: the description of the class instance variables for the + meaning of I{rdclass} and I{rdtype}""" + + super(Rdataset, self).__init__() + self.rdclass = rdclass + self.rdtype = rdtype + self.covers = covers + self.ttl = 0 + + def _clone(self): + obj = super(Rdataset, self)._clone() + obj.rdclass = self.rdclass + obj.rdtype = self.rdtype + obj.covers = self.covers + obj.ttl = self.ttl + return obj + + def update_ttl(self, ttl): + """Set the TTL of the rdataset to be the lesser of the set's current + TTL or the specified TTL. If the set contains no rdatas, set the TTL + to the specified TTL. + @param ttl: The TTL + @type ttl: int""" + + if len(self) == 0: + self.ttl = ttl + elif ttl < self.ttl: + self.ttl = ttl + + def add(self, rd, ttl=None): + """Add the specified rdata to the rdataset. + + If the optional I{ttl} parameter is supplied, then + self.update_ttl(ttl) will be called prior to adding the rdata. + + @param rd: The rdata + @type rd: dns.rdata.Rdata object + @param ttl: The TTL + @type ttl: int""" + + # + # If we're adding a signature, do some special handling to + # check that the signature covers the same type as the + # other rdatas in this rdataset. If this is the first rdata + # in the set, initialize the covers field. + # + if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: + raise IncompatibleTypes + if not ttl is None: + self.update_ttl(ttl) + if self.rdtype == dns.rdatatype.RRSIG or \ + self.rdtype == dns.rdatatype.SIG: + covers = rd.covers() + if len(self) == 0 and self.covers == dns.rdatatype.NONE: + self.covers = covers + elif self.covers != covers: + raise DifferingCovers + if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: + self.clear() + super(Rdataset, self).add(rd) + + def union_update(self, other): + self.update_ttl(other.ttl) + super(Rdataset, self).union_update(other) + + def intersection_update(self, other): + self.update_ttl(other.ttl) + super(Rdataset, self).intersection_update(other) + + def update(self, other): + """Add all rdatas in other to self. + + @param other: The rdataset from which to update + @type other: dns.rdataset.Rdataset object""" + + self.update_ttl(other.ttl) + super(Rdataset, self).update(other) + + def __repr__(self): + if self.covers == 0: + ctext = '' + else: + ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' + return '' + + def __str__(self): + return self.to_text() + + def __eq__(self, other): + """Two rdatasets are equal if they have the same class, type, and + covers, and contain the same rdata. + @rtype: bool""" + + if not isinstance(other, Rdataset): + return False + if self.rdclass != other.rdclass or \ + self.rdtype != other.rdtype or \ + self.covers != other.covers: + return False + return super(Rdataset, self).__eq__(other) + + def __ne__(self, other): + return not self.__eq__(other) + + def to_text(self, name=None, origin=None, relativize=True, + override_rdclass=None, **kw): + """Convert the rdataset into DNS master file format. + + @see: L{dns.name.Name.choose_relativity} for more information + on how I{origin} and I{relativize} determine the way names + are emitted. + + Any additional keyword arguments are passed on to the rdata + to_text() method. + + @param name: If name is not None, emit a RRs with I{name} as + the owner name. + @type name: dns.name.Name object + @param origin: The origin for relative names, or None. + @type origin: dns.name.Name object + @param relativize: True if names should names be relativized + @type relativize: bool""" + if not name is None: + name = name.choose_relativity(origin, relativize) + ntext = str(name) + pad = ' ' + else: + ntext = '' + pad = '' + s = StringIO.StringIO() + if not override_rdclass is None: + rdclass = override_rdclass + else: + rdclass = self.rdclass + if len(self) == 0: + # + # Empty rdatasets are used for the question section, and in + # some dynamic updates, so we don't need to print out the TTL + # (which is meaningless anyway). + # + print >> s, '%s%s%s %s' % (ntext, pad, + dns.rdataclass.to_text(rdclass), + dns.rdatatype.to_text(self.rdtype)) + else: + for rd in self: + print >> s, '%s%s%d %s %s %s' % \ + (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass), + dns.rdatatype.to_text(self.rdtype), + rd.to_text(origin=origin, relativize=relativize, **kw)) + # + # We strip off the final \n for the caller's convenience in printing + # + return s.getvalue()[:-1] + + def to_wire(self, name, file, compress=None, origin=None, + override_rdclass=None, want_shuffle=True): + """Convert the rdataset to wire format. + + @param name: The owner name of the RRset that will be emitted + @type name: dns.name.Name object + @param file: The file to which the wire format data will be appended + @type file: file + @param compress: The compression table to use; the default is None. + @type compress: dict + @param origin: The origin to be appended to any relative names when + they are emitted. The default is None. + @returns: the number of records emitted + @rtype: int + """ + + if not override_rdclass is None: + rdclass = override_rdclass + want_shuffle = False + else: + rdclass = self.rdclass + file.seek(0, 2) + if len(self) == 0: + name.to_wire(file, compress, origin) + stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0) + file.write(stuff) + return 1 + else: + if want_shuffle: + l = list(self) + random.shuffle(l) + else: + l = self + for rd in l: + name.to_wire(file, compress, origin) + stuff = struct.pack("!HHIH", self.rdtype, rdclass, + self.ttl, 0) + file.write(stuff) + start = file.tell() + rd.to_wire(file, compress, origin) + end = file.tell() + assert end - start < 65536 + file.seek(start - 2) + stuff = struct.pack("!H", end - start) + file.write(stuff) + file.seek(0, 2) + return len(self) + + def match(self, rdclass, rdtype, covers): + """Returns True if this rdataset matches the specified class, type, + and covers""" + if self.rdclass == rdclass and \ + self.rdtype == rdtype and \ + self.covers == covers: + return True + return False + +def from_text_list(rdclass, rdtype, ttl, text_rdatas): + """Create an rdataset with the specified class, type, and TTL, and with + the specified list of rdatas in text format. + + @rtype: dns.rdataset.Rdataset object + """ + + if isinstance(rdclass, (str, unicode)): + rdclass = dns.rdataclass.from_text(rdclass) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + r = Rdataset(rdclass, rdtype) + r.update_ttl(ttl) + for t in text_rdatas: + rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) + r.add(rd) + return r + +def from_text(rdclass, rdtype, ttl, *text_rdatas): + """Create an rdataset with the specified class, type, and TTL, and with + the specified rdatas in text format. + + @rtype: dns.rdataset.Rdataset object + """ + + return from_text_list(rdclass, rdtype, ttl, text_rdatas) + +def from_rdata_list(ttl, rdatas): + """Create an rdataset with the specified TTL, and with + the specified list of rdata objects. + + @rtype: dns.rdataset.Rdataset object + """ + + if len(rdatas) == 0: + raise ValueError("rdata list must not be empty") + r = None + for rd in rdatas: + if r is None: + r = Rdataset(rd.rdclass, rd.rdtype) + r.update_ttl(ttl) + first_time = False + r.add(rd) + return r + +def from_rdata(ttl, *rdatas): + """Create an rdataset with the specified TTL, and with + the specified rdata objects. + + @rtype: dns.rdataset.Rdataset object + """ + + return from_rdata_list(ttl, rdatas) diff --git a/third_party/dnspython/dns/rdatatype.py b/third_party/dnspython/dns/rdatatype.py new file mode 100644 index 0000000000..380cfcd0d3 --- /dev/null +++ b/third_party/dnspython/dns/rdatatype.py @@ -0,0 +1,232 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Rdata Types. + +@var _by_text: The rdata type textual name to value mapping +@type _by_text: dict +@var _by_value: The rdata type value to textual name mapping +@type _by_value: dict +@var _metatypes: If an rdatatype is a metatype, there will be a mapping +whose key is the rdatatype value and whose value is True in this dictionary. +@type _metatypes: dict +@var _singletons: If an rdatatype is a singleton, there will be a mapping +whose key is the rdatatype value and whose value is True in this dictionary. +@type _singletons: dict""" + +import re + +import dns.exception + +NONE = 0 +A = 1 +NS = 2 +MD = 3 +MF = 4 +CNAME = 5 +SOA = 6 +MB = 7 +MG = 8 +MR = 9 +NULL = 10 +WKS = 11 +PTR = 12 +HINFO = 13 +MINFO = 14 +MX = 15 +TXT = 16 +RP = 17 +AFSDB = 18 +X25 = 19 +ISDN = 20 +RT = 21 +NSAP = 22 +NSAP_PTR = 23 +SIG = 24 +KEY = 25 +PX = 26 +GPOS = 27 +AAAA = 28 +LOC = 29 +NXT = 30 +SRV = 33 +NAPTR = 35 +KX = 36 +CERT = 37 +A6 = 38 +DNAME = 39 +OPT = 41 +APL = 42 +DS = 43 +SSHFP = 44 +IPSECKEY = 45 +RRSIG = 46 +NSEC = 47 +DNSKEY = 48 +DHCID = 49 +NSEC3 = 50 +NSEC3PARAM = 51 +HIP = 55 +SPF = 99 +UNSPEC = 103 +TKEY = 249 +TSIG = 250 +IXFR = 251 +AXFR = 252 +MAILB = 253 +MAILA = 254 +ANY = 255 +TA = 32768 +DLV = 32769 + +_by_text = { + 'NONE' : NONE, + 'A' : A, + 'NS' : NS, + 'MD' : MD, + 'MF' : MF, + 'CNAME' : CNAME, + 'SOA' : SOA, + 'MB' : MB, + 'MG' : MG, + 'MR' : MR, + 'NULL' : NULL, + 'WKS' : WKS, + 'PTR' : PTR, + 'HINFO' : HINFO, + 'MINFO' : MINFO, + 'MX' : MX, + 'TXT' : TXT, + 'RP' : RP, + 'AFSDB' : AFSDB, + 'X25' : X25, + 'ISDN' : ISDN, + 'RT' : RT, + 'NSAP' : NSAP, + 'NSAP-PTR' : NSAP_PTR, + 'SIG' : SIG, + 'KEY' : KEY, + 'PX' : PX, + 'GPOS' : GPOS, + 'AAAA' : AAAA, + 'LOC' : LOC, + 'NXT' : NXT, + 'SRV' : SRV, + 'NAPTR' : NAPTR, + 'KX' : KX, + 'CERT' : CERT, + 'A6' : A6, + 'DNAME' : DNAME, + 'OPT' : OPT, + 'APL' : APL, + 'DS' : DS, + 'SSHFP' : SSHFP, + 'IPSECKEY' : IPSECKEY, + 'RRSIG' : RRSIG, + 'NSEC' : NSEC, + 'DNSKEY' : DNSKEY, + 'DHCID' : DHCID, + 'NSEC3' : NSEC3, + 'NSEC3PARAM' : NSEC3PARAM, + 'HIP' : HIP, + 'SPF' : SPF, + 'UNSPEC' : UNSPEC, + 'TKEY' : TKEY, + 'TSIG' : TSIG, + 'IXFR' : IXFR, + 'AXFR' : AXFR, + 'MAILB' : MAILB, + 'MAILA' : MAILA, + 'ANY' : ANY, + 'TA' : TA, + 'DLV' : DLV, + } + +# We construct the inverse mapping programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mapping not to be true inverse. + +_by_value = dict([(y, x) for x, y in _by_text.iteritems()]) + + +_metatypes = { + OPT : True + } + +_singletons = { + SOA : True, + NXT : True, + DNAME : True, + NSEC : True, + # CNAME is technically a singleton, but we allow multiple CNAMEs. + } + +_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I); + +class UnknownRdatatype(dns.exception.DNSException): + """Raised if a type is unknown.""" + pass + +def from_text(text): + """Convert text into a DNS rdata type value. + @param text: the text + @type text: string + @raises dns.rdatatype.UnknownRdatatype: the type is unknown + @raises ValueError: the rdata type value is not >= 0 and <= 65535 + @rtype: int""" + + value = _by_text.get(text.upper()) + if value is None: + match = _unknown_type_pattern.match(text) + if match == None: + raise UnknownRdatatype + value = int(match.group(1)) + if value < 0 or value > 65535: + raise ValueError("type must be between >= 0 and <= 65535") + return value + +def to_text(value): + """Convert a DNS rdata type to text. + @param value: the rdata type value + @type value: int + @raises ValueError: the rdata type value is not >= 0 and <= 65535 + @rtype: string""" + + if value < 0 or value > 65535: + raise ValueError("type must be between >= 0 and <= 65535") + text = _by_value.get(value) + if text is None: + text = 'TYPE' + `value` + return text + +def is_metatype(rdtype): + """True if the type is a metatype. + @param rdtype: the type + @type rdtype: int + @rtype: bool""" + + if rdtype >= TKEY and rdtype <= ANY or _metatypes.has_key(rdtype): + return True + return False + +def is_singleton(rdtype): + """True if the type is a singleton. + @param rdtype: the type + @type rdtype: int + @rtype: bool""" + + if _singletons.has_key(rdtype): + return True + return False diff --git a/third_party/dnspython/dns/rdtypes/ANY/AFSDB.py b/third_party/dnspython/dns/rdtypes/ANY/AFSDB.py new file mode 100644 index 0000000000..c729789afa --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/AFSDB.py @@ -0,0 +1,51 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.mxbase + +class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX): + """AFSDB record + + @ivar subtype: the subtype value + @type subtype: int + @ivar hostname: the hostname name + @type hostname: dns.name.Name object""" + + # Use the property mechanism to make "subtype" an alias for the + # "preference" attribute, and "hostname" an alias for the "exchange" + # attribute. + # + # This lets us inherit the UncompressedMX implementation but lets + # the caller use appropriate attribute names for the rdata type. + # + # We probably lose some performance vs. a cut-and-paste + # implementation, but this way we don't copy code, and that's + # good. + + def get_subtype(self): + return self.preference + + def set_subtype(self, subtype): + self.preference = subtype + + subtype = property(get_subtype, set_subtype) + + def get_hostname(self): + return self.exchange + + def set_hostname(self, hostname): + self.exchange = hostname + + hostname = property(get_hostname, set_hostname) diff --git a/third_party/dnspython/dns/rdtypes/ANY/CERT.py b/third_party/dnspython/dns/rdtypes/ANY/CERT.py new file mode 100644 index 0000000000..c102521a14 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/CERT.py @@ -0,0 +1,131 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import struct + +import dns.exception +import dns.dnssec +import dns.rdata +import dns.tokenizer + +_ctype_by_value = { + 1 : 'PKIX', + 2 : 'SPKI', + 3 : 'PGP', + 253 : 'URI', + 254 : 'OID', + } + +_ctype_by_name = { + 'PKIX' : 1, + 'SPKI' : 2, + 'PGP' : 3, + 'URI' : 253, + 'OID' : 254, + } + +def _ctype_from_text(what): + v = _ctype_by_name.get(what) + if not v is None: + return v + return int(what) + +def _ctype_to_text(what): + v = _ctype_by_value.get(what) + if not v is None: + return v + return str(what) + +class CERT(dns.rdata.Rdata): + """CERT record + + @ivar certificate_type: certificate type + @type certificate_type: int + @ivar key_tag: key tag + @type key_tag: int + @ivar algorithm: algorithm + @type algorithm: int + @ivar certificate: the certificate or CRL + @type certificate: string + @see: RFC 2538""" + + __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate'] + + def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm, + certificate): + super(CERT, self).__init__(rdclass, rdtype) + self.certificate_type = certificate_type + self.key_tag = key_tag + self.algorithm = algorithm + self.certificate = certificate + + def to_text(self, origin=None, relativize=True, **kw): + certificate_type = _ctype_to_text(self.certificate_type) + return "%s %d %s %s" % (certificate_type, self.key_tag, + dns.dnssec.algorithm_to_text(self.algorithm), + dns.rdata._base64ify(self.certificate)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + certificate_type = _ctype_from_text(tok.get_string()) + key_tag = tok.get_uint16() + algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) + if algorithm < 0 or algorithm > 255: + raise dns.exception.SyntaxError("bad algorithm type") + chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + chunks.append(t.value) + b64 = ''.join(chunks) + certificate = b64.decode('base64_codec') + return cls(rdclass, rdtype, certificate_type, key_tag, + algorithm, certificate) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + prefix = struct.pack("!HHB", self.certificate_type, self.key_tag, + self.algorithm) + file.write(prefix) + file.write(self.certificate) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + prefix = wire[current : current + 5].unwrap() + current += 5 + rdlen -= 5 + if rdlen < 0: + raise dns.exception.FormError + (certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix) + certificate = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, + certificate) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + f = cStringIO.StringIO() + self.to_wire(f) + wire1 = f.getvalue() + f.seek(0) + f.truncate() + other.to_wire(f) + wire2 = f.getvalue() + f.close() + + return cmp(wire1, wire2) diff --git a/third_party/dnspython/dns/rdtypes/ANY/CNAME.py b/third_party/dnspython/dns/rdtypes/ANY/CNAME.py new file mode 100644 index 0000000000..fb8e9be49e --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/CNAME.py @@ -0,0 +1,24 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.nsbase + +class CNAME(dns.rdtypes.nsbase.NSBase): + """CNAME record + + Note: although CNAME is officially a singleton type, dnspython allows + non-singleton CNAME rdatasets because such sets have been commonly + used by BIND and other nameservers for load balancing.""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/DLV.py b/third_party/dnspython/dns/rdtypes/ANY/DLV.py new file mode 100644 index 0000000000..8bd79793d1 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/DLV.py @@ -0,0 +1,20 @@ +# Copyright (C) 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.dsbase + +class DLV(dns.rdtypes.dsbase.DSBase): + """DLV record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/DNAME.py b/third_party/dnspython/dns/rdtypes/ANY/DNAME.py new file mode 100644 index 0000000000..d864001190 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/DNAME.py @@ -0,0 +1,21 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.nsbase + +class DNAME(dns.rdtypes.nsbase.UncompressedNS): + """DNAME record""" + def to_digestable(self, origin = None): + return self.target.to_digestable(origin) diff --git a/third_party/dnspython/dns/rdtypes/ANY/DNSKEY.py b/third_party/dnspython/dns/rdtypes/ANY/DNSKEY.py new file mode 100644 index 0000000000..1d678d2f2a --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/DNSKEY.py @@ -0,0 +1,94 @@ +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +import struct + +import dns.exception +import dns.dnssec +import dns.rdata + +# flag constants +SEP = 0x0001 +REVOKE = 0x0080 +ZONE = 0x0100 + +class DNSKEY(dns.rdata.Rdata): + """DNSKEY record + + @ivar flags: the key flags + @type flags: int + @ivar protocol: the protocol for which this key may be used + @type protocol: int + @ivar algorithm: the algorithm used for the key + @type algorithm: int + @ivar key: the public key + @type key: string""" + + __slots__ = ['flags', 'protocol', 'algorithm', 'key'] + + def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): + super(DNSKEY, self).__init__(rdclass, rdtype) + self.flags = flags + self.protocol = protocol + self.algorithm = algorithm + self.key = key + + def to_text(self, origin=None, relativize=True, **kw): + return '%d %d %d %s' % (self.flags, self.protocol, self.algorithm, + dns.rdata._base64ify(self.key)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + flags = tok.get_uint16() + protocol = tok.get_uint8() + algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) + chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + chunks.append(t.value) + b64 = ''.join(chunks) + key = b64.decode('base64_codec') + return cls(rdclass, rdtype, flags, protocol, algorithm, key) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) + file.write(header) + file.write(self.key) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + if rdlen < 4: + raise dns.exception.FormError + header = struct.unpack('!HBB', wire[current : current + 4]) + current += 4 + rdlen -= 4 + key = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, header[0], header[1], header[2], + key) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + hs = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) + ho = struct.pack("!HBB", other.flags, other.protocol, other.algorithm) + v = cmp(hs, ho) + if v == 0: + v = cmp(self.key, other.key) + return v diff --git a/third_party/dnspython/dns/rdtypes/ANY/DS.py b/third_party/dnspython/dns/rdtypes/ANY/DS.py new file mode 100644 index 0000000000..56b6332de2 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/DS.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.dsbase + +class DS(dns.rdtypes.dsbase.DSBase): + """DS record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/GPOS.py b/third_party/dnspython/dns/rdtypes/ANY/GPOS.py new file mode 100644 index 0000000000..38d1d88b28 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/GPOS.py @@ -0,0 +1,156 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.rdata +import dns.tokenizer + +def _validate_float_string(what): + if what[0] == '-' or what[0] == '+': + what = what[1:] + if what.isdigit(): + return + (left, right) = what.split('.') + if left == '' and right == '': + raise dns.exception.FormError + if not left == '' and not left.isdigit(): + raise dns.exception.FormError + if not right == '' and not right.isdigit(): + raise dns.exception.FormError + +class GPOS(dns.rdata.Rdata): + """GPOS record + + @ivar latitude: latitude + @type latitude: string + @ivar longitude: longitude + @type longitude: string + @ivar altitude: altitude + @type altitude: string + @see: RFC 1712""" + + __slots__ = ['latitude', 'longitude', 'altitude'] + + def __init__(self, rdclass, rdtype, latitude, longitude, altitude): + super(GPOS, self).__init__(rdclass, rdtype) + if isinstance(latitude, float) or \ + isinstance(latitude, int) or \ + isinstance(latitude, long): + latitude = str(latitude) + if isinstance(longitude, float) or \ + isinstance(longitude, int) or \ + isinstance(longitude, long): + longitude = str(longitude) + if isinstance(altitude, float) or \ + isinstance(altitude, int) or \ + isinstance(altitude, long): + altitude = str(altitude) + _validate_float_string(latitude) + _validate_float_string(longitude) + _validate_float_string(altitude) + self.latitude = latitude + self.longitude = longitude + self.altitude = altitude + + def to_text(self, origin=None, relativize=True, **kw): + return '%s %s %s' % (self.latitude, self.longitude, self.altitude) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + latitude = tok.get_string() + longitude = tok.get_string() + altitude = tok.get_string() + tok.get_eol() + return cls(rdclass, rdtype, latitude, longitude, altitude) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + l = len(self.latitude) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.latitude) + l = len(self.longitude) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.longitude) + l = len(self.altitude) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.altitude) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l > rdlen: + raise dns.exception.FormError + latitude = wire[current : current + l].unwrap() + current += l + rdlen -= l + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l > rdlen: + raise dns.exception.FormError + longitude = wire[current : current + l].unwrap() + current += l + rdlen -= l + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l != rdlen: + raise dns.exception.FormError + altitude = wire[current : current + l].unwrap() + return cls(rdclass, rdtype, latitude, longitude, altitude) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + v = cmp(self.latitude, other.latitude) + if v == 0: + v = cmp(self.longitude, other.longitude) + if v == 0: + v = cmp(self.altitude, other.altitude) + return v + + def _get_float_latitude(self): + return float(self.latitude) + + def _set_float_latitude(self, value): + self.latitude = str(value) + + float_latitude = property(_get_float_latitude, _set_float_latitude, + doc="latitude as a floating point value") + + def _get_float_longitude(self): + return float(self.longitude) + + def _set_float_longitude(self, value): + self.longitude = str(value) + + float_longitude = property(_get_float_longitude, _set_float_longitude, + doc="longitude as a floating point value") + + def _get_float_altitude(self): + return float(self.altitude) + + def _set_float_altitude(self, value): + self.altitude = str(value) + + float_altitude = property(_get_float_altitude, _set_float_altitude, + doc="altitude as a floating point value") diff --git a/third_party/dnspython/dns/rdtypes/ANY/HINFO.py b/third_party/dnspython/dns/rdtypes/ANY/HINFO.py new file mode 100644 index 0000000000..15fd54e69e --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/HINFO.py @@ -0,0 +1,83 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.rdata +import dns.tokenizer + +class HINFO(dns.rdata.Rdata): + """HINFO record + + @ivar cpu: the CPU type + @type cpu: string + @ivar os: the OS type + @type os: string + @see: RFC 1035""" + + __slots__ = ['cpu', 'os'] + + def __init__(self, rdclass, rdtype, cpu, os): + super(HINFO, self).__init__(rdclass, rdtype) + self.cpu = cpu + self.os = os + + def to_text(self, origin=None, relativize=True, **kw): + return '"%s" "%s"' % (dns.rdata._escapify(self.cpu), + dns.rdata._escapify(self.os)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + cpu = tok.get_string() + os = tok.get_string() + tok.get_eol() + return cls(rdclass, rdtype, cpu, os) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + l = len(self.cpu) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.cpu) + l = len(self.os) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.os) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l > rdlen: + raise dns.exception.FormError + cpu = wire[current : current + l].unwrap() + current += l + rdlen -= l + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l != rdlen: + raise dns.exception.FormError + os = wire[current : current + l].unwrap() + return cls(rdclass, rdtype, cpu, os) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + v = cmp(self.cpu, other.cpu) + if v == 0: + v = cmp(self.os, other.os) + return v diff --git a/third_party/dnspython/dns/rdtypes/ANY/HIP.py b/third_party/dnspython/dns/rdtypes/ANY/HIP.py new file mode 100644 index 0000000000..968b36f5e2 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/HIP.py @@ -0,0 +1,140 @@ +# Copyright (C) 2010, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import string +import struct + +import dns.exception +import dns.rdata +import dns.rdatatype + +class HIP(dns.rdata.Rdata): + """HIP record + + @ivar hit: the host identity tag + @type hit: string + @ivar algorithm: the public key cryptographic algorithm + @type algorithm: int + @ivar key: the public key + @type key: string + @ivar servers: the rendezvous servers + @type servers: list of dns.name.Name objects + @see: RFC 5205""" + + __slots__ = ['hit', 'algorithm', 'key', 'servers'] + + def __init__(self, rdclass, rdtype, hit, algorithm, key, servers): + super(HIP, self).__init__(rdclass, rdtype) + self.hit = hit + self.algorithm = algorithm + self.key = key + self.servers = servers + + def to_text(self, origin=None, relativize=True, **kw): + hit = self.hit.encode('hex-codec') + key = self.key.encode('base64-codec').replace('\n', '') + text = '' + servers = [] + for server in self.servers: + servers.append(str(server.choose_relativity(origin, relativize))) + if len(servers) > 0: + text += (' ' + ' '.join(servers)) + return '%u %s %s%s' % (self.algorithm, hit, key, text) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + algorithm = tok.get_uint8() + hit = tok.get_string().decode('hex-codec') + if len(hit) > 255: + raise dns.exception.SyntaxError("HIT too long") + key = tok.get_string().decode('base64-codec') + servers = [] + while 1: + token = tok.get() + if token.is_eol_or_eof(): + break + server = dns.name.from_text(token.value, origin) + server.choose_relativity(origin, relativize) + servers.append(server) + return cls(rdclass, rdtype, hit, algorithm, key, servers) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + lh = len(self.hit) + lk = len(self.key) + file.write(struct.pack("!BBH", lh, self.algorithm, lk)) + file.write(self.hit) + file.write(self.key) + for server in self.servers: + server.to_wire(file, None, origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (lh, algorithm, lk) = struct.unpack('!BBH', + wire[current : current + 4]) + current += 4 + rdlen -= 4 + hit = wire[current : current + lh].unwrap() + current += lh + rdlen -= lh + key = wire[current : current + lk].unwrap() + current += lk + rdlen -= lk + servers = [] + while rdlen > 0: + (server, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + current += cused + rdlen -= cused + if not origin is None: + server = server.relativize(origin) + servers.append(server) + return cls(rdclass, rdtype, hit, algorithm, key, servers) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + servers = [] + for server in self.servers: + server = server.choose_relativity(origin, relativize) + servers.append(server) + self.servers = servers + + def _cmp(self, other): + b1 = cStringIO.StringIO() + lh = len(self.hit) + lk = len(self.key) + b1.write(struct.pack("!BBH", lh, self.algorithm, lk)) + b1.write(self.hit) + b1.write(self.key) + b2 = cStringIO.StringIO() + lh = len(other.hit) + lk = len(other.key) + b2.write(struct.pack("!BBH", lh, other.algorithm, lk)) + b2.write(other.hit) + b2.write(other.key) + v = cmp(b1.getvalue(), b2.getvalue()) + if v != 0: + return v + ls = len(self.servers) + lo = len(other.servers) + count = min(ls, lo) + i = 0 + while i < count: + v = cmp(self.servers[i], other.servers[i]) + if v != 0: + return v + i += 1 + return ls - lo diff --git a/third_party/dnspython/dns/rdtypes/ANY/ISDN.py b/third_party/dnspython/dns/rdtypes/ANY/ISDN.py new file mode 100644 index 0000000000..0c2d3cd035 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/ISDN.py @@ -0,0 +1,96 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.rdata +import dns.tokenizer + +class ISDN(dns.rdata.Rdata): + """ISDN record + + @ivar address: the ISDN address + @type address: string + @ivar subaddress: the ISDN subaddress (or '' if not present) + @type subaddress: string + @see: RFC 1183""" + + __slots__ = ['address', 'subaddress'] + + def __init__(self, rdclass, rdtype, address, subaddress): + super(ISDN, self).__init__(rdclass, rdtype) + self.address = address + self.subaddress = subaddress + + def to_text(self, origin=None, relativize=True, **kw): + if self.subaddress: + return '"%s" "%s"' % (dns.rdata._escapify(self.address), + dns.rdata._escapify(self.subaddress)) + else: + return '"%s"' % dns.rdata._escapify(self.address) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + address = tok.get_string() + t = tok.get() + if not t.is_eol_or_eof(): + tok.unget(t) + subaddress = tok.get_string() + else: + tok.unget(t) + subaddress = '' + tok.get_eol() + return cls(rdclass, rdtype, address, subaddress) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + l = len(self.address) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.address) + l = len(self.subaddress) + if l > 0: + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.subaddress) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l > rdlen: + raise dns.exception.FormError + address = wire[current : current + l].unwrap() + current += l + rdlen -= l + if rdlen > 0: + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l != rdlen: + raise dns.exception.FormError + subaddress = wire[current : current + l].unwrap() + else: + subaddress = '' + return cls(rdclass, rdtype, address, subaddress) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + v = cmp(self.address, other.address) + if v == 0: + v = cmp(self.subaddress, other.subaddress) + return v diff --git a/third_party/dnspython/dns/rdtypes/ANY/LOC.py b/third_party/dnspython/dns/rdtypes/ANY/LOC.py new file mode 100644 index 0000000000..154546d79d --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/LOC.py @@ -0,0 +1,334 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import struct + +import dns.exception +import dns.rdata + +_pows = (1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, + 100000000L, 1000000000L, 10000000000L) + +def _exponent_of(what, desc): + exp = None + for i in xrange(len(_pows)): + if what // _pows[i] == 0L: + exp = i - 1 + break + if exp is None or exp < 0: + raise dns.exception.SyntaxError("%s value out of bounds" % desc) + return exp + +def _float_to_tuple(what): + if what < 0: + sign = -1 + what *= -1 + else: + sign = 1 + what = long(round(what * 3600000)) + degrees = int(what // 3600000) + what -= degrees * 3600000 + minutes = int(what // 60000) + what -= minutes * 60000 + seconds = int(what // 1000) + what -= int(seconds * 1000) + what = int(what) + return (degrees * sign, minutes, seconds, what) + +def _tuple_to_float(what): + if what[0] < 0: + sign = -1 + value = float(what[0]) * -1 + else: + sign = 1 + value = float(what[0]) + value += float(what[1]) / 60.0 + value += float(what[2]) / 3600.0 + value += float(what[3]) / 3600000.0 + return sign * value + +def _encode_size(what, desc): + what = long(what); + exponent = _exponent_of(what, desc) & 0xF + base = what // pow(10, exponent) & 0xF + return base * 16 + exponent + +def _decode_size(what, desc): + exponent = what & 0x0F + if exponent > 9: + raise dns.exception.SyntaxError("bad %s exponent" % desc) + base = (what & 0xF0) >> 4 + if base > 9: + raise dns.exception.SyntaxError("bad %s base" % desc) + return long(base) * pow(10, exponent) + +class LOC(dns.rdata.Rdata): + """LOC record + + @ivar latitude: latitude + @type latitude: (int, int, int, int) tuple specifying the degrees, minutes, + seconds, and milliseconds of the coordinate. + @ivar longitude: longitude + @type longitude: (int, int, int, int) tuple specifying the degrees, + minutes, seconds, and milliseconds of the coordinate. + @ivar altitude: altitude + @type altitude: float + @ivar size: size of the sphere + @type size: float + @ivar horizontal_precision: horizontal precision + @type horizontal_precision: float + @ivar vertical_precision: vertical precision + @type vertical_precision: float + @see: RFC 1876""" + + __slots__ = ['latitude', 'longitude', 'altitude', 'size', + 'horizontal_precision', 'vertical_precision'] + + def __init__(self, rdclass, rdtype, latitude, longitude, altitude, + size=1.0, hprec=10000.0, vprec=10.0): + """Initialize a LOC record instance. + + The parameters I{latitude} and I{longitude} may be either a 4-tuple + of integers specifying (degrees, minutes, seconds, milliseconds), + or they may be floating point values specifying the number of + degrees. The other parameters are floats.""" + + super(LOC, self).__init__(rdclass, rdtype) + if isinstance(latitude, int) or isinstance(latitude, long): + latitude = float(latitude) + if isinstance(latitude, float): + latitude = _float_to_tuple(latitude) + self.latitude = latitude + if isinstance(longitude, int) or isinstance(longitude, long): + longitude = float(longitude) + if isinstance(longitude, float): + longitude = _float_to_tuple(longitude) + self.longitude = longitude + self.altitude = float(altitude) + self.size = float(size) + self.horizontal_precision = float(hprec) + self.vertical_precision = float(vprec) + + def to_text(self, origin=None, relativize=True, **kw): + if self.latitude[0] > 0: + lat_hemisphere = 'N' + lat_degrees = self.latitude[0] + else: + lat_hemisphere = 'S' + lat_degrees = -1 * self.latitude[0] + if self.longitude[0] > 0: + long_hemisphere = 'E' + long_degrees = self.longitude[0] + else: + long_hemisphere = 'W' + long_degrees = -1 * self.longitude[0] + text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( + lat_degrees, self.latitude[1], self.latitude[2], self.latitude[3], + lat_hemisphere, long_degrees, self.longitude[1], self.longitude[2], + self.longitude[3], long_hemisphere, self.altitude / 100.0 + ) + + if self.size != 1.0 or self.horizontal_precision != 10000.0 or \ + self.vertical_precision != 10.0: + text += " %0.2fm %0.2fm %0.2fm" % ( + self.size / 100.0, self.horizontal_precision / 100.0, + self.vertical_precision / 100.0 + ) + return text + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + latitude = [0, 0, 0, 0] + longitude = [0, 0, 0, 0] + size = 1.0 + hprec = 10000.0 + vprec = 10.0 + + latitude[0] = tok.get_int() + t = tok.get_string() + if t.isdigit(): + latitude[1] = int(t) + t = tok.get_string() + if '.' in t: + (seconds, milliseconds) = t.split('.') + if not seconds.isdigit(): + raise dns.exception.SyntaxError('bad latitude seconds value') + latitude[2] = int(seconds) + if latitude[2] >= 60: + raise dns.exception.SyntaxError('latitude seconds >= 60') + l = len(milliseconds) + if l == 0 or l > 3 or not milliseconds.isdigit(): + raise dns.exception.SyntaxError('bad latitude milliseconds value') + if l == 1: + m = 100 + elif l == 2: + m = 10 + else: + m = 1 + latitude[3] = m * int(milliseconds) + t = tok.get_string() + elif t.isdigit(): + latitude[2] = int(t) + t = tok.get_string() + if t == 'S': + latitude[0] *= -1 + elif t != 'N': + raise dns.exception.SyntaxError('bad latitude hemisphere value') + + longitude[0] = tok.get_int() + t = tok.get_string() + if t.isdigit(): + longitude[1] = int(t) + t = tok.get_string() + if '.' in t: + (seconds, milliseconds) = t.split('.') + if not seconds.isdigit(): + raise dns.exception.SyntaxError('bad longitude seconds value') + longitude[2] = int(seconds) + if longitude[2] >= 60: + raise dns.exception.SyntaxError('longitude seconds >= 60') + l = len(milliseconds) + if l == 0 or l > 3 or not milliseconds.isdigit(): + raise dns.exception.SyntaxError('bad longitude milliseconds value') + if l == 1: + m = 100 + elif l == 2: + m = 10 + else: + m = 1 + longitude[3] = m * int(milliseconds) + t = tok.get_string() + elif t.isdigit(): + longitude[2] = int(t) + t = tok.get_string() + if t == 'W': + longitude[0] *= -1 + elif t != 'E': + raise dns.exception.SyntaxError('bad longitude hemisphere value') + + t = tok.get_string() + if t[-1] == 'm': + t = t[0 : -1] + altitude = float(t) * 100.0 # m -> cm + + token = tok.get().unescape() + if not token.is_eol_or_eof(): + value = token.value + if value[-1] == 'm': + value = value[0 : -1] + size = float(value) * 100.0 # m -> cm + token = tok.get().unescape() + if not token.is_eol_or_eof(): + value = token.value + if value[-1] == 'm': + value = value[0 : -1] + hprec = float(value) * 100.0 # m -> cm + token = tok.get().unescape() + if not token.is_eol_or_eof(): + value = token.value + if value[-1] == 'm': + value = value[0 : -1] + vprec = float(value) * 100.0 # m -> cm + tok.get_eol() + + return cls(rdclass, rdtype, latitude, longitude, altitude, + size, hprec, vprec) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + if self.latitude[0] < 0: + sign = -1 + degrees = long(-1 * self.latitude[0]) + else: + sign = 1 + degrees = long(self.latitude[0]) + milliseconds = (degrees * 3600000 + + self.latitude[1] * 60000 + + self.latitude[2] * 1000 + + self.latitude[3]) * sign + latitude = 0x80000000L + milliseconds + if self.longitude[0] < 0: + sign = -1 + degrees = long(-1 * self.longitude[0]) + else: + sign = 1 + degrees = long(self.longitude[0]) + milliseconds = (degrees * 3600000 + + self.longitude[1] * 60000 + + self.longitude[2] * 1000 + + self.longitude[3]) * sign + longitude = 0x80000000L + milliseconds + altitude = long(self.altitude) + 10000000L + size = _encode_size(self.size, "size") + hprec = _encode_size(self.horizontal_precision, "horizontal precision") + vprec = _encode_size(self.vertical_precision, "vertical precision") + wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude, + longitude, altitude) + file.write(wire) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (version, size, hprec, vprec, latitude, longitude, altitude) = \ + struct.unpack("!BBBBIII", wire[current : current + rdlen]) + if latitude > 0x80000000L: + latitude = float(latitude - 0x80000000L) / 3600000 + else: + latitude = -1 * float(0x80000000L - latitude) / 3600000 + if latitude < -90.0 or latitude > 90.0: + raise dns.exception.FormError("bad latitude") + if longitude > 0x80000000L: + longitude = float(longitude - 0x80000000L) / 3600000 + else: + longitude = -1 * float(0x80000000L - longitude) / 3600000 + if longitude < -180.0 or longitude > 180.0: + raise dns.exception.FormError("bad longitude") + altitude = float(altitude) - 10000000.0 + size = _decode_size(size, "size") + hprec = _decode_size(hprec, "horizontal precision") + vprec = _decode_size(vprec, "vertical precision") + return cls(rdclass, rdtype, latitude, longitude, altitude, + size, hprec, vprec) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + f = cStringIO.StringIO() + self.to_wire(f) + wire1 = f.getvalue() + f.seek(0) + f.truncate() + other.to_wire(f) + wire2 = f.getvalue() + f.close() + + return cmp(wire1, wire2) + + def _get_float_latitude(self): + return _tuple_to_float(self.latitude) + + def _set_float_latitude(self, value): + self.latitude = _float_to_tuple(value) + + float_latitude = property(_get_float_latitude, _set_float_latitude, + doc="latitude as a floating point value") + + def _get_float_longitude(self): + return _tuple_to_float(self.longitude) + + def _set_float_longitude(self, value): + self.longitude = _float_to_tuple(value) + + float_longitude = property(_get_float_longitude, _set_float_longitude, + doc="longitude as a floating point value") diff --git a/third_party/dnspython/dns/rdtypes/ANY/MX.py b/third_party/dnspython/dns/rdtypes/ANY/MX.py new file mode 100644 index 0000000000..92f41532a7 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/MX.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.mxbase + +class MX(dns.rdtypes.mxbase.MXBase): + """MX record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/NS.py b/third_party/dnspython/dns/rdtypes/ANY/NS.py new file mode 100644 index 0000000000..6b45d4d4b6 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/NS.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.nsbase + +class NS(dns.rdtypes.nsbase.NSBase): + """NS record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/NSEC.py b/third_party/dnspython/dns/rdtypes/ANY/NSEC.py new file mode 100644 index 0000000000..ad113a4b59 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/NSEC.py @@ -0,0 +1,128 @@ +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO + +import dns.exception +import dns.rdata +import dns.rdatatype +import dns.name + +class NSEC(dns.rdata.Rdata): + """NSEC record + + @ivar next: the next name + @type next: dns.name.Name object + @ivar windows: the windowed bitmap list + @type windows: list of (window number, string) tuples""" + + __slots__ = ['next', 'windows'] + + def __init__(self, rdclass, rdtype, next, windows): + super(NSEC, self).__init__(rdclass, rdtype) + self.next = next + self.windows = windows + + def to_text(self, origin=None, relativize=True, **kw): + next = self.next.choose_relativity(origin, relativize) + text = '' + for (window, bitmap) in self.windows: + bits = [] + for i in xrange(0, len(bitmap)): + byte = ord(bitmap[i]) + for j in xrange(0, 8): + if byte & (0x80 >> j): + bits.append(dns.rdatatype.to_text(window * 256 + \ + i * 8 + j)) + text += (' ' + ' '.join(bits)) + return '%s%s' % (next, text) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + next = tok.get_name() + next = next.choose_relativity(origin, relativize) + rdtypes = [] + while 1: + token = tok.get().unescape() + if token.is_eol_or_eof(): + break + nrdtype = dns.rdatatype.from_text(token.value) + if nrdtype == 0: + raise dns.exception.SyntaxError("NSEC with bit 0") + if nrdtype > 65535: + raise dns.exception.SyntaxError("NSEC with bit > 65535") + rdtypes.append(nrdtype) + rdtypes.sort() + window = 0 + octets = 0 + prior_rdtype = 0 + bitmap = ['\0'] * 32 + windows = [] + for nrdtype in rdtypes: + if nrdtype == prior_rdtype: + continue + prior_rdtype = nrdtype + new_window = nrdtype // 256 + if new_window != window: + windows.append((window, ''.join(bitmap[0:octets]))) + bitmap = ['\0'] * 32 + window = new_window + offset = nrdtype % 256 + byte = offset // 8 + bit = offset % 8 + octets = byte + 1 + bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) + windows.append((window, ''.join(bitmap[0:octets]))) + return cls(rdclass, rdtype, next, windows) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + self.next.to_wire(file, None, origin) + for (window, bitmap) in self.windows: + file.write(chr(window)) + file.write(chr(len(bitmap))) + file.write(bitmap) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (next, cused) = dns.name.from_wire(wire[: current + rdlen], current) + current += cused + rdlen -= cused + windows = [] + while rdlen > 0: + if rdlen < 3: + raise dns.exception.FormError("NSEC too short") + window = ord(wire[current]) + octets = ord(wire[current + 1]) + if octets == 0 or octets > 32: + raise dns.exception.FormError("bad NSEC octets") + current += 2 + rdlen -= 2 + if rdlen < octets: + raise dns.exception.FormError("bad NSEC bitmap length") + bitmap = wire[current : current + octets].unwrap() + current += octets + rdlen -= octets + windows.append((window, bitmap)) + if not origin is None: + next = next.relativize(origin) + return cls(rdclass, rdtype, next, windows) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.next = self.next.choose_relativity(origin, relativize) + + def _cmp(self, other): + return self._wire_cmp(other) diff --git a/third_party/dnspython/dns/rdtypes/ANY/NSEC3.py b/third_party/dnspython/dns/rdtypes/ANY/NSEC3.py new file mode 100644 index 0000000000..c7ac7375b4 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/NSEC3.py @@ -0,0 +1,182 @@ +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import cStringIO +import string +import struct + +import dns.exception +import dns.rdata +import dns.rdatatype + +b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') +b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', + '0123456789ABCDEFGHIJKLMNOPQRSTUV') + +# hash algorithm constants +SHA1 = 1 + +# flag constants +OPTOUT = 1 + +class NSEC3(dns.rdata.Rdata): + """NSEC3 record + + @ivar algorithm: the hash algorithm number + @type algorithm: int + @ivar flags: the flags + @type flags: int + @ivar iterations: the number of iterations + @type iterations: int + @ivar salt: the salt + @type salt: string + @ivar next: the next name hash + @type next: string + @ivar windows: the windowed bitmap list + @type windows: list of (window number, string) tuples""" + + __slots__ = ['algorithm', 'flags', 'iterations', 'salt', 'next', 'windows'] + + def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt, + next, windows): + super(NSEC3, self).__init__(rdclass, rdtype) + self.algorithm = algorithm + self.flags = flags + self.iterations = iterations + self.salt = salt + self.next = next + self.windows = windows + + def to_text(self, origin=None, relativize=True, **kw): + next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower() + if self.salt == '': + salt = '-' + else: + salt = self.salt.encode('hex-codec') + text = '' + for (window, bitmap) in self.windows: + bits = [] + for i in xrange(0, len(bitmap)): + byte = ord(bitmap[i]) + for j in xrange(0, 8): + if byte & (0x80 >> j): + bits.append(dns.rdatatype.to_text(window * 256 + \ + i * 8 + j)) + text += (' ' + ' '.join(bits)) + return '%u %u %u %s %s%s' % (self.algorithm, self.flags, self.iterations, + salt, next, text) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + algorithm = tok.get_uint8() + flags = tok.get_uint8() + iterations = tok.get_uint16() + salt = tok.get_string() + if salt == '-': + salt = '' + else: + salt = salt.decode('hex-codec') + next = tok.get_string().upper().translate(b32_hex_to_normal) + next = base64.b32decode(next) + rdtypes = [] + while 1: + token = tok.get().unescape() + if token.is_eol_or_eof(): + break + nrdtype = dns.rdatatype.from_text(token.value) + if nrdtype == 0: + raise dns.exception.SyntaxError("NSEC3 with bit 0") + if nrdtype > 65535: + raise dns.exception.SyntaxError("NSEC3 with bit > 65535") + rdtypes.append(nrdtype) + rdtypes.sort() + window = 0 + octets = 0 + prior_rdtype = 0 + bitmap = ['\0'] * 32 + windows = [] + for nrdtype in rdtypes: + if nrdtype == prior_rdtype: + continue + prior_rdtype = nrdtype + new_window = nrdtype // 256 + if new_window != window: + windows.append((window, ''.join(bitmap[0:octets]))) + bitmap = ['\0'] * 32 + window = new_window + offset = nrdtype % 256 + byte = offset // 8 + bit = offset % 8 + octets = byte + 1 + bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) + windows.append((window, ''.join(bitmap[0:octets]))) + return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + l = len(self.salt) + file.write(struct.pack("!BBHB", self.algorithm, self.flags, + self.iterations, l)) + file.write(self.salt) + l = len(self.next) + file.write(struct.pack("!B", l)) + file.write(self.next) + for (window, bitmap) in self.windows: + file.write(chr(window)) + file.write(chr(len(bitmap))) + file.write(bitmap) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (algorithm, flags, iterations, slen) = struct.unpack('!BBHB', + wire[current : current + 5]) + current += 5 + rdlen -= 5 + salt = wire[current : current + slen].unwrap() + current += slen + rdlen -= slen + (nlen, ) = struct.unpack('!B', wire[current]) + current += 1 + rdlen -= 1 + next = wire[current : current + nlen].unwrap() + current += nlen + rdlen -= nlen + windows = [] + while rdlen > 0: + if rdlen < 3: + raise dns.exception.FormError("NSEC3 too short") + window = ord(wire[current]) + octets = ord(wire[current + 1]) + if octets == 0 or octets > 32: + raise dns.exception.FormError("bad NSEC3 octets") + current += 2 + rdlen -= 2 + if rdlen < octets: + raise dns.exception.FormError("bad NSEC3 bitmap length") + bitmap = wire[current : current + octets].unwrap() + current += octets + rdlen -= octets + windows.append((window, bitmap)) + return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + b1 = cStringIO.StringIO() + self.to_wire(b1) + b2 = cStringIO.StringIO() + other.to_wire(b2) + return cmp(b1.getvalue(), b2.getvalue()) diff --git a/third_party/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py b/third_party/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py new file mode 100644 index 0000000000..4e687825ad --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/NSEC3PARAM.py @@ -0,0 +1,88 @@ +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import struct + +import dns.exception +import dns.rdata + +class NSEC3PARAM(dns.rdata.Rdata): + """NSEC3PARAM record + + @ivar algorithm: the hash algorithm number + @type algorithm: int + @ivar flags: the flags + @type flags: int + @ivar iterations: the number of iterations + @type iterations: int + @ivar salt: the salt + @type salt: string""" + + __slots__ = ['algorithm', 'flags', 'iterations', 'salt'] + + def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt): + super(NSEC3PARAM, self).__init__(rdclass, rdtype) + self.algorithm = algorithm + self.flags = flags + self.iterations = iterations + self.salt = salt + + def to_text(self, origin=None, relativize=True, **kw): + if self.salt == '': + salt = '-' + else: + salt = self.salt.encode('hex-codec') + return '%u %u %u %s' % (self.algorithm, self.flags, self.iterations, salt) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + algorithm = tok.get_uint8() + flags = tok.get_uint8() + iterations = tok.get_uint16() + salt = tok.get_string() + if salt == '-': + salt = '' + else: + salt = salt.decode('hex-codec') + return cls(rdclass, rdtype, algorithm, flags, iterations, salt) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + l = len(self.salt) + file.write(struct.pack("!BBHB", self.algorithm, self.flags, + self.iterations, l)) + file.write(self.salt) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (algorithm, flags, iterations, slen) = struct.unpack('!BBHB', + wire[current : current + 5]) + current += 5 + rdlen -= 5 + salt = wire[current : current + slen].unwrap() + current += slen + rdlen -= slen + if rdlen != 0: + raise dns.exception.FormError + return cls(rdclass, rdtype, algorithm, flags, iterations, salt) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + b1 = cStringIO.StringIO() + self.to_wire(b1) + b2 = cStringIO.StringIO() + other.to_wire(b2) + return cmp(b1.getvalue(), b2.getvalue()) diff --git a/third_party/dnspython/dns/rdtypes/ANY/PTR.py b/third_party/dnspython/dns/rdtypes/ANY/PTR.py new file mode 100644 index 0000000000..4a037537b6 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/PTR.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.nsbase + +class PTR(dns.rdtypes.nsbase.NSBase): + """PTR record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/RP.py b/third_party/dnspython/dns/rdtypes/ANY/RP.py new file mode 100644 index 0000000000..26c55314e1 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/RP.py @@ -0,0 +1,86 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.rdata +import dns.name + +class RP(dns.rdata.Rdata): + """RP record + + @ivar mbox: The responsible person's mailbox + @type mbox: dns.name.Name object + @ivar txt: The owner name of a node with TXT records, or the root name + if no TXT records are associated with this RP. + @type txt: dns.name.Name object + @see: RFC 1183""" + + __slots__ = ['mbox', 'txt'] + + def __init__(self, rdclass, rdtype, mbox, txt): + super(RP, self).__init__(rdclass, rdtype) + self.mbox = mbox + self.txt = txt + + def to_text(self, origin=None, relativize=True, **kw): + mbox = self.mbox.choose_relativity(origin, relativize) + txt = self.txt.choose_relativity(origin, relativize) + return "%s %s" % (str(mbox), str(txt)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + mbox = tok.get_name() + txt = tok.get_name() + mbox = mbox.choose_relativity(origin, relativize) + txt = txt.choose_relativity(origin, relativize) + tok.get_eol() + return cls(rdclass, rdtype, mbox, txt) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + self.mbox.to_wire(file, None, origin) + self.txt.to_wire(file, None, origin) + + def to_digestable(self, origin = None): + return self.mbox.to_digestable(origin) + \ + self.txt.to_digestable(origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (mbox, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + current += cused + rdlen -= cused + if rdlen <= 0: + raise dns.exception.FormError + (txt, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused != rdlen: + raise dns.exception.FormError + if not origin is None: + mbox = mbox.relativize(origin) + txt = txt.relativize(origin) + return cls(rdclass, rdtype, mbox, txt) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.mbox = self.mbox.choose_relativity(origin, relativize) + self.txt = self.txt.choose_relativity(origin, relativize) + + def _cmp(self, other): + v = cmp(self.mbox, other.mbox) + if v == 0: + v = cmp(self.txt, other.txt) + return v diff --git a/third_party/dnspython/dns/rdtypes/ANY/RRSIG.py b/third_party/dnspython/dns/rdtypes/ANY/RRSIG.py new file mode 100644 index 0000000000..63d389cd7a --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/RRSIG.py @@ -0,0 +1,155 @@ +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import calendar +import struct +import time + +import dns.dnssec +import dns.exception +import dns.rdata +import dns.rdatatype + +class BadSigTime(dns.exception.DNSException): + """Raised when a SIG or RRSIG RR's time cannot be parsed.""" + pass + +def sigtime_to_posixtime(what): + if len(what) != 14: + raise BadSigTime + year = int(what[0:4]) + month = int(what[4:6]) + day = int(what[6:8]) + hour = int(what[8:10]) + minute = int(what[10:12]) + second = int(what[12:14]) + return calendar.timegm((year, month, day, hour, minute, second, + 0, 0, 0)) + +def posixtime_to_sigtime(what): + return time.strftime('%Y%m%d%H%M%S', time.gmtime(what)) + +class RRSIG(dns.rdata.Rdata): + """RRSIG record + + @ivar type_covered: the rdata type this signature covers + @type type_covered: int + @ivar algorithm: the algorithm used for the sig + @type algorithm: int + @ivar labels: number of labels + @type labels: int + @ivar original_ttl: the original TTL + @type original_ttl: long + @ivar expiration: signature expiration time + @type expiration: long + @ivar inception: signature inception time + @type inception: long + @ivar key_tag: the key tag + @type key_tag: int + @ivar signer: the signer + @type signer: dns.name.Name object + @ivar signature: the signature + @type signature: string""" + + __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl', + 'expiration', 'inception', 'key_tag', 'signer', + 'signature'] + + def __init__(self, rdclass, rdtype, type_covered, algorithm, labels, + original_ttl, expiration, inception, key_tag, signer, + signature): + super(RRSIG, self).__init__(rdclass, rdtype) + self.type_covered = type_covered + self.algorithm = algorithm + self.labels = labels + self.original_ttl = original_ttl + self.expiration = expiration + self.inception = inception + self.key_tag = key_tag + self.signer = signer + self.signature = signature + + def covers(self): + return self.type_covered + + def to_text(self, origin=None, relativize=True, **kw): + return '%s %d %d %d %s %s %d %s %s' % ( + dns.rdatatype.to_text(self.type_covered), + self.algorithm, + self.labels, + self.original_ttl, + posixtime_to_sigtime(self.expiration), + posixtime_to_sigtime(self.inception), + self.key_tag, + self.signer, + dns.rdata._base64ify(self.signature) + ) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + type_covered = dns.rdatatype.from_text(tok.get_string()) + algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) + labels = tok.get_int() + original_ttl = tok.get_ttl() + expiration = sigtime_to_posixtime(tok.get_string()) + inception = sigtime_to_posixtime(tok.get_string()) + key_tag = tok.get_int() + signer = tok.get_name() + signer = signer.choose_relativity(origin, relativize) + chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + chunks.append(t.value) + b64 = ''.join(chunks) + signature = b64.decode('base64_codec') + return cls(rdclass, rdtype, type_covered, algorithm, labels, + original_ttl, expiration, inception, key_tag, signer, + signature) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + header = struct.pack('!HBBIIIH', self.type_covered, + self.algorithm, self.labels, + self.original_ttl, self.expiration, + self.inception, self.key_tag) + file.write(header) + self.signer.to_wire(file, None, origin) + file.write(self.signature) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + header = struct.unpack('!HBBIIIH', wire[current : current + 18]) + current += 18 + rdlen -= 18 + (signer, cused) = dns.name.from_wire(wire[: current + rdlen], current) + current += cused + rdlen -= cused + if not origin is None: + signer = signer.relativize(origin) + signature = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, header[0], header[1], header[2], + header[3], header[4], header[5], header[6], signer, + signature) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.signer = self.signer.choose_relativity(origin, relativize) + + def _cmp(self, other): + return self._wire_cmp(other) diff --git a/third_party/dnspython/dns/rdtypes/ANY/RT.py b/third_party/dnspython/dns/rdtypes/ANY/RT.py new file mode 100644 index 0000000000..f9653fdb34 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/RT.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.mxbase + +class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX): + """RT record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/SOA.py b/third_party/dnspython/dns/rdtypes/ANY/SOA.py new file mode 100644 index 0000000000..2d6f21b56b --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/SOA.py @@ -0,0 +1,127 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.rdata +import dns.name + +class SOA(dns.rdata.Rdata): + """SOA record + + @ivar mname: the SOA MNAME (master name) field + @type mname: dns.name.Name object + @ivar rname: the SOA RNAME (responsible name) field + @type rname: dns.name.Name object + @ivar serial: The zone's serial number + @type serial: int + @ivar refresh: The zone's refresh value (in seconds) + @type refresh: int + @ivar retry: The zone's retry value (in seconds) + @type retry: int + @ivar expire: The zone's expiration value (in seconds) + @type expire: int + @ivar minimum: The zone's negative caching time (in seconds, called + "minimum" for historical reasons) + @type minimum: int + @see: RFC 1035""" + + __slots__ = ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire', + 'minimum'] + + def __init__(self, rdclass, rdtype, mname, rname, serial, refresh, retry, + expire, minimum): + super(SOA, self).__init__(rdclass, rdtype) + self.mname = mname + self.rname = rname + self.serial = serial + self.refresh = refresh + self.retry = retry + self.expire = expire + self.minimum = minimum + + def to_text(self, origin=None, relativize=True, **kw): + mname = self.mname.choose_relativity(origin, relativize) + rname = self.rname.choose_relativity(origin, relativize) + return '%s %s %d %d %d %d %d' % ( + mname, rname, self.serial, self.refresh, self.retry, + self.expire, self.minimum ) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + mname = tok.get_name() + rname = tok.get_name() + mname = mname.choose_relativity(origin, relativize) + rname = rname.choose_relativity(origin, relativize) + serial = tok.get_uint32() + refresh = tok.get_ttl() + retry = tok.get_ttl() + expire = tok.get_ttl() + minimum = tok.get_ttl() + tok.get_eol() + return cls(rdclass, rdtype, mname, rname, serial, refresh, retry, + expire, minimum ) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + self.mname.to_wire(file, compress, origin) + self.rname.to_wire(file, compress, origin) + five_ints = struct.pack('!IIIII', self.serial, self.refresh, + self.retry, self.expire, self.minimum) + file.write(five_ints) + + def to_digestable(self, origin = None): + return self.mname.to_digestable(origin) + \ + self.rname.to_digestable(origin) + \ + struct.pack('!IIIII', self.serial, self.refresh, + self.retry, self.expire, self.minimum) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (mname, cused) = dns.name.from_wire(wire[: current + rdlen], current) + current += cused + rdlen -= cused + (rname, cused) = dns.name.from_wire(wire[: current + rdlen], current) + current += cused + rdlen -= cused + if rdlen != 20: + raise dns.exception.FormError + five_ints = struct.unpack('!IIIII', + wire[current : current + rdlen]) + if not origin is None: + mname = mname.relativize(origin) + rname = rname.relativize(origin) + return cls(rdclass, rdtype, mname, rname, + five_ints[0], five_ints[1], five_ints[2], five_ints[3], + five_ints[4]) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.mname = self.mname.choose_relativity(origin, relativize) + self.rname = self.rname.choose_relativity(origin, relativize) + + def _cmp(self, other): + v = cmp(self.mname, other.mname) + if v == 0: + v = cmp(self.rname, other.rname) + if v == 0: + self_ints = struct.pack('!IIIII', self.serial, self.refresh, + self.retry, self.expire, self.minimum) + other_ints = struct.pack('!IIIII', other.serial, other.refresh, + other.retry, other.expire, + other.minimum) + v = cmp(self_ints, other_ints) + return v diff --git a/third_party/dnspython/dns/rdtypes/ANY/SPF.py b/third_party/dnspython/dns/rdtypes/ANY/SPF.py new file mode 100644 index 0000000000..8860dd72bb --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/SPF.py @@ -0,0 +1,22 @@ +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.txtbase + +class SPF(dns.rdtypes.txtbase.TXTBase): + """SPF record + + @see: RFC 4408""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/SSHFP.py b/third_party/dnspython/dns/rdtypes/ANY/SSHFP.py new file mode 100644 index 0000000000..cec650a570 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/SSHFP.py @@ -0,0 +1,77 @@ +# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.rdata +import dns.rdatatype + +class SSHFP(dns.rdata.Rdata): + """SSHFP record + + @ivar algorithm: the algorithm + @type algorithm: int + @ivar fp_type: the digest type + @type fp_type: int + @ivar fingerprint: the fingerprint + @type fingerprint: string + @see: draft-ietf-secsh-dns-05.txt""" + + __slots__ = ['algorithm', 'fp_type', 'fingerprint'] + + def __init__(self, rdclass, rdtype, algorithm, fp_type, + fingerprint): + super(SSHFP, self).__init__(rdclass, rdtype) + self.algorithm = algorithm + self.fp_type = fp_type + self.fingerprint = fingerprint + + def to_text(self, origin=None, relativize=True, **kw): + return '%d %d %s' % (self.algorithm, + self.fp_type, + dns.rdata._hexify(self.fingerprint, + chunksize=128)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + algorithm = tok.get_uint8() + fp_type = tok.get_uint8() + fingerprint = tok.get_string() + fingerprint = fingerprint.decode('hex_codec') + tok.get_eol() + return cls(rdclass, rdtype, algorithm, fp_type, fingerprint) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + header = struct.pack("!BB", self.algorithm, self.fp_type) + file.write(header) + file.write(self.fingerprint) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + header = struct.unpack("!BB", wire[current : current + 2]) + current += 2 + rdlen -= 2 + fingerprint = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, header[0], header[1], fingerprint) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + hs = struct.pack("!BB", self.algorithm, self.fp_type) + ho = struct.pack("!BB", other.algorithm, other.fp_type) + v = cmp(hs, ho) + if v == 0: + v = cmp(self.fingerprint, other.fingerprint) + return v diff --git a/third_party/dnspython/dns/rdtypes/ANY/TXT.py b/third_party/dnspython/dns/rdtypes/ANY/TXT.py new file mode 100644 index 0000000000..604fd0f4de --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/TXT.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.txtbase + +class TXT(dns.rdtypes.txtbase.TXTBase): + """TXT record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/ANY/X25.py b/third_party/dnspython/dns/rdtypes/ANY/X25.py new file mode 100644 index 0000000000..ae91295f8c --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/X25.py @@ -0,0 +1,62 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.rdata +import dns.tokenizer + +class X25(dns.rdata.Rdata): + """X25 record + + @ivar address: the PSDN address + @type address: string + @see: RFC 1183""" + + __slots__ = ['address'] + + def __init__(self, rdclass, rdtype, address): + super(X25, self).__init__(rdclass, rdtype) + self.address = address + + def to_text(self, origin=None, relativize=True, **kw): + return '"%s"' % dns.rdata._escapify(self.address) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + address = tok.get_string() + tok.get_eol() + return cls(rdclass, rdtype, address) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + l = len(self.address) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(self.address) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l != rdlen: + raise dns.exception.FormError + address = wire[current : current + l].unwrap() + return cls(rdclass, rdtype, address) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + return cmp(self.address, other.address) diff --git a/third_party/dnspython/dns/rdtypes/ANY/__init__.py b/third_party/dnspython/dns/rdtypes/ANY/__init__.py new file mode 100644 index 0000000000..721e9dd60b --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/ANY/__init__.py @@ -0,0 +1,45 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Class ANY (generic) rdata type classes.""" + +__all__ = [ + 'AFSDB', + 'CERT', + 'CNAME', + 'DLV', + 'DNAME', + 'DNSKEY', + 'DS', + 'GPOS', + 'HINFO', + 'HIP', + 'ISDN', + 'LOC', + 'MX', + 'NS', + 'NSEC', + 'NSEC3', + 'NSEC3PARAM', + 'PTR', + 'RP', + 'RRSIG', + 'RT', + 'SOA', + 'SPF', + 'SSHFP', + 'TXT', + 'X25', +] diff --git a/third_party/dnspython/dns/rdtypes/IN/A.py b/third_party/dnspython/dns/rdtypes/IN/A.py new file mode 100644 index 0000000000..372d333226 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/A.py @@ -0,0 +1,57 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.ipv4 +import dns.rdata +import dns.tokenizer + +class A(dns.rdata.Rdata): + """A record. + + @ivar address: an IPv4 address + @type address: string (in the standard "dotted quad" format)""" + + __slots__ = ['address'] + + def __init__(self, rdclass, rdtype, address): + super(A, self).__init__(rdclass, rdtype) + # check that it's OK + junk = dns.ipv4.inet_aton(address) + self.address = address + + def to_text(self, origin=None, relativize=True, **kw): + return self.address + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + address = tok.get_identifier() + tok.get_eol() + return cls(rdclass, rdtype, address) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + file.write(dns.ipv4.inet_aton(self.address)) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + address = dns.ipv4.inet_ntoa(wire[current : current + rdlen]) + return cls(rdclass, rdtype, address) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + sa = dns.ipv4.inet_aton(self.address) + oa = dns.ipv4.inet_aton(other.address) + return cmp(sa, oa) diff --git a/third_party/dnspython/dns/rdtypes/IN/AAAA.py b/third_party/dnspython/dns/rdtypes/IN/AAAA.py new file mode 100644 index 0000000000..e131bd5070 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/AAAA.py @@ -0,0 +1,58 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.inet +import dns.rdata +import dns.tokenizer + +class AAAA(dns.rdata.Rdata): + """AAAA record. + + @ivar address: an IPv6 address + @type address: string (in the standard IPv6 format)""" + + __slots__ = ['address'] + + def __init__(self, rdclass, rdtype, address): + super(AAAA, self).__init__(rdclass, rdtype) + # check that it's OK + junk = dns.inet.inet_pton(dns.inet.AF_INET6, address) + self.address = address + + def to_text(self, origin=None, relativize=True, **kw): + return self.address + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + address = tok.get_identifier() + tok.get_eol() + return cls(rdclass, rdtype, address) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address)) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + address = dns.inet.inet_ntop(dns.inet.AF_INET6, + wire[current : current + rdlen]) + return cls(rdclass, rdtype, address) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + sa = dns.inet.inet_pton(dns.inet.AF_INET6, self.address) + oa = dns.inet.inet_pton(dns.inet.AF_INET6, other.address) + return cmp(sa, oa) diff --git a/third_party/dnspython/dns/rdtypes/IN/APL.py b/third_party/dnspython/dns/rdtypes/IN/APL.py new file mode 100644 index 0000000000..260fd6f39f --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/APL.py @@ -0,0 +1,170 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import struct + +import dns.exception +import dns.inet +import dns.rdata +import dns.tokenizer + +class APLItem(object): + """An APL list item. + + @ivar family: the address family (IANA address family registry) + @type family: int + @ivar negation: is this item negated? + @type negation: bool + @ivar address: the address + @type address: string + @ivar prefix: the prefix length + @type prefix: int + """ + + __slots__ = ['family', 'negation', 'address', 'prefix'] + + def __init__(self, family, negation, address, prefix): + self.family = family + self.negation = negation + self.address = address + self.prefix = prefix + + def __str__(self): + if self.negation: + return "!%d:%s/%s" % (self.family, self.address, self.prefix) + else: + return "%d:%s/%s" % (self.family, self.address, self.prefix) + + def to_wire(self, file): + if self.family == 1: + address = dns.inet.inet_pton(dns.inet.AF_INET, self.address) + elif self.family == 2: + address = dns.inet.inet_pton(dns.inet.AF_INET6, self.address) + else: + address = self.address.decode('hex_codec') + # + # Truncate least significant zero bytes. + # + last = 0 + for i in xrange(len(address) - 1, -1, -1): + if address[i] != chr(0): + last = i + 1 + break + address = address[0 : last] + l = len(address) + assert l < 128 + if self.negation: + l |= 0x80 + header = struct.pack('!HBB', self.family, self.prefix, l) + file.write(header) + file.write(address) + +class APL(dns.rdata.Rdata): + """APL record. + + @ivar items: a list of APL items + @type items: list of APL_Item + @see: RFC 3123""" + + __slots__ = ['items'] + + def __init__(self, rdclass, rdtype, items): + super(APL, self).__init__(rdclass, rdtype) + self.items = items + + def to_text(self, origin=None, relativize=True, **kw): + return ' '.join(map(lambda x: str(x), self.items)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + items = [] + while 1: + token = tok.get().unescape() + if token.is_eol_or_eof(): + break + item = token.value + if item[0] == '!': + negation = True + item = item[1:] + else: + negation = False + (family, rest) = item.split(':', 1) + family = int(family) + (address, prefix) = rest.split('/', 1) + prefix = int(prefix) + item = APLItem(family, negation, address, prefix) + items.append(item) + + return cls(rdclass, rdtype, items) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + for item in self.items: + item.to_wire(file) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + items = [] + while 1: + if rdlen < 4: + raise dns.exception.FormError + header = struct.unpack('!HBB', wire[current : current + 4]) + afdlen = header[2] + if afdlen > 127: + negation = True + afdlen -= 128 + else: + negation = False + current += 4 + rdlen -= 4 + if rdlen < afdlen: + raise dns.exception.FormError + address = wire[current : current + afdlen].unwrap() + l = len(address) + if header[0] == 1: + if l < 4: + address += '\x00' * (4 - l) + address = dns.inet.inet_ntop(dns.inet.AF_INET, address) + elif header[0] == 2: + if l < 16: + address += '\x00' * (16 - l) + address = dns.inet.inet_ntop(dns.inet.AF_INET6, address) + else: + # + # This isn't really right according to the RFC, but it + # seems better than throwing an exception + # + address = address.encode('hex_codec') + current += afdlen + rdlen -= afdlen + item = APLItem(header[0], negation, address, header[1]) + items.append(item) + if rdlen == 0: + break + return cls(rdclass, rdtype, items) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + f = cStringIO.StringIO() + self.to_wire(f) + wire1 = f.getvalue() + f.seek(0) + f.truncate() + other.to_wire(f) + wire2 = f.getvalue() + f.close() + + return cmp(wire1, wire2) diff --git a/third_party/dnspython/dns/rdtypes/IN/DHCID.py b/third_party/dnspython/dns/rdtypes/IN/DHCID.py new file mode 100644 index 0000000000..5524beadd1 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/DHCID.py @@ -0,0 +1,60 @@ +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception + +class DHCID(dns.rdata.Rdata): + """DHCID record + + @ivar data: the data (the content of the RR is opaque as far as the + DNS is concerned) + @type data: string + @see: RFC 4701""" + + __slots__ = ['data'] + + def __init__(self, rdclass, rdtype, data): + super(DHCID, self).__init__(rdclass, rdtype) + self.data = data + + def to_text(self, origin=None, relativize=True, **kw): + return dns.rdata._base64ify(self.data) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + chunks.append(t.value) + b64 = ''.join(chunks) + data = b64.decode('base64_codec') + return cls(rdclass, rdtype, data) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + file.write(self.data) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + data = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, data) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + return cmp(self.data, other.data) diff --git a/third_party/dnspython/dns/rdtypes/IN/IPSECKEY.py b/third_party/dnspython/dns/rdtypes/IN/IPSECKEY.py new file mode 100644 index 0000000000..d85b6fe993 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/IPSECKEY.py @@ -0,0 +1,159 @@ +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import struct + +import dns.exception +import dns.inet +import dns.name + +class IPSECKEY(dns.rdata.Rdata): + """IPSECKEY record + + @ivar precedence: the precedence for this key data + @type precedence: int + @ivar gateway_type: the gateway type + @type gateway_type: int + @ivar algorithm: the algorithm to use + @type algorithm: int + @ivar gateway: the public key + @type gateway: None, IPv4 address, IPV6 address, or domain name + @ivar key: the public key + @type key: string + @see: RFC 4025""" + + __slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key'] + + def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm, + gateway, key): + super(IPSECKEY, self).__init__(rdclass, rdtype) + if gateway_type == 0: + if gateway != '.' and not gateway is None: + raise SyntaxError('invalid gateway for gateway type 0') + gateway = None + elif gateway_type == 1: + # check that it's OK + junk = dns.inet.inet_pton(dns.inet.AF_INET, gateway) + elif gateway_type == 2: + # check that it's OK + junk = dns.inet.inet_pton(dns.inet.AF_INET6, gateway) + elif gateway_type == 3: + pass + else: + raise SyntaxError('invalid IPSECKEY gateway type: %d' % gateway_type) + self.precedence = precedence + self.gateway_type = gateway_type + self.algorithm = algorithm + self.gateway = gateway + self.key = key + + def to_text(self, origin=None, relativize=True, **kw): + if self.gateway_type == 0: + gateway = '.' + elif self.gateway_type == 1: + gateway = self.gateway + elif self.gateway_type == 2: + gateway = self.gateway + elif self.gateway_type == 3: + gateway = str(self.gateway.choose_relativity(origin, relativize)) + else: + raise ValueError('invalid gateway type') + return '%d %d %d %s %s' % (self.precedence, self.gateway_type, + self.algorithm, gateway, + dns.rdata._base64ify(self.key)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + precedence = tok.get_uint8() + gateway_type = tok.get_uint8() + algorithm = tok.get_uint8() + if gateway_type == 3: + gateway = tok.get_name().choose_relativity(origin, relativize) + else: + gateway = tok.get_string() + chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + chunks.append(t.value) + b64 = ''.join(chunks) + key = b64.decode('base64_codec') + return cls(rdclass, rdtype, precedence, gateway_type, algorithm, + gateway, key) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + header = struct.pack("!BBB", self.precedence, self.gateway_type, + self.algorithm) + file.write(header) + if self.gateway_type == 0: + pass + elif self.gateway_type == 1: + file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway)) + elif self.gateway_type == 2: + file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway)) + elif self.gateway_type == 3: + self.gateway.to_wire(file, None, origin) + else: + raise ValueError('invalid gateway type') + file.write(self.key) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + if rdlen < 3: + raise dns.exception.FormError + header = struct.unpack('!BBB', wire[current : current + 3]) + gateway_type = header[1] + current += 3 + rdlen -= 3 + if gateway_type == 0: + gateway = None + elif gateway_type == 1: + gateway = dns.inet.inet_ntop(dns.inet.AF_INET, + wire[current : current + 4]) + current += 4 + rdlen -= 4 + elif gateway_type == 2: + gateway = dns.inet.inet_ntop(dns.inet.AF_INET6, + wire[current : current + 16]) + current += 16 + rdlen -= 16 + elif gateway_type == 3: + (gateway, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + current += cused + rdlen -= cused + else: + raise dns.exception.FormError('invalid IPSECKEY gateway type') + key = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, header[0], gateway_type, header[2], + gateway, key) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + f = cStringIO.StringIO() + self.to_wire(f) + wire1 = f.getvalue() + f.seek(0) + f.truncate() + other.to_wire(f) + wire2 = f.getvalue() + f.close() + + return cmp(wire1, wire2) diff --git a/third_party/dnspython/dns/rdtypes/IN/KX.py b/third_party/dnspython/dns/rdtypes/IN/KX.py new file mode 100644 index 0000000000..c7bd5bbe65 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/KX.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.mxbase + +class KX(dns.rdtypes.mxbase.UncompressedMX): + """KX record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/IN/NAPTR.py b/third_party/dnspython/dns/rdtypes/IN/NAPTR.py new file mode 100644 index 0000000000..7fe043082b --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/NAPTR.py @@ -0,0 +1,132 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.name +import dns.rdata + +def _write_string(file, s): + l = len(s) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(s) + +class NAPTR(dns.rdata.Rdata): + """NAPTR record + + @ivar order: order + @type order: int + @ivar preference: preference + @type preference: int + @ivar flags: flags + @type flags: string + @ivar service: service + @type service: string + @ivar regexp: regular expression + @type regexp: string + @ivar replacement: replacement name + @type replacement: dns.name.Name object + @see: RFC 3403""" + + __slots__ = ['order', 'preference', 'flags', 'service', 'regexp', + 'replacement'] + + def __init__(self, rdclass, rdtype, order, preference, flags, service, + regexp, replacement): + super(NAPTR, self).__init__(rdclass, rdtype) + self.order = order + self.preference = preference + self.flags = flags + self.service = service + self.regexp = regexp + self.replacement = replacement + + def to_text(self, origin=None, relativize=True, **kw): + replacement = self.replacement.choose_relativity(origin, relativize) + return '%d %d "%s" "%s" "%s" %s' % \ + (self.order, self.preference, + dns.rdata._escapify(self.flags), + dns.rdata._escapify(self.service), + dns.rdata._escapify(self.regexp), + self.replacement) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + order = tok.get_uint16() + preference = tok.get_uint16() + flags = tok.get_string() + service = tok.get_string() + regexp = tok.get_string() + replacement = tok.get_name() + replacement = replacement.choose_relativity(origin, relativize) + tok.get_eol() + return cls(rdclass, rdtype, order, preference, flags, service, + regexp, replacement) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + two_ints = struct.pack("!HH", self.order, self.preference) + file.write(two_ints) + _write_string(file, self.flags) + _write_string(file, self.service) + _write_string(file, self.regexp) + self.replacement.to_wire(file, compress, origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (order, preference) = struct.unpack('!HH', wire[current : current + 4]) + current += 4 + rdlen -= 4 + strings = [] + for i in xrange(3): + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l > rdlen or rdlen < 0: + raise dns.exception.FormError + s = wire[current : current + l].unwrap() + current += l + rdlen -= l + strings.append(s) + (replacement, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused != rdlen: + raise dns.exception.FormError + if not origin is None: + replacement = replacement.relativize(origin) + return cls(rdclass, rdtype, order, preference, strings[0], strings[1], + strings[2], replacement) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.replacement = self.replacement.choose_relativity(origin, + relativize) + + def _cmp(self, other): + sp = struct.pack("!HH", self.order, self.preference) + op = struct.pack("!HH", other.order, other.preference) + v = cmp(sp, op) + if v == 0: + v = cmp(self.flags, other.flags) + if v == 0: + v = cmp(self.service, other.service) + if v == 0: + v = cmp(self.regexp, other.regexp) + if v == 0: + v = cmp(self.replacement, other.replacement) + return v diff --git a/third_party/dnspython/dns/rdtypes/IN/NSAP.py b/third_party/dnspython/dns/rdtypes/IN/NSAP.py new file mode 100644 index 0000000000..216cb0a81a --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/NSAP.py @@ -0,0 +1,59 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.rdata +import dns.tokenizer + +class NSAP(dns.rdata.Rdata): + """NSAP record. + + @ivar address: a NASP + @type address: string + @see: RFC 1706""" + + __slots__ = ['address'] + + def __init__(self, rdclass, rdtype, address): + super(NSAP, self).__init__(rdclass, rdtype) + self.address = address + + def to_text(self, origin=None, relativize=True, **kw): + return "0x%s" % self.address.encode('hex_codec') + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + address = tok.get_string() + t = tok.get_eol() + if address[0:2] != '0x': + raise dns.exception.SyntaxError('string does not start with 0x') + address = address[2:].replace('.', '') + if len(address) % 2 != 0: + raise dns.exception.SyntaxError('hexstring has odd length') + address = address.decode('hex_codec') + return cls(rdclass, rdtype, address) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + file.write(self.address) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + address = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, address) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + return cmp(self.address, other.address) diff --git a/third_party/dnspython/dns/rdtypes/IN/NSAP_PTR.py b/third_party/dnspython/dns/rdtypes/IN/NSAP_PTR.py new file mode 100644 index 0000000000..df5b989ac8 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/NSAP_PTR.py @@ -0,0 +1,20 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.rdtypes.nsbase + +class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS): + """NSAP-PTR record""" + pass diff --git a/third_party/dnspython/dns/rdtypes/IN/PX.py b/third_party/dnspython/dns/rdtypes/IN/PX.py new file mode 100644 index 0000000000..1422b83487 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/PX.py @@ -0,0 +1,97 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.rdata +import dns.name + +class PX(dns.rdata.Rdata): + """PX record. + + @ivar preference: the preference value + @type preference: int + @ivar map822: the map822 name + @type map822: dns.name.Name object + @ivar mapx400: the mapx400 name + @type mapx400: dns.name.Name object + @see: RFC 2163""" + + __slots__ = ['preference', 'map822', 'mapx400'] + + def __init__(self, rdclass, rdtype, preference, map822, mapx400): + super(PX, self).__init__(rdclass, rdtype) + self.preference = preference + self.map822 = map822 + self.mapx400 = mapx400 + + def to_text(self, origin=None, relativize=True, **kw): + map822 = self.map822.choose_relativity(origin, relativize) + mapx400 = self.mapx400.choose_relativity(origin, relativize) + return '%d %s %s' % (self.preference, map822, mapx400) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + preference = tok.get_uint16() + map822 = tok.get_name() + map822 = map822.choose_relativity(origin, relativize) + mapx400 = tok.get_name(None) + mapx400 = mapx400.choose_relativity(origin, relativize) + tok.get_eol() + return cls(rdclass, rdtype, preference, map822, mapx400) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + pref = struct.pack("!H", self.preference) + file.write(pref) + self.map822.to_wire(file, None, origin) + self.mapx400.to_wire(file, None, origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (preference, ) = struct.unpack('!H', wire[current : current + 2]) + current += 2 + rdlen -= 2 + (map822, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused > rdlen: + raise dns.exception.FormError + current += cused + rdlen -= cused + if not origin is None: + map822 = map822.relativize(origin) + (mapx400, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused != rdlen: + raise dns.exception.FormError + if not origin is None: + mapx400 = mapx400.relativize(origin) + return cls(rdclass, rdtype, preference, map822, mapx400) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.map822 = self.map822.choose_relativity(origin, relativize) + self.mapx400 = self.mapx400.choose_relativity(origin, relativize) + + def _cmp(self, other): + sp = struct.pack("!H", self.preference) + op = struct.pack("!H", other.preference) + v = cmp(sp, op) + if v == 0: + v = cmp(self.map822, other.map822) + if v == 0: + v = cmp(self.mapx400, other.mapx400) + return v diff --git a/third_party/dnspython/dns/rdtypes/IN/SRV.py b/third_party/dnspython/dns/rdtypes/IN/SRV.py new file mode 100644 index 0000000000..e101b26bea --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/SRV.py @@ -0,0 +1,89 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.rdata +import dns.name + +class SRV(dns.rdata.Rdata): + """SRV record + + @ivar priority: the priority + @type priority: int + @ivar weight: the weight + @type weight: int + @ivar port: the port of the service + @type port: int + @ivar target: the target host + @type target: dns.name.Name object + @see: RFC 2782""" + + __slots__ = ['priority', 'weight', 'port', 'target'] + + def __init__(self, rdclass, rdtype, priority, weight, port, target): + super(SRV, self).__init__(rdclass, rdtype) + self.priority = priority + self.weight = weight + self.port = port + self.target = target + + def to_text(self, origin=None, relativize=True, **kw): + target = self.target.choose_relativity(origin, relativize) + return '%d %d %d %s' % (self.priority, self.weight, self.port, + target) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + priority = tok.get_uint16() + weight = tok.get_uint16() + port = tok.get_uint16() + target = tok.get_name(None) + target = target.choose_relativity(origin, relativize) + tok.get_eol() + return cls(rdclass, rdtype, priority, weight, port, target) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) + file.write(three_ints) + self.target.to_wire(file, compress, origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (priority, weight, port) = struct.unpack('!HHH', + wire[current : current + 6]) + current += 6 + rdlen -= 6 + (target, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused != rdlen: + raise dns.exception.FormError + if not origin is None: + target = target.relativize(origin) + return cls(rdclass, rdtype, priority, weight, port, target) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.target = self.target.choose_relativity(origin, relativize) + + def _cmp(self, other): + sp = struct.pack("!HHH", self.priority, self.weight, self.port) + op = struct.pack("!HHH", other.priority, other.weight, other.port) + v = cmp(sp, op) + if v == 0: + v = cmp(self.target, other.target) + return v diff --git a/third_party/dnspython/dns/rdtypes/IN/WKS.py b/third_party/dnspython/dns/rdtypes/IN/WKS.py new file mode 100644 index 0000000000..04c3054e44 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/WKS.py @@ -0,0 +1,113 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import socket +import struct + +import dns.ipv4 +import dns.rdata + +_proto_tcp = socket.getprotobyname('tcp') +_proto_udp = socket.getprotobyname('udp') + +class WKS(dns.rdata.Rdata): + """WKS record + + @ivar address: the address + @type address: string + @ivar protocol: the protocol + @type protocol: int + @ivar bitmap: the bitmap + @type bitmap: string + @see: RFC 1035""" + + __slots__ = ['address', 'protocol', 'bitmap'] + + def __init__(self, rdclass, rdtype, address, protocol, bitmap): + super(WKS, self).__init__(rdclass, rdtype) + self.address = address + self.protocol = protocol + self.bitmap = bitmap + + def to_text(self, origin=None, relativize=True, **kw): + bits = [] + for i in xrange(0, len(self.bitmap)): + byte = ord(self.bitmap[i]) + for j in xrange(0, 8): + if byte & (0x80 >> j): + bits.append(str(i * 8 + j)) + text = ' '.join(bits) + return '%s %d %s' % (self.address, self.protocol, text) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + address = tok.get_string() + protocol = tok.get_string() + if protocol.isdigit(): + protocol = int(protocol) + else: + protocol = socket.getprotobyname(protocol) + bitmap = [] + while 1: + token = tok.get().unescape() + if token.is_eol_or_eof(): + break + if token.value.isdigit(): + serv = int(token.value) + else: + if protocol != _proto_udp and protocol != _proto_tcp: + raise NotImplementedError("protocol must be TCP or UDP") + if protocol == _proto_udp: + protocol_text = "udp" + else: + protocol_text = "tcp" + serv = socket.getservbyname(token.value, protocol_text) + i = serv // 8 + l = len(bitmap) + if l < i + 1: + for j in xrange(l, i + 1): + bitmap.append('\x00') + bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (serv % 8))) + bitmap = dns.rdata._truncate_bitmap(bitmap) + return cls(rdclass, rdtype, address, protocol, bitmap) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + file.write(dns.ipv4.inet_aton(self.address)) + protocol = struct.pack('!B', self.protocol) + file.write(protocol) + file.write(self.bitmap) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + address = dns.ipv4.inet_ntoa(wire[current : current + 4]) + protocol, = struct.unpack('!B', wire[current + 4 : current + 5]) + current += 5 + rdlen -= 5 + bitmap = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, address, protocol, bitmap) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + sa = dns.ipv4.inet_aton(self.address) + oa = dns.ipv4.inet_aton(other.address) + v = cmp(sa, oa) + if v == 0: + sp = struct.pack('!B', self.protocol) + op = struct.pack('!B', other.protocol) + v = cmp(sp, op) + if v == 0: + v = cmp(self.bitmap, other.bitmap) + return v diff --git a/third_party/dnspython/dns/rdtypes/IN/__init__.py b/third_party/dnspython/dns/rdtypes/IN/__init__.py new file mode 100644 index 0000000000..24cf1ece4e --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/IN/__init__.py @@ -0,0 +1,30 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Class IN rdata type classes.""" + +__all__ = [ + 'A', + 'AAAA', + 'APL', + 'DHCID', + 'KX', + 'NAPTR', + 'NSAP', + 'NSAP_PTR', + 'PX', + 'SRV', + 'WKS', +] diff --git a/third_party/dnspython/dns/rdtypes/__init__.py b/third_party/dnspython/dns/rdtypes/__init__.py new file mode 100644 index 0000000000..49db5a3776 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/__init__.py @@ -0,0 +1,23 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS rdata type classes""" + +__all__ = [ + 'ANY', + 'IN', + 'mxbase', + 'nsbase', +] diff --git a/third_party/dnspython/dns/rdtypes/dsbase.py b/third_party/dnspython/dns/rdtypes/dsbase.py new file mode 100644 index 0000000000..6f5559a7e4 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/dsbase.py @@ -0,0 +1,92 @@ +# Copyright (C) 2010, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.rdata +import dns.rdatatype + +class DSBase(dns.rdata.Rdata): + """Base class for rdata that is like a DS record + + @ivar key_tag: the key tag + @type key_tag: int + @ivar algorithm: the algorithm + @type algorithm: int + @ivar digest_type: the digest type + @type digest_type: int + @ivar digest: the digest + @type digest: int + @see: draft-ietf-dnsext-delegation-signer-14.txt""" + + __slots__ = ['key_tag', 'algorithm', 'digest_type', 'digest'] + + def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, + digest): + super(DSBase, self).__init__(rdclass, rdtype) + self.key_tag = key_tag + self.algorithm = algorithm + self.digest_type = digest_type + self.digest = digest + + def to_text(self, origin=None, relativize=True, **kw): + return '%d %d %d %s' % (self.key_tag, self.algorithm, + self.digest_type, + dns.rdata._hexify(self.digest, + chunksize=128)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + key_tag = tok.get_uint16() + algorithm = tok.get_uint8() + digest_type = tok.get_uint8() + chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + chunks.append(t.value) + digest = ''.join(chunks) + digest = digest.decode('hex_codec') + return cls(rdclass, rdtype, key_tag, algorithm, digest_type, + digest) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + header = struct.pack("!HBB", self.key_tag, self.algorithm, + self.digest_type) + file.write(header) + file.write(self.digest) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + header = struct.unpack("!HBB", wire[current : current + 4]) + current += 4 + rdlen -= 4 + digest = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, header[0], header[1], header[2], digest) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + hs = struct.pack("!HBB", self.key_tag, self.algorithm, + self.digest_type) + ho = struct.pack("!HBB", other.key_tag, other.algorithm, + other.digest_type) + v = cmp(hs, ho) + if v == 0: + v = cmp(self.digest, other.digest) + return v diff --git a/third_party/dnspython/dns/rdtypes/mxbase.py b/third_party/dnspython/dns/rdtypes/mxbase.py new file mode 100644 index 0000000000..abc6a9ed5b --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/mxbase.py @@ -0,0 +1,105 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""MX-like base classes.""" + +import cStringIO +import struct + +import dns.exception +import dns.rdata +import dns.name + +class MXBase(dns.rdata.Rdata): + """Base class for rdata that is like an MX record. + + @ivar preference: the preference value + @type preference: int + @ivar exchange: the exchange name + @type exchange: dns.name.Name object""" + + __slots__ = ['preference', 'exchange'] + + def __init__(self, rdclass, rdtype, preference, exchange): + super(MXBase, self).__init__(rdclass, rdtype) + self.preference = preference + self.exchange = exchange + + def to_text(self, origin=None, relativize=True, **kw): + exchange = self.exchange.choose_relativity(origin, relativize) + return '%d %s' % (self.preference, exchange) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + preference = tok.get_uint16() + exchange = tok.get_name() + exchange = exchange.choose_relativity(origin, relativize) + tok.get_eol() + return cls(rdclass, rdtype, preference, exchange) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + pref = struct.pack("!H", self.preference) + file.write(pref) + self.exchange.to_wire(file, compress, origin) + + def to_digestable(self, origin = None): + return struct.pack("!H", self.preference) + \ + self.exchange.to_digestable(origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (preference, ) = struct.unpack('!H', wire[current : current + 2]) + current += 2 + rdlen -= 2 + (exchange, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused != rdlen: + raise dns.exception.FormError + if not origin is None: + exchange = exchange.relativize(origin) + return cls(rdclass, rdtype, preference, exchange) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.exchange = self.exchange.choose_relativity(origin, relativize) + + def _cmp(self, other): + sp = struct.pack("!H", self.preference) + op = struct.pack("!H", other.preference) + v = cmp(sp, op) + if v == 0: + v = cmp(self.exchange, other.exchange) + return v + +class UncompressedMX(MXBase): + """Base class for rdata that is like an MX record, but whose name + is not compressed when converted to DNS wire format, and whose + digestable form is not downcased.""" + + def to_wire(self, file, compress = None, origin = None): + super(UncompressedMX, self).to_wire(file, None, origin) + + def to_digestable(self, origin = None): + f = cStringIO.StringIO() + self.to_wire(f, None, origin) + return f.getvalue() + +class UncompressedDowncasingMX(MXBase): + """Base class for rdata that is like an MX record, but whose name + is not compressed when convert to DNS wire format.""" + + def to_wire(self, file, compress = None, origin = None): + super(UncompressedDowncasingMX, self).to_wire(file, None, origin) diff --git a/third_party/dnspython/dns/rdtypes/nsbase.py b/third_party/dnspython/dns/rdtypes/nsbase.py new file mode 100644 index 0000000000..fbd5ef1e51 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/nsbase.py @@ -0,0 +1,82 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""NS-like base classes.""" + +import cStringIO + +import dns.exception +import dns.rdata +import dns.name + +class NSBase(dns.rdata.Rdata): + """Base class for rdata that is like an NS record. + + @ivar target: the target name of the rdata + @type target: dns.name.Name object""" + + __slots__ = ['target'] + + def __init__(self, rdclass, rdtype, target): + super(NSBase, self).__init__(rdclass, rdtype) + self.target = target + + def to_text(self, origin=None, relativize=True, **kw): + target = self.target.choose_relativity(origin, relativize) + return str(target) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + target = tok.get_name() + target = target.choose_relativity(origin, relativize) + tok.get_eol() + return cls(rdclass, rdtype, target) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + self.target.to_wire(file, compress, origin) + + def to_digestable(self, origin = None): + return self.target.to_digestable(origin) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + (target, cused) = dns.name.from_wire(wire[: current + rdlen], + current) + if cused != rdlen: + raise dns.exception.FormError + if not origin is None: + target = target.relativize(origin) + return cls(rdclass, rdtype, target) + + from_wire = classmethod(from_wire) + + def choose_relativity(self, origin = None, relativize = True): + self.target = self.target.choose_relativity(origin, relativize) + + def _cmp(self, other): + return cmp(self.target, other.target) + +class UncompressedNS(NSBase): + """Base class for rdata that is like an NS record, but whose name + is not compressed when convert to DNS wire format, and whose + digestable form is not downcased.""" + + def to_wire(self, file, compress = None, origin = None): + super(UncompressedNS, self).to_wire(file, None, origin) + + def to_digestable(self, origin = None): + f = cStringIO.StringIO() + self.to_wire(f, None, origin) + return f.getvalue() diff --git a/third_party/dnspython/dns/rdtypes/txtbase.py b/third_party/dnspython/dns/rdtypes/txtbase.py new file mode 100644 index 0000000000..580f056ea0 --- /dev/null +++ b/third_party/dnspython/dns/rdtypes/txtbase.py @@ -0,0 +1,87 @@ +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""TXT-like base class.""" + +import dns.exception +import dns.rdata +import dns.tokenizer + +class TXTBase(dns.rdata.Rdata): + """Base class for rdata that is like a TXT record + + @ivar strings: the text strings + @type strings: list of string + @see: RFC 1035""" + + __slots__ = ['strings'] + + def __init__(self, rdclass, rdtype, strings): + super(TXTBase, self).__init__(rdclass, rdtype) + if isinstance(strings, str): + strings = [ strings ] + self.strings = strings[:] + + def to_text(self, origin=None, relativize=True, **kw): + txt = '' + prefix = '' + for s in self.strings: + txt += '%s"%s"' % (prefix, dns.rdata._escapify(s)) + prefix = ' ' + return txt + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + strings = [] + while 1: + token = tok.get().unescape() + if token.is_eol_or_eof(): + break + if not (token.is_quoted_string() or token.is_identifier()): + raise dns.exception.SyntaxError("expected a string") + if len(token.value) > 255: + raise dns.exception.SyntaxError("string too long") + strings.append(token.value) + if len(strings) == 0: + raise dns.exception.UnexpectedEnd + return cls(rdclass, rdtype, strings) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + for s in self.strings: + l = len(s) + assert l < 256 + byte = chr(l) + file.write(byte) + file.write(s) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + strings = [] + while rdlen > 0: + l = ord(wire[current]) + current += 1 + rdlen -= 1 + if l > rdlen: + raise dns.exception.FormError + s = wire[current : current + l].unwrap() + current += l + rdlen -= l + strings.append(s) + return cls(rdclass, rdtype, strings) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + return cmp(self.strings, other.strings) diff --git a/third_party/dnspython/dns/renderer.py b/third_party/dnspython/dns/renderer.py new file mode 100644 index 0000000000..ad3f83d450 --- /dev/null +++ b/third_party/dnspython/dns/renderer.py @@ -0,0 +1,325 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Help for building DNS wire format messages""" + +import cStringIO +import struct +import random +import time + +import dns.exception +import dns.tsig + +QUESTION = 0 +ANSWER = 1 +AUTHORITY = 2 +ADDITIONAL = 3 + +class Renderer(object): + """Helper class for building DNS wire-format messages. + + Most applications can use the higher-level L{dns.message.Message} + class and its to_wire() method to generate wire-format messages. + This class is for those applications which need finer control + over the generation of messages. + + Typical use:: + + r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512) + r.add_question(qname, qtype, qclass) + r.add_rrset(dns.renderer.ANSWER, rrset_1) + r.add_rrset(dns.renderer.ANSWER, rrset_2) + r.add_rrset(dns.renderer.AUTHORITY, ns_rrset) + r.add_edns(0, 0, 4096) + r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_1) + r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_2) + r.write_header() + r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) + wire = r.get_wire() + + @ivar output: where rendering is written + @type output: cStringIO.StringIO object + @ivar id: the message id + @type id: int + @ivar flags: the message flags + @type flags: int + @ivar max_size: the maximum size of the message + @type max_size: int + @ivar origin: the origin to use when rendering relative names + @type origin: dns.name.Name object + @ivar compress: the compression table + @type compress: dict + @ivar section: the section currently being rendered + @type section: int (dns.renderer.QUESTION, dns.renderer.ANSWER, + dns.renderer.AUTHORITY, or dns.renderer.ADDITIONAL) + @ivar counts: list of the number of RRs in each section + @type counts: int list of length 4 + @ivar mac: the MAC of the rendered message (if TSIG was used) + @type mac: string + """ + + def __init__(self, id=None, flags=0, max_size=65535, origin=None): + """Initialize a new renderer. + + @param id: the message id + @type id: int + @param flags: the DNS message flags + @type flags: int + @param max_size: the maximum message size; the default is 65535. + If rendering results in a message greater than I{max_size}, + then L{dns.exception.TooBig} will be raised. + @type max_size: int + @param origin: the origin to use when rendering relative names + @type origin: dns.name.Namem or None. + """ + + self.output = cStringIO.StringIO() + if id is None: + self.id = random.randint(0, 65535) + else: + self.id = id + self.flags = flags + self.max_size = max_size + self.origin = origin + self.compress = {} + self.section = QUESTION + self.counts = [0, 0, 0, 0] + self.output.write('\x00' * 12) + self.mac = '' + + def _rollback(self, where): + """Truncate the output buffer at offset I{where}, and remove any + compression table entries that pointed beyond the truncation + point. + + @param where: the offset + @type where: int + """ + + self.output.seek(where) + self.output.truncate() + keys_to_delete = [] + for k, v in self.compress.iteritems(): + if v >= where: + keys_to_delete.append(k) + for k in keys_to_delete: + del self.compress[k] + + def _set_section(self, section): + """Set the renderer's current section. + + Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, + ADDITIONAL. Sections may be empty. + + @param section: the section + @type section: int + @raises dns.exception.FormError: an attempt was made to set + a section value less than the current section. + """ + + if self.section != section: + if self.section > section: + raise dns.exception.FormError + self.section = section + + def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): + """Add a question to the message. + + @param qname: the question name + @type qname: dns.name.Name + @param rdtype: the question rdata type + @type rdtype: int + @param rdclass: the question rdata class + @type rdclass: int + """ + + self._set_section(QUESTION) + before = self.output.tell() + qname.to_wire(self.output, self.compress, self.origin) + self.output.write(struct.pack("!HH", rdtype, rdclass)) + after = self.output.tell() + if after >= self.max_size: + self._rollback(before) + raise dns.exception.TooBig + self.counts[QUESTION] += 1 + + def add_rrset(self, section, rrset, **kw): + """Add the rrset to the specified section. + + Any keyword arguments are passed on to the rdataset's to_wire() + routine. + + @param section: the section + @type section: int + @param rrset: the rrset + @type rrset: dns.rrset.RRset object + """ + + self._set_section(section) + before = self.output.tell() + n = rrset.to_wire(self.output, self.compress, self.origin, **kw) + after = self.output.tell() + if after >= self.max_size: + self._rollback(before) + raise dns.exception.TooBig + self.counts[section] += n + + def add_rdataset(self, section, name, rdataset, **kw): + """Add the rdataset to the specified section, using the specified + name as the owner name. + + Any keyword arguments are passed on to the rdataset's to_wire() + routine. + + @param section: the section + @type section: int + @param name: the owner name + @type name: dns.name.Name object + @param rdataset: the rdataset + @type rdataset: dns.rdataset.Rdataset object + """ + + self._set_section(section) + before = self.output.tell() + n = rdataset.to_wire(name, self.output, self.compress, self.origin, + **kw) + after = self.output.tell() + if after >= self.max_size: + self._rollback(before) + raise dns.exception.TooBig + self.counts[section] += n + + def add_edns(self, edns, ednsflags, payload, options=None): + """Add an EDNS OPT record to the message. + + @param edns: The EDNS level to use. + @type edns: int + @param ednsflags: EDNS flag values. + @type ednsflags: int + @param payload: The EDNS sender's payload field, which is the maximum + size of UDP datagram the sender can handle. + @type payload: int + @param options: The EDNS options list + @type options: list of dns.edns.Option instances + @see: RFC 2671 + """ + + # make sure the EDNS version in ednsflags agrees with edns + ednsflags &= 0xFF00FFFFL + ednsflags |= (edns << 16) + self._set_section(ADDITIONAL) + before = self.output.tell() + self.output.write(struct.pack('!BHHIH', 0, dns.rdatatype.OPT, payload, + ednsflags, 0)) + if not options is None: + lstart = self.output.tell() + for opt in options: + stuff = struct.pack("!HH", opt.otype, 0) + self.output.write(stuff) + start = self.output.tell() + opt.to_wire(self.output) + end = self.output.tell() + assert end - start < 65536 + self.output.seek(start - 2) + stuff = struct.pack("!H", end - start) + self.output.write(stuff) + self.output.seek(0, 2) + lend = self.output.tell() + assert lend - lstart < 65536 + self.output.seek(lstart - 2) + stuff = struct.pack("!H", lend - lstart) + self.output.write(stuff) + self.output.seek(0, 2) + after = self.output.tell() + if after >= self.max_size: + self._rollback(before) + raise dns.exception.TooBig + self.counts[ADDITIONAL] += 1 + + def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data, + request_mac, algorithm=dns.tsig.default_algorithm): + """Add a TSIG signature to the message. + + @param keyname: the TSIG key name + @type keyname: dns.name.Name object + @param secret: the secret to use + @type secret: string + @param fudge: TSIG time fudge + @type fudge: int + @param id: the message id to encode in the tsig signature + @type id: int + @param tsig_error: TSIG error code; default is 0. + @type tsig_error: int + @param other_data: TSIG other data. + @type other_data: string + @param request_mac: This message is a response to the request which + had the specified MAC. + @type request_mac: string + @param algorithm: the TSIG algorithm to use + @type algorithm: dns.name.Name object + """ + + self._set_section(ADDITIONAL) + before = self.output.tell() + s = self.output.getvalue() + (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s, + keyname, + secret, + int(time.time()), + fudge, + id, + tsig_error, + other_data, + request_mac, + algorithm=algorithm) + keyname.to_wire(self.output, self.compress, self.origin) + self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG, + dns.rdataclass.ANY, 0, 0)) + rdata_start = self.output.tell() + self.output.write(tsig_rdata) + after = self.output.tell() + assert after - rdata_start < 65536 + if after >= self.max_size: + self._rollback(before) + raise dns.exception.TooBig + self.output.seek(rdata_start - 2) + self.output.write(struct.pack('!H', after - rdata_start)) + self.counts[ADDITIONAL] += 1 + self.output.seek(10) + self.output.write(struct.pack('!H', self.counts[ADDITIONAL])) + self.output.seek(0, 2) + + def write_header(self): + """Write the DNS message header. + + Writing the DNS message header is done asfter all sections + have been rendered, but before the optional TSIG signature + is added. + """ + + self.output.seek(0) + self.output.write(struct.pack('!HHHHHH', self.id, self.flags, + self.counts[0], self.counts[1], + self.counts[2], self.counts[3])) + self.output.seek(0, 2) + + def get_wire(self): + """Return the wire format message. + + @rtype: string + """ + + return self.output.getvalue() diff --git a/third_party/dnspython/dns/resolver.py b/third_party/dnspython/dns/resolver.py new file mode 100644 index 0000000000..90f95e8ed0 --- /dev/null +++ b/third_party/dnspython/dns/resolver.py @@ -0,0 +1,1161 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS stub resolver. + +@var default_resolver: The default resolver object +@type default_resolver: dns.resolver.Resolver object""" + +import socket +import sys +import time + +import dns.exception +import dns.ipv4 +import dns.ipv6 +import dns.message +import dns.name +import dns.query +import dns.rcode +import dns.rdataclass +import dns.rdatatype +import dns.reversename + +if sys.platform == 'win32': + import _winreg + +class NXDOMAIN(dns.exception.DNSException): + """The query name does not exist.""" + pass + +# The definition of the Timeout exception has moved from here to the +# dns.exception module. We keep dns.resolver.Timeout defined for +# backwards compatibility. + +Timeout = dns.exception.Timeout + +class NoAnswer(dns.exception.DNSException): + """The response did not contain an answer to the question.""" + pass + +class NoNameservers(dns.exception.DNSException): + """No non-broken nameservers are available to answer the query.""" + pass + +class NotAbsolute(dns.exception.DNSException): + """Raised if an absolute domain name is required but a relative name + was provided.""" + pass + +class NoRootSOA(dns.exception.DNSException): + """Raised if for some reason there is no SOA at the root name. + This should never happen!""" + pass + +class NoMetaqueries(dns.exception.DNSException): + """Metaqueries are not allowed.""" + pass + + +class Answer(object): + """DNS stub resolver answer + + Instances of this class bundle up the result of a successful DNS + resolution. + + For convenience, the answer object implements much of the sequence + protocol, forwarding to its rrset. E.g. "for a in answer" is + equivalent to "for a in answer.rrset", "answer[i]" is equivalent + to "answer.rrset[i]", and "answer[i:j]" is equivalent to + "answer.rrset[i:j]". + + Note that CNAMEs or DNAMEs in the response may mean that answer + node's name might not be the query name. + + @ivar qname: The query name + @type qname: dns.name.Name object + @ivar rdtype: The query type + @type rdtype: int + @ivar rdclass: The query class + @type rdclass: int + @ivar response: The response message + @type response: dns.message.Message object + @ivar rrset: The answer + @type rrset: dns.rrset.RRset object + @ivar expiration: The time when the answer expires + @type expiration: float (seconds since the epoch) + @ivar canonical_name: The canonical name of the query name + @type canonical_name: dns.name.Name object + """ + def __init__(self, qname, rdtype, rdclass, response, + raise_on_no_answer=True): + self.qname = qname + self.rdtype = rdtype + self.rdclass = rdclass + self.response = response + min_ttl = -1 + rrset = None + for count in xrange(0, 15): + try: + rrset = response.find_rrset(response.answer, qname, + rdclass, rdtype) + if min_ttl == -1 or rrset.ttl < min_ttl: + min_ttl = rrset.ttl + break + except KeyError: + if rdtype != dns.rdatatype.CNAME: + try: + crrset = response.find_rrset(response.answer, + qname, + rdclass, + dns.rdatatype.CNAME) + if min_ttl == -1 or crrset.ttl < min_ttl: + min_ttl = crrset.ttl + for rd in crrset: + qname = rd.target + break + continue + except KeyError: + if raise_on_no_answer: + raise NoAnswer + if raise_on_no_answer: + raise NoAnswer + if rrset is None and raise_on_no_answer: + raise NoAnswer + self.canonical_name = qname + self.rrset = rrset + if rrset is None: + while 1: + # Look for a SOA RR whose owner name is a superdomain + # of qname. + try: + srrset = response.find_rrset(response.authority, qname, + rdclass, dns.rdatatype.SOA) + if min_ttl == -1 or srrset.ttl < min_ttl: + min_ttl = srrset.ttl + if srrset[0].minimum < min_ttl: + min_ttl = srrset[0].minimum + break + except KeyError: + try: + qname = qname.parent() + except dns.name.NoParent: + break + self.expiration = time.time() + min_ttl + + def __getattr__(self, attr): + if attr == 'name': + return self.rrset.name + elif attr == 'ttl': + return self.rrset.ttl + elif attr == 'covers': + return self.rrset.covers + elif attr == 'rdclass': + return self.rrset.rdclass + elif attr == 'rdtype': + return self.rrset.rdtype + else: + raise AttributeError(attr) + + def __len__(self): + return len(self.rrset) + + def __iter__(self): + return iter(self.rrset) + + def __getitem__(self, i): + return self.rrset[i] + + def __delitem__(self, i): + del self.rrset[i] + + def __getslice__(self, i, j): + return self.rrset[i:j] + + def __delslice__(self, i, j): + del self.rrset[i:j] + +class Cache(object): + """Simple DNS answer cache. + + @ivar data: A dictionary of cached data + @type data: dict + @ivar cleaning_interval: The number of seconds between cleanings. The + default is 300 (5 minutes). + @type cleaning_interval: float + @ivar next_cleaning: The time the cache should next be cleaned (in seconds + since the epoch.) + @type next_cleaning: float + """ + + def __init__(self, cleaning_interval=300.0): + """Initialize a DNS cache. + + @param cleaning_interval: the number of seconds between periodic + cleanings. The default is 300.0 + @type cleaning_interval: float. + """ + + self.data = {} + self.cleaning_interval = cleaning_interval + self.next_cleaning = time.time() + self.cleaning_interval + + def maybe_clean(self): + """Clean the cache if it's time to do so.""" + + now = time.time() + if self.next_cleaning <= now: + keys_to_delete = [] + for (k, v) in self.data.iteritems(): + if v.expiration <= now: + keys_to_delete.append(k) + for k in keys_to_delete: + del self.data[k] + now = time.time() + self.next_cleaning = now + self.cleaning_interval + + def get(self, key): + """Get the answer associated with I{key}. Returns None if + no answer is cached for the key. + @param key: the key + @type key: (dns.name.Name, int, int) tuple whose values are the + query name, rdtype, and rdclass. + @rtype: dns.resolver.Answer object or None + """ + + self.maybe_clean() + v = self.data.get(key) + if v is None or v.expiration <= time.time(): + return None + return v + + def put(self, key, value): + """Associate key and value in the cache. + @param key: the key + @type key: (dns.name.Name, int, int) tuple whose values are the + query name, rdtype, and rdclass. + @param value: The answer being cached + @type value: dns.resolver.Answer object + """ + + self.maybe_clean() + self.data[key] = value + + def flush(self, key=None): + """Flush the cache. + + If I{key} is specified, only that item is flushed. Otherwise + the entire cache is flushed. + + @param key: the key to flush + @type key: (dns.name.Name, int, int) tuple or None + """ + + if not key is None: + if self.data.has_key(key): + del self.data[key] + else: + self.data = {} + self.next_cleaning = time.time() + self.cleaning_interval + +class LRUCacheNode(object): + """LRUCache node. + """ + def __init__(self, key, value): + self.key = key + self.value = value + self.prev = self + self.next = self + + def link_before(self, node): + self.prev = node.prev + self.next = node + node.prev.next = self + node.prev = self + + def link_after(self, node): + self.prev = node + self.next = node.next + node.next.prev = self + node.next = self + + def unlink(self): + self.next.prev = self.prev + self.prev.next = self.next + +class LRUCache(object): + """Bounded least-recently-used DNS answer cache. + + This cache is better than the simple cache (above) if you're + running a web crawler or other process that does a lot of + resolutions. The LRUCache has a maximum number of nodes, and when + it is full, the least-recently used node is removed to make space + for a new one. + + @ivar data: A dictionary of cached data + @type data: dict + @ivar sentinel: sentinel node for circular doubly linked list of nodes + @type sentinel: LRUCacheNode object + @ivar max_size: The maximum number of nodes + @type max_size: int + """ + + def __init__(self, max_size=100000): + """Initialize a DNS cache. + + @param max_size: The maximum number of nodes to cache; the default is + 100000. Must be > 1. + @type max_size: int + """ + self.data = {} + self.set_max_size(max_size) + self.sentinel = LRUCacheNode(None, None) + + def set_max_size(self, max_size): + if max_size < 1: + max_size = 1 + self.max_size = max_size + + def get(self, key): + """Get the answer associated with I{key}. Returns None if + no answer is cached for the key. + @param key: the key + @type key: (dns.name.Name, int, int) tuple whose values are the + query name, rdtype, and rdclass. + @rtype: dns.resolver.Answer object or None + """ + node = self.data.get(key) + if node is None: + return None + # Unlink because we're either going to move the node to the front + # of the LRU list or we're going to free it. + node.unlink() + if node.value.expiration <= time.time(): + del self.data[node.key] + return None + node.link_after(self.sentinel) + return node.value + + def put(self, key, value): + """Associate key and value in the cache. + @param key: the key + @type key: (dns.name.Name, int, int) tuple whose values are the + query name, rdtype, and rdclass. + @param value: The answer being cached + @type value: dns.resolver.Answer object + """ + node = self.data.get(key) + if not node is None: + node.unlink() + del self.data[node.key] + while len(self.data) >= self.max_size: + node = self.sentinel.prev + node.unlink() + del self.data[node.key] + node = LRUCacheNode(key, value) + node.link_after(self.sentinel) + self.data[key] = node + + def flush(self, key=None): + """Flush the cache. + + If I{key} is specified, only that item is flushed. Otherwise + the entire cache is flushed. + + @param key: the key to flush + @type key: (dns.name.Name, int, int) tuple or None + """ + if not key is None: + node = self.data.get(key) + if not node is None: + node.unlink() + del self.data[node.key] + else: + node = self.sentinel.next + while node != self.sentinel: + next = node.next + node.prev = None + node.next = None + node = next + self.data = {} + +class Resolver(object): + """DNS stub resolver + + @ivar domain: The domain of this host + @type domain: dns.name.Name object + @ivar nameservers: A list of nameservers to query. Each nameserver is + a string which contains the IP address of a nameserver. + @type nameservers: list of strings + @ivar search: The search list. If the query name is a relative name, + the resolver will construct an absolute query name by appending the search + names one by one to the query name. + @type search: list of dns.name.Name objects + @ivar port: The port to which to send queries. The default is 53. + @type port: int + @ivar timeout: The number of seconds to wait for a response from a + server, before timing out. + @type timeout: float + @ivar lifetime: The total number of seconds to spend trying to get an + answer to the question. If the lifetime expires, a Timeout exception + will occur. + @type lifetime: float + @ivar keyring: The TSIG keyring to use. The default is None. + @type keyring: dict + @ivar keyname: The TSIG keyname to use. The default is None. + @type keyname: dns.name.Name object + @ivar keyalgorithm: The TSIG key algorithm to use. The default is + dns.tsig.default_algorithm. + @type keyalgorithm: string + @ivar edns: The EDNS level to use. The default is -1, no Edns. + @type edns: int + @ivar ednsflags: The EDNS flags + @type ednsflags: int + @ivar payload: The EDNS payload size. The default is 0. + @type payload: int + @ivar cache: The cache to use. The default is None. + @type cache: dns.resolver.Cache object + """ + def __init__(self, filename='/etc/resolv.conf', configure=True): + """Initialize a resolver instance. + + @param filename: The filename of a configuration file in + standard /etc/resolv.conf format. This parameter is meaningful + only when I{configure} is true and the platform is POSIX. + @type filename: string or file object + @param configure: If True (the default), the resolver instance + is configured in the normal fashion for the operating system + the resolver is running on. (I.e. a /etc/resolv.conf file on + POSIX systems and from the registry on Windows systems.) + @type configure: bool""" + + self.reset() + if configure: + if sys.platform == 'win32': + self.read_registry() + elif filename: + self.read_resolv_conf(filename) + + def reset(self): + """Reset all resolver configuration to the defaults.""" + self.domain = \ + dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) + if len(self.domain) == 0: + self.domain = dns.name.root + self.nameservers = [] + self.search = [] + self.port = 53 + self.timeout = 2.0 + self.lifetime = 30.0 + self.keyring = None + self.keyname = None + self.keyalgorithm = dns.tsig.default_algorithm + self.edns = -1 + self.ednsflags = 0 + self.payload = 0 + self.cache = None + + def read_resolv_conf(self, f): + """Process f as a file in the /etc/resolv.conf format. If f is + a string, it is used as the name of the file to open; otherwise it + is treated as the file itself.""" + if isinstance(f, str) or isinstance(f, unicode): + try: + f = open(f, 'r') + except IOError: + # /etc/resolv.conf doesn't exist, can't be read, etc. + # We'll just use the default resolver configuration. + self.nameservers = ['127.0.0.1'] + return + want_close = True + else: + want_close = False + try: + for l in f: + if len(l) == 0 or l[0] == '#' or l[0] == ';': + continue + tokens = l.split() + if len(tokens) == 0: + continue + if tokens[0] == 'nameserver': + self.nameservers.append(tokens[1]) + elif tokens[0] == 'domain': + self.domain = dns.name.from_text(tokens[1]) + elif tokens[0] == 'search': + for suffix in tokens[1:]: + self.search.append(dns.name.from_text(suffix)) + finally: + if want_close: + f.close() + if len(self.nameservers) == 0: + self.nameservers.append('127.0.0.1') + + def _determine_split_char(self, entry): + # + # The windows registry irritatingly changes the list element + # delimiter in between ' ' and ',' (and vice-versa) in various + # versions of windows. + # + if entry.find(' ') >= 0: + split_char = ' ' + elif entry.find(',') >= 0: + split_char = ',' + else: + # probably a singleton; treat as a space-separated list. + split_char = ' ' + return split_char + + def _config_win32_nameservers(self, nameservers): + """Configure a NameServer registry entry.""" + # we call str() on nameservers to convert it from unicode to ascii + nameservers = str(nameservers) + split_char = self._determine_split_char(nameservers) + ns_list = nameservers.split(split_char) + for ns in ns_list: + if not ns in self.nameservers: + self.nameservers.append(ns) + + def _config_win32_domain(self, domain): + """Configure a Domain registry entry.""" + # we call str() on domain to convert it from unicode to ascii + self.domain = dns.name.from_text(str(domain)) + + def _config_win32_search(self, search): + """Configure a Search registry entry.""" + # we call str() on search to convert it from unicode to ascii + search = str(search) + split_char = self._determine_split_char(search) + search_list = search.split(split_char) + for s in search_list: + if not s in self.search: + self.search.append(dns.name.from_text(s)) + + def _config_win32_fromkey(self, key): + """Extract DNS info from a registry key.""" + try: + servers, rtype = _winreg.QueryValueEx(key, 'NameServer') + except WindowsError: + servers = None + if servers: + self._config_win32_nameservers(servers) + try: + dom, rtype = _winreg.QueryValueEx(key, 'Domain') + if dom: + self._config_win32_domain(dom) + except WindowsError: + pass + else: + try: + servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer') + except WindowsError: + servers = None + if servers: + self._config_win32_nameservers(servers) + try: + dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') + if dom: + self._config_win32_domain(dom) + except WindowsError: + pass + try: + search, rtype = _winreg.QueryValueEx(key, 'SearchList') + except WindowsError: + search = None + if search: + self._config_win32_search(search) + + def read_registry(self): + """Extract resolver configuration from the Windows registry.""" + lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + want_scan = False + try: + try: + # XP, 2000 + tcp_params = _winreg.OpenKey(lm, + r'SYSTEM\CurrentControlSet' + r'\Services\Tcpip\Parameters') + want_scan = True + except EnvironmentError: + # ME + tcp_params = _winreg.OpenKey(lm, + r'SYSTEM\CurrentControlSet' + r'\Services\VxD\MSTCP') + try: + self._config_win32_fromkey(tcp_params) + finally: + tcp_params.Close() + if want_scan: + interfaces = _winreg.OpenKey(lm, + r'SYSTEM\CurrentControlSet' + r'\Services\Tcpip\Parameters' + r'\Interfaces') + try: + i = 0 + while True: + try: + guid = _winreg.EnumKey(interfaces, i) + i += 1 + key = _winreg.OpenKey(interfaces, guid) + if not self._win32_is_nic_enabled(lm, guid, key): + continue + try: + self._config_win32_fromkey(key) + finally: + key.Close() + except EnvironmentError: + break + finally: + interfaces.Close() + finally: + lm.Close() + + def _win32_is_nic_enabled(self, lm, guid, interface_key): + # Look in the Windows Registry to determine whether the network + # interface corresponding to the given guid is enabled. + # + # (Code contributed by Paul Marks, thanks!) + # + try: + # This hard-coded location seems to be consistent, at least + # from Windows 2000 through Vista. + connection_key = _winreg.OpenKey( + lm, + r'SYSTEM\CurrentControlSet\Control\Network' + r'\{4D36E972-E325-11CE-BFC1-08002BE10318}' + r'\%s\Connection' % guid) + + try: + # The PnpInstanceID points to a key inside Enum + (pnp_id, ttype) = _winreg.QueryValueEx( + connection_key, 'PnpInstanceID') + + if ttype != _winreg.REG_SZ: + raise ValueError + + device_key = _winreg.OpenKey( + lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id) + + try: + # Get ConfigFlags for this device + (flags, ttype) = _winreg.QueryValueEx( + device_key, 'ConfigFlags') + + if ttype != _winreg.REG_DWORD: + raise ValueError + + # Based on experimentation, bit 0x1 indicates that the + # device is disabled. + return not (flags & 0x1) + + finally: + device_key.Close() + finally: + connection_key.Close() + except (EnvironmentError, ValueError): + # Pre-vista, enabled interfaces seem to have a non-empty + # NTEContextList; this was how dnspython detected enabled + # nics before the code above was contributed. We've retained + # the old method since we don't know if the code above works + # on Windows 95/98/ME. + try: + (nte, ttype) = _winreg.QueryValueEx(interface_key, + 'NTEContextList') + return nte is not None + except WindowsError: + return False + + def _compute_timeout(self, start): + now = time.time() + if now < start: + if start - now > 1: + # Time going backwards is bad. Just give up. + raise Timeout + else: + # Time went backwards, but only a little. This can + # happen, e.g. under vmware with older linux kernels. + # Pretend it didn't happen. + now = start + duration = now - start + if duration >= self.lifetime: + raise Timeout + return min(self.lifetime - duration, self.timeout) + + def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, + tcp=False, source=None, raise_on_no_answer=True): + """Query nameservers to find the answer to the question. + + The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects + of the appropriate type, or strings that can be converted into objects + of the appropriate type. E.g. For I{rdtype} the integer 2 and the + the string 'NS' both mean to query for records with DNS rdata type NS. + + @param qname: the query name + @type qname: dns.name.Name object or string + @param rdtype: the query type + @type rdtype: int or string + @param rdclass: the query class + @type rdclass: int or string + @param tcp: use TCP to make the query (default is False). + @type tcp: bool + @param source: bind to this IP address (defaults to machine default IP). + @type source: IP address in dotted quad notation + @param raise_on_no_answer: raise NoAnswer if there's no answer + (defaults is True). + @type raise_on_no_answer: bool + @rtype: dns.resolver.Answer instance + @raises Timeout: no answers could be found in the specified lifetime + @raises NXDOMAIN: the query name does not exist + @raises NoAnswer: the response did not contain an answer and + raise_on_no_answer is True. + @raises NoNameservers: no non-broken nameservers are available to + answer the question.""" + + if isinstance(qname, (str, unicode)): + qname = dns.name.from_text(qname, None) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if dns.rdatatype.is_metatype(rdtype): + raise NoMetaqueries + if isinstance(rdclass, (str, unicode)): + rdclass = dns.rdataclass.from_text(rdclass) + if dns.rdataclass.is_metaclass(rdclass): + raise NoMetaqueries + qnames_to_try = [] + if qname.is_absolute(): + qnames_to_try.append(qname) + else: + if len(qname) > 1: + qnames_to_try.append(qname.concatenate(dns.name.root)) + if self.search: + for suffix in self.search: + qnames_to_try.append(qname.concatenate(suffix)) + else: + qnames_to_try.append(qname.concatenate(self.domain)) + all_nxdomain = True + start = time.time() + for qname in qnames_to_try: + if self.cache: + answer = self.cache.get((qname, rdtype, rdclass)) + if not answer is None: + if answer.rrset is None and raise_on_no_answer: + raise NoAnswer + else: + return answer + request = dns.message.make_query(qname, rdtype, rdclass) + if not self.keyname is None: + request.use_tsig(self.keyring, self.keyname, + algorithm=self.keyalgorithm) + request.use_edns(self.edns, self.ednsflags, self.payload) + response = None + # + # make a copy of the servers list so we can alter it later. + # + nameservers = self.nameservers[:] + backoff = 0.10 + while response is None: + if len(nameservers) == 0: + raise NoNameservers + for nameserver in nameservers[:]: + timeout = self._compute_timeout(start) + try: + if tcp: + response = dns.query.tcp(request, nameserver, + timeout, self.port, + source=source) + else: + response = dns.query.udp(request, nameserver, + timeout, self.port, + source=source) + except (socket.error, dns.exception.Timeout): + # + # Communication failure or timeout. Go to the + # next server + # + response = None + continue + except dns.query.UnexpectedSource: + # + # Who knows? Keep going. + # + response = None + continue + except dns.exception.FormError: + # + # We don't understand what this server is + # saying. Take it out of the mix and + # continue. + # + nameservers.remove(nameserver) + response = None + continue + rcode = response.rcode() + if rcode == dns.rcode.NOERROR or \ + rcode == dns.rcode.NXDOMAIN: + break + # + # We got a response, but we're not happy with the + # rcode in it. Remove the server from the mix if + # the rcode isn't SERVFAIL. + # + if rcode != dns.rcode.SERVFAIL: + nameservers.remove(nameserver) + response = None + if not response is None: + break + # + # All nameservers failed! + # + if len(nameservers) > 0: + # + # But we still have servers to try. Sleep a bit + # so we don't pound them! + # + timeout = self._compute_timeout(start) + sleep_time = min(timeout, backoff) + backoff *= 2 + time.sleep(sleep_time) + if response.rcode() == dns.rcode.NXDOMAIN: + continue + all_nxdomain = False + break + if all_nxdomain: + raise NXDOMAIN + answer = Answer(qname, rdtype, rdclass, response, + raise_on_no_answer) + if self.cache: + self.cache.put((qname, rdtype, rdclass), answer) + return answer + + def use_tsig(self, keyring, keyname=None, + algorithm=dns.tsig.default_algorithm): + """Add a TSIG signature to the query. + + @param keyring: The TSIG keyring to use; defaults to None. + @type keyring: dict + @param keyname: The name of the TSIG key to use; defaults to None. + The key must be defined in the keyring. If a keyring is specified + but a keyname is not, then the key used will be the first key in the + keyring. Note that the order of keys in a dictionary is not defined, + so applications should supply a keyname when a keyring is used, unless + they know the keyring contains only one key. + @param algorithm: The TSIG key algorithm to use. The default + is dns.tsig.default_algorithm. + @type algorithm: string""" + self.keyring = keyring + if keyname is None: + self.keyname = self.keyring.keys()[0] + else: + self.keyname = keyname + self.keyalgorithm = algorithm + + def use_edns(self, edns, ednsflags, payload): + """Configure Edns. + + @param edns: The EDNS level to use. The default is -1, no Edns. + @type edns: int + @param ednsflags: The EDNS flags + @type ednsflags: int + @param payload: The EDNS payload size. The default is 0. + @type payload: int""" + + if edns is None: + edns = -1 + self.edns = edns + self.ednsflags = ednsflags + self.payload = payload + +default_resolver = None + +def get_default_resolver(): + """Get the default resolver, initializing it if necessary.""" + global default_resolver + if default_resolver is None: + default_resolver = Resolver() + return default_resolver + +def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, + tcp=False, source=None, raise_on_no_answer=True): + """Query nameservers to find the answer to the question. + + This is a convenience function that uses the default resolver + object to make the query. + @see: L{dns.resolver.Resolver.query} for more information on the + parameters.""" + return get_default_resolver().query(qname, rdtype, rdclass, tcp, source, + raise_on_no_answer) + +def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None): + """Find the name of the zone which contains the specified name. + + @param name: the query name + @type name: absolute dns.name.Name object or string + @param rdclass: The query class + @type rdclass: int + @param tcp: use TCP to make the query (default is False). + @type tcp: bool + @param resolver: the resolver to use + @type resolver: dns.resolver.Resolver object or None + @rtype: dns.name.Name""" + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, dns.name.root) + if resolver is None: + resolver = get_default_resolver() + if not name.is_absolute(): + raise NotAbsolute(name) + while 1: + try: + answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp) + if answer.rrset.name == name: + return name + # otherwise we were CNAMEd or DNAMEd and need to look higher + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + pass + try: + name = name.parent() + except dns.name.NoParent: + raise NoRootSOA + +# +# Support for overriding the system resolver for all python code in the +# running process. +# + +_protocols_for_socktype = { + socket.SOCK_DGRAM : [socket.SOL_UDP], + socket.SOCK_STREAM : [socket.SOL_TCP], + } + +_resolver = None +_original_getaddrinfo = socket.getaddrinfo +_original_getnameinfo = socket.getnameinfo +_original_getfqdn = socket.getfqdn +_original_gethostbyname = socket.gethostbyname +_original_gethostbyname_ex = socket.gethostbyname_ex +_original_gethostbyaddr = socket.gethostbyaddr + +def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0, + proto=0, flags=0): + if flags & (socket.AI_ADDRCONFIG|socket.AI_V4MAPPED) != 0: + raise NotImplementedError + if host is None and service is None: + raise socket.gaierror(socket.EAI_NONAME) + v6addrs = [] + v4addrs = [] + canonical_name = None + try: + # Is host None or a V6 address literal? + if host is None: + canonical_name = 'localhost' + if flags & socket.AI_PASSIVE != 0: + v6addrs.append('::') + v4addrs.append('0.0.0.0') + else: + v6addrs.append('::1') + v4addrs.append('127.0.0.1') + else: + parts = host.split('%') + if len(parts) == 2: + ahost = parts[0] + else: + ahost = host + addr = dns.ipv6.inet_aton(ahost) + v6addrs.append(host) + canonical_name = host + except: + try: + # Is it a V4 address literal? + addr = dns.ipv4.inet_aton(host) + v4addrs.append(host) + canonical_name = host + except: + if flags & socket.AI_NUMERICHOST == 0: + try: + qname = None + if family == socket.AF_INET6 or family == socket.AF_UNSPEC: + v6 = _resolver.query(host, dns.rdatatype.AAAA, + raise_on_no_answer=False) + # Note that setting host ensures we query the same name + # for A as we did for AAAA. + host = v6.qname + canonical_name = v6.canonical_name.to_text(True) + if v6.rrset is not None: + for rdata in v6.rrset: + v6addrs.append(rdata.address) + if family == socket.AF_INET or family == socket.AF_UNSPEC: + v4 = _resolver.query(host, dns.rdatatype.A, + raise_on_no_answer=False) + host = v4.qname + canonical_name = v4.canonical_name.to_text(True) + if v4.rrset is not None: + for rdata in v4.rrset: + v4addrs.append(rdata.address) + except dns.resolver.NXDOMAIN: + raise socket.gaierror(socket.EAI_NONAME) + except: + raise socket.gaierror(socket.EAI_SYSTEM) + port = None + try: + # Is it a port literal? + if service is None: + port = 0 + else: + port = int(service) + except: + if flags & socket.AI_NUMERICSERV == 0: + try: + port = socket.getservbyname(service) + except: + pass + if port is None: + raise socket.gaierror(socket.EAI_NONAME) + tuples = [] + if socktype == 0: + socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] + else: + socktypes = [socktype] + if flags & socket.AI_CANONNAME != 0: + cname = canonical_name + else: + cname = '' + if family == socket.AF_INET6 or family == socket.AF_UNSPEC: + for addr in v6addrs: + for socktype in socktypes: + for proto in _protocols_for_socktype[socktype]: + tuples.append((socket.AF_INET6, socktype, proto, + cname, (addr, port, 0, 0))) + if family == socket.AF_INET or family == socket.AF_UNSPEC: + for addr in v4addrs: + for socktype in socktypes: + for proto in _protocols_for_socktype[socktype]: + tuples.append((socket.AF_INET, socktype, proto, + cname, (addr, port))) + if len(tuples) == 0: + raise socket.gaierror(socket.EAI_NONAME) + return tuples + +def _getnameinfo(sockaddr, flags=0): + host = sockaddr[0] + port = sockaddr[1] + if len(sockaddr) == 4: + scope = sockaddr[3] + family = socket.AF_INET6 + else: + scope = None + family = socket.AF_INET + tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, + socket.SOL_TCP, 0) + if len(tuples) > 1: + raise socket.error('sockaddr resolved to multiple addresses') + addr = tuples[0][4][0] + if flags & socket.NI_DGRAM: + pname = 'udp' + else: + pname = 'tcp' + qname = dns.reversename.from_address(addr) + if flags & socket.NI_NUMERICHOST == 0: + try: + answer = _resolver.query(qname, 'PTR') + hostname = answer.rrset[0].target.to_text(True) + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + if flags & socket.NI_NAMEREQD: + raise socket.gaierror(socket.EAI_NONAME) + hostname = addr + if scope is not None: + hostname += '%' + str(scope) + else: + hostname = addr + if scope is not None: + hostname += '%' + str(scope) + if flags & socket.NI_NUMERICSERV: + service = str(port) + else: + service = socket.getservbyport(port, pname) + return (hostname, service) + +def _getfqdn(name=None): + if name is None: + name = socket.gethostname() + return _getnameinfo(_getaddrinfo(name, 80)[0][4])[0] + +def _gethostbyname(name): + return _gethostbyname_ex(name)[2][0] + +def _gethostbyname_ex(name): + aliases = [] + addresses = [] + tuples = _getaddrinfo(name, 0, socket.AF_INET, socket.SOCK_STREAM, + socket.SOL_TCP, socket.AI_CANONNAME) + canonical = tuples[0][3] + for item in tuples: + addresses.append(item[4][0]) + # XXX we just ignore aliases + return (canonical, aliases, addresses) + +def _gethostbyaddr(ip): + try: + addr = dns.ipv6.inet_aton(ip) + sockaddr = (ip, 80, 0, 0) + family = socket.AF_INET6 + except: + sockaddr = (ip, 80) + family = socket.AF_INET + (name, port) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) + aliases = [] + addresses = [] + tuples = _getaddrinfo(name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, + socket.AI_CANONNAME) + canonical = tuples[0][3] + for item in tuples: + addresses.append(item[4][0]) + # XXX we just ignore aliases + return (canonical, aliases, addresses) + +def override_system_resolver(resolver=None): + """Override the system resolver routines in the socket module with + versions which use dnspython's resolver. + + This can be useful in testing situations where you want to control + the resolution behavior of python code without having to change + the system's resolver settings (e.g. /etc/resolv.conf). + + The resolver to use may be specified; if it's not, the default + resolver will be used. + + @param resolver: the resolver to use + @type resolver: dns.resolver.Resolver object or None + """ + if resolver is None: + resolver = get_default_resolver() + global _resolver + _resolver = resolver + socket.getaddrinfo = _getaddrinfo + socket.getnameinfo = _getnameinfo + socket.getfqdn = _getfqdn + socket.gethostbyname = _gethostbyname + socket.gethostbyname_ex = _gethostbyname_ex + socket.gethostbyaddr = _gethostbyaddr + +def restore_system_resolver(): + """Undo the effects of override_system_resolver(). + """ + global _resolver + _resolver = None + socket.getaddrinfo = _original_getaddrinfo + socket.getnameinfo = _original_getnameinfo + socket.getfqdn = _original_getfqdn + socket.gethostbyname = _original_gethostbyname + socket.gethostbyname_ex = _original_gethostbyname_ex + socket.gethostbyaddr = _original_gethostbyaddr diff --git a/third_party/dnspython/dns/reversename.py b/third_party/dnspython/dns/reversename.py new file mode 100644 index 0000000000..4925cfd6ff --- /dev/null +++ b/third_party/dnspython/dns/reversename.py @@ -0,0 +1,75 @@ +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Reverse Map Names. + +@var ipv4_reverse_domain: The DNS IPv4 reverse-map domain, in-addr.arpa. +@type ipv4_reverse_domain: dns.name.Name object +@var ipv6_reverse_domain: The DNS IPv6 reverse-map domain, ip6.arpa. +@type ipv6_reverse_domain: dns.name.Name object +""" + +import dns.name +import dns.ipv6 +import dns.ipv4 + +ipv4_reverse_domain = dns.name.from_text('in-addr.arpa.') +ipv6_reverse_domain = dns.name.from_text('ip6.arpa.') + +def from_address(text): + """Convert an IPv4 or IPv6 address in textual form into a Name object whose + value is the reverse-map domain name of the address. + @param text: an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1', + '::1') + @type text: str + @rtype: dns.name.Name object + """ + try: + parts = list(dns.ipv6.inet_aton(text).encode('hex_codec')) + origin = ipv6_reverse_domain + except: + parts = ['%d' % ord(byte) for byte in dns.ipv4.inet_aton(text)] + origin = ipv4_reverse_domain + parts.reverse() + return dns.name.from_text('.'.join(parts), origin=origin) + +def to_address(name): + """Convert a reverse map domain name into textual address form. + @param name: an IPv4 or IPv6 address in reverse-map form. + @type name: dns.name.Name object + @rtype: str + """ + if name.is_subdomain(ipv4_reverse_domain): + name = name.relativize(ipv4_reverse_domain) + labels = list(name.labels) + labels.reverse() + text = '.'.join(labels) + # run through inet_aton() to check syntax and make pretty. + return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) + elif name.is_subdomain(ipv6_reverse_domain): + name = name.relativize(ipv6_reverse_domain) + labels = list(name.labels) + labels.reverse() + parts = [] + i = 0 + l = len(labels) + while i < l: + parts.append(''.join(labels[i:i+4])) + i += 4 + text = ':'.join(parts) + # run through inet_aton() to check syntax and make pretty. + return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) + else: + raise dns.exception.SyntaxError('unknown reverse-map address family') diff --git a/third_party/dnspython/dns/rrset.py b/third_party/dnspython/dns/rrset.py new file mode 100644 index 0000000000..f6051fea4f --- /dev/null +++ b/third_party/dnspython/dns/rrset.py @@ -0,0 +1,175 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS RRsets (an RRset is a named rdataset)""" + +import dns.name +import dns.rdataset +import dns.rdataclass +import dns.renderer + +class RRset(dns.rdataset.Rdataset): + """A DNS RRset (named rdataset). + + RRset inherits from Rdataset, and RRsets can be treated as + Rdatasets in most cases. There are, however, a few notable + exceptions. RRsets have different to_wire() and to_text() method + arguments, reflecting the fact that RRsets always have an owner + name. + """ + + __slots__ = ['name', 'deleting'] + + def __init__(self, name, rdclass, rdtype, covers=dns.rdatatype.NONE, + deleting=None): + """Create a new RRset.""" + + super(RRset, self).__init__(rdclass, rdtype, covers) + self.name = name + self.deleting = deleting + + def _clone(self): + obj = super(RRset, self)._clone() + obj.name = self.name + obj.deleting = self.deleting + return obj + + def __repr__(self): + if self.covers == 0: + ctext = '' + else: + ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' + if not self.deleting is None: + dtext = ' delete=' + dns.rdataclass.to_text(self.deleting) + else: + dtext = '' + return '' + + def __str__(self): + return self.to_text() + + def __eq__(self, other): + """Two RRsets are equal if they have the same name and the same + rdataset + + @rtype: bool""" + if not isinstance(other, RRset): + return False + if self.name != other.name: + return False + return super(RRset, self).__eq__(other) + + def match(self, name, rdclass, rdtype, covers, deleting=None): + """Returns True if this rrset matches the specified class, type, + covers, and deletion state.""" + + if not super(RRset, self).match(rdclass, rdtype, covers): + return False + if self.name != name or self.deleting != deleting: + return False + return True + + def to_text(self, origin=None, relativize=True, **kw): + """Convert the RRset into DNS master file format. + + @see: L{dns.name.Name.choose_relativity} for more information + on how I{origin} and I{relativize} determine the way names + are emitted. + + Any additional keyword arguments are passed on to the rdata + to_text() method. + + @param origin: The origin for relative names, or None. + @type origin: dns.name.Name object + @param relativize: True if names should names be relativized + @type relativize: bool""" + + return super(RRset, self).to_text(self.name, origin, relativize, + self.deleting, **kw) + + def to_wire(self, file, compress=None, origin=None, **kw): + """Convert the RRset to wire format.""" + + return super(RRset, self).to_wire(self.name, file, compress, origin, + self.deleting, **kw) + + def to_rdataset(self): + """Convert an RRset into an Rdataset. + + @rtype: dns.rdataset.Rdataset object + """ + return dns.rdataset.from_rdata_list(self.ttl, list(self)) + + +def from_text_list(name, ttl, rdclass, rdtype, text_rdatas): + """Create an RRset with the specified name, TTL, class, and type, and with + the specified list of rdatas in text format. + + @rtype: dns.rrset.RRset object + """ + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + if isinstance(rdclass, (str, unicode)): + rdclass = dns.rdataclass.from_text(rdclass) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + r = RRset(name, rdclass, rdtype) + r.update_ttl(ttl) + for t in text_rdatas: + rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) + r.add(rd) + return r + +def from_text(name, ttl, rdclass, rdtype, *text_rdatas): + """Create an RRset with the specified name, TTL, class, and type and with + the specified rdatas in text format. + + @rtype: dns.rrset.RRset object + """ + + return from_text_list(name, ttl, rdclass, rdtype, text_rdatas) + +def from_rdata_list(name, ttl, rdatas): + """Create an RRset with the specified name and TTL, and with + the specified list of rdata objects. + + @rtype: dns.rrset.RRset object + """ + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + + if len(rdatas) == 0: + raise ValueError("rdata list must not be empty") + r = None + for rd in rdatas: + if r is None: + r = RRset(name, rd.rdclass, rd.rdtype) + r.update_ttl(ttl) + first_time = False + r.add(rd) + return r + +def from_rdata(name, ttl, *rdatas): + """Create an RRset with the specified name and TTL, and with + the specified rdata objects. + + @rtype: dns.rrset.RRset object + """ + + return from_rdata_list(name, ttl, rdatas) diff --git a/third_party/dnspython/dns/set.py b/third_party/dnspython/dns/set.py new file mode 100644 index 0000000000..14c76a05c4 --- /dev/null +++ b/third_party/dnspython/dns/set.py @@ -0,0 +1,263 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""A simple Set class.""" + +class Set(object): + """A simple set class. + + Sets are not in Python until 2.3, and rdata are not immutable so + we cannot use sets.Set anyway. This class implements subset of + the 2.3 Set interface using a list as the container. + + @ivar items: A list of the items which are in the set + @type items: list""" + + __slots__ = ['items'] + + def __init__(self, items=None): + """Initialize the set. + + @param items: the initial set of items + @type items: any iterable or None + """ + + self.items = [] + if not items is None: + for item in items: + self.add(item) + + def __repr__(self): + return "dns.simpleset.Set(%s)" % repr(self.items) + + def add(self, item): + """Add an item to the set.""" + if not item in self.items: + self.items.append(item) + + def remove(self, item): + """Remove an item from the set.""" + self.items.remove(item) + + def discard(self, item): + """Remove an item from the set if present.""" + try: + self.items.remove(item) + except ValueError: + pass + + def _clone(self): + """Make a (shallow) copy of the set. + + There is a 'clone protocol' that subclasses of this class + should use. To make a copy, first call your super's _clone() + method, and use the object returned as the new instance. Then + make shallow copies of the attributes defined in the subclass. + + This protocol allows us to write the set algorithms that + return new instances (e.g. union) once, and keep using them in + subclasses. + """ + + cls = self.__class__ + obj = cls.__new__(cls) + obj.items = list(self.items) + return obj + + def __copy__(self): + """Make a (shallow) copy of the set.""" + return self._clone() + + def copy(self): + """Make a (shallow) copy of the set.""" + return self._clone() + + def union_update(self, other): + """Update the set, adding any elements from other which are not + already in the set. + @param other: the collection of items with which to update the set + @type other: Set object + """ + if not isinstance(other, Set): + raise ValueError('other must be a Set instance') + if self is other: + return + for item in other.items: + self.add(item) + + def intersection_update(self, other): + """Update the set, removing any elements from other which are not + in both sets. + @param other: the collection of items with which to update the set + @type other: Set object + """ + if not isinstance(other, Set): + raise ValueError('other must be a Set instance') + if self is other: + return + # we make a copy of the list so that we can remove items from + # the list without breaking the iterator. + for item in list(self.items): + if item not in other.items: + self.items.remove(item) + + def difference_update(self, other): + """Update the set, removing any elements from other which are in + the set. + @param other: the collection of items with which to update the set + @type other: Set object + """ + if not isinstance(other, Set): + raise ValueError('other must be a Set instance') + if self is other: + self.items = [] + else: + for item in other.items: + self.discard(item) + + def union(self, other): + """Return a new set which is the union of I{self} and I{other}. + + @param other: the other set + @type other: Set object + @rtype: the same type as I{self} + """ + + obj = self._clone() + obj.union_update(other) + return obj + + def intersection(self, other): + """Return a new set which is the intersection of I{self} and I{other}. + + @param other: the other set + @type other: Set object + @rtype: the same type as I{self} + """ + + obj = self._clone() + obj.intersection_update(other) + return obj + + def difference(self, other): + """Return a new set which I{self} - I{other}, i.e. the items + in I{self} which are not also in I{other}. + + @param other: the other set + @type other: Set object + @rtype: the same type as I{self} + """ + + obj = self._clone() + obj.difference_update(other) + return obj + + def __or__(self, other): + return self.union(other) + + def __and__(self, other): + return self.intersection(other) + + def __add__(self, other): + return self.union(other) + + def __sub__(self, other): + return self.difference(other) + + def __ior__(self, other): + self.union_update(other) + return self + + def __iand__(self, other): + self.intersection_update(other) + return self + + def __iadd__(self, other): + self.union_update(other) + return self + + def __isub__(self, other): + self.difference_update(other) + return self + + def update(self, other): + """Update the set, adding any elements from other which are not + already in the set. + @param other: the collection of items with which to update the set + @type other: any iterable type""" + for item in other: + self.add(item) + + def clear(self): + """Make the set empty.""" + self.items = [] + + def __eq__(self, other): + # Yes, this is inefficient but the sets we're dealing with are + # usually quite small, so it shouldn't hurt too much. + for item in self.items: + if not item in other.items: + return False + for item in other.items: + if not item in self.items: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def __len__(self): + return len(self.items) + + def __iter__(self): + return iter(self.items) + + def __getitem__(self, i): + return self.items[i] + + def __delitem__(self, i): + del self.items[i] + + def __getslice__(self, i, j): + return self.items[i:j] + + def __delslice__(self, i, j): + del self.items[i:j] + + def issubset(self, other): + """Is I{self} a subset of I{other}? + + @rtype: bool + """ + + if not isinstance(other, Set): + raise ValueError('other must be a Set instance') + for item in self.items: + if not item in other.items: + return False + return True + + def issuperset(self, other): + """Is I{self} a superset of I{other}? + + @rtype: bool + """ + + if not isinstance(other, Set): + raise ValueError('other must be a Set instance') + for item in other.items: + if not item in self.items: + return False + return True diff --git a/third_party/dnspython/dns/tokenizer.py b/third_party/dnspython/dns/tokenizer.py new file mode 100644 index 0000000000..4bff7b6c72 --- /dev/null +++ b/third_party/dnspython/dns/tokenizer.py @@ -0,0 +1,547 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Tokenize DNS master file format""" + +import cStringIO +import sys + +import dns.exception +import dns.name +import dns.ttl + +_DELIMITERS = { + ' ' : True, + '\t' : True, + '\n' : True, + ';' : True, + '(' : True, + ')' : True, + '"' : True } + +_QUOTING_DELIMITERS = { '"' : True } + +EOF = 0 +EOL = 1 +WHITESPACE = 2 +IDENTIFIER = 3 +QUOTED_STRING = 4 +COMMENT = 5 +DELIMITER = 6 + +class UngetBufferFull(dns.exception.DNSException): + """Raised when an attempt is made to unget a token when the unget + buffer is full.""" + pass + +class Token(object): + """A DNS master file format token. + + @ivar ttype: The token type + @type ttype: int + @ivar value: The token value + @type value: string + @ivar has_escape: Does the token value contain escapes? + @type has_escape: bool + """ + + def __init__(self, ttype, value='', has_escape=False): + """Initialize a token instance. + + @param ttype: The token type + @type ttype: int + @ivar value: The token value + @type value: string + @ivar has_escape: Does the token value contain escapes? + @type has_escape: bool + """ + self.ttype = ttype + self.value = value + self.has_escape = has_escape + + def is_eof(self): + return self.ttype == EOF + + def is_eol(self): + return self.ttype == EOL + + def is_whitespace(self): + return self.ttype == WHITESPACE + + def is_identifier(self): + return self.ttype == IDENTIFIER + + def is_quoted_string(self): + return self.ttype == QUOTED_STRING + + def is_comment(self): + return self.ttype == COMMENT + + def is_delimiter(self): + return self.ttype == DELIMITER + + def is_eol_or_eof(self): + return (self.ttype == EOL or self.ttype == EOF) + + def __eq__(self, other): + if not isinstance(other, Token): + return False + return (self.ttype == other.ttype and + self.value == other.value) + + def __ne__(self, other): + if not isinstance(other, Token): + return True + return (self.ttype != other.ttype or + self.value != other.value) + + def __str__(self): + return '%d "%s"' % (self.ttype, self.value) + + def unescape(self): + if not self.has_escape: + return self + unescaped = '' + l = len(self.value) + i = 0 + while i < l: + c = self.value[i] + i += 1 + if c == '\\': + if i >= l: + raise dns.exception.UnexpectedEnd + c = self.value[i] + i += 1 + if c.isdigit(): + if i >= l: + raise dns.exception.UnexpectedEnd + c2 = self.value[i] + i += 1 + if i >= l: + raise dns.exception.UnexpectedEnd + c3 = self.value[i] + i += 1 + if not (c2.isdigit() and c3.isdigit()): + raise dns.exception.SyntaxError + c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) + unescaped += c + return Token(self.ttype, unescaped) + + # compatibility for old-style tuple tokens + + def __len__(self): + return 2 + + def __iter__(self): + return iter((self.ttype, self.value)) + + def __getitem__(self, i): + if i == 0: + return self.ttype + elif i == 1: + return self.value + else: + raise IndexError + +class Tokenizer(object): + """A DNS master file format tokenizer. + + A token is a (type, value) tuple, where I{type} is an int, and + I{value} is a string. The valid types are EOF, EOL, WHITESPACE, + IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER. + + @ivar file: The file to tokenize + @type file: file + @ivar ungotten_char: The most recently ungotten character, or None. + @type ungotten_char: string + @ivar ungotten_token: The most recently ungotten token, or None. + @type ungotten_token: (int, string) token tuple + @ivar multiline: The current multiline level. This value is increased + by one every time a '(' delimiter is read, and decreased by one every time + a ')' delimiter is read. + @type multiline: int + @ivar quoting: This variable is true if the tokenizer is currently + reading a quoted string. + @type quoting: bool + @ivar eof: This variable is true if the tokenizer has encountered EOF. + @type eof: bool + @ivar delimiters: The current delimiter dictionary. + @type delimiters: dict + @ivar line_number: The current line number + @type line_number: int + @ivar filename: A filename that will be returned by the L{where} method. + @type filename: string + """ + + def __init__(self, f=sys.stdin, filename=None): + """Initialize a tokenizer instance. + + @param f: The file to tokenize. The default is sys.stdin. + This parameter may also be a string, in which case the tokenizer + will take its input from the contents of the string. + @type f: file or string + @param filename: the name of the filename that the L{where} method + will return. + @type filename: string + """ + + if isinstance(f, str): + f = cStringIO.StringIO(f) + if filename is None: + filename = '' + else: + if filename is None: + if f is sys.stdin: + filename = '' + else: + filename = '' + self.file = f + self.ungotten_char = None + self.ungotten_token = None + self.multiline = 0 + self.quoting = False + self.eof = False + self.delimiters = _DELIMITERS + self.line_number = 1 + self.filename = filename + + def _get_char(self): + """Read a character from input. + @rtype: string + """ + + if self.ungotten_char is None: + if self.eof: + c = '' + else: + c = self.file.read(1) + if c == '': + self.eof = True + elif c == '\n': + self.line_number += 1 + else: + c = self.ungotten_char + self.ungotten_char = None + return c + + def where(self): + """Return the current location in the input. + + @rtype: (string, int) tuple. The first item is the filename of + the input, the second is the current line number. + """ + + return (self.filename, self.line_number) + + def _unget_char(self, c): + """Unget a character. + + The unget buffer for characters is only one character large; it is + an error to try to unget a character when the unget buffer is not + empty. + + @param c: the character to unget + @type c: string + @raises UngetBufferFull: there is already an ungotten char + """ + + if not self.ungotten_char is None: + raise UngetBufferFull + self.ungotten_char = c + + def skip_whitespace(self): + """Consume input until a non-whitespace character is encountered. + + The non-whitespace character is then ungotten, and the number of + whitespace characters consumed is returned. + + If the tokenizer is in multiline mode, then newlines are whitespace. + + @rtype: int + """ + + skipped = 0 + while True: + c = self._get_char() + if c != ' ' and c != '\t': + if (c != '\n') or not self.multiline: + self._unget_char(c) + return skipped + skipped += 1 + + def get(self, want_leading = False, want_comment = False): + """Get the next token. + + @param want_leading: If True, return a WHITESPACE token if the + first character read is whitespace. The default is False. + @type want_leading: bool + @param want_comment: If True, return a COMMENT token if the + first token read is a comment. The default is False. + @type want_comment: bool + @rtype: Token object + @raises dns.exception.UnexpectedEnd: input ended prematurely + @raises dns.exception.SyntaxError: input was badly formed + """ + + if not self.ungotten_token is None: + token = self.ungotten_token + self.ungotten_token = None + if token.is_whitespace(): + if want_leading: + return token + elif token.is_comment(): + if want_comment: + return token + else: + return token + skipped = self.skip_whitespace() + if want_leading and skipped > 0: + return Token(WHITESPACE, ' ') + token = '' + ttype = IDENTIFIER + has_escape = False + while True: + c = self._get_char() + if c == '' or c in self.delimiters: + if c == '' and self.quoting: + raise dns.exception.UnexpectedEnd + if token == '' and ttype != QUOTED_STRING: + if c == '(': + self.multiline += 1 + self.skip_whitespace() + continue + elif c == ')': + if not self.multiline > 0: + raise dns.exception.SyntaxError + self.multiline -= 1 + self.skip_whitespace() + continue + elif c == '"': + if not self.quoting: + self.quoting = True + self.delimiters = _QUOTING_DELIMITERS + ttype = QUOTED_STRING + continue + else: + self.quoting = False + self.delimiters = _DELIMITERS + self.skip_whitespace() + continue + elif c == '\n': + return Token(EOL, '\n') + elif c == ';': + while 1: + c = self._get_char() + if c == '\n' or c == '': + break + token += c + if want_comment: + self._unget_char(c) + return Token(COMMENT, token) + elif c == '': + if self.multiline: + raise dns.exception.SyntaxError('unbalanced parentheses') + return Token(EOF) + elif self.multiline: + self.skip_whitespace() + token = '' + continue + else: + return Token(EOL, '\n') + else: + # This code exists in case we ever want a + # delimiter to be returned. It never produces + # a token currently. + token = c + ttype = DELIMITER + else: + self._unget_char(c) + break + elif self.quoting: + if c == '\\': + c = self._get_char() + if c == '': + raise dns.exception.UnexpectedEnd + if c.isdigit(): + c2 = self._get_char() + if c2 == '': + raise dns.exception.UnexpectedEnd + c3 = self._get_char() + if c == '': + raise dns.exception.UnexpectedEnd + if not (c2.isdigit() and c3.isdigit()): + raise dns.exception.SyntaxError + c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) + elif c == '\n': + raise dns.exception.SyntaxError('newline in quoted string') + elif c == '\\': + # + # It's an escape. Put it and the next character into + # the token; it will be checked later for goodness. + # + token += c + has_escape = True + c = self._get_char() + if c == '' or c == '\n': + raise dns.exception.UnexpectedEnd + token += c + if token == '' and ttype != QUOTED_STRING: + if self.multiline: + raise dns.exception.SyntaxError('unbalanced parentheses') + ttype = EOF + return Token(ttype, token, has_escape) + + def unget(self, token): + """Unget a token. + + The unget buffer for tokens is only one token large; it is + an error to try to unget a token when the unget buffer is not + empty. + + @param token: the token to unget + @type token: Token object + @raises UngetBufferFull: there is already an ungotten token + """ + + if not self.ungotten_token is None: + raise UngetBufferFull + self.ungotten_token = token + + def next(self): + """Return the next item in an iteration. + @rtype: (int, string) + """ + + token = self.get() + if token.is_eof(): + raise StopIteration + return token + + def __iter__(self): + return self + + # Helpers + + def get_int(self): + """Read the next token and interpret it as an integer. + + @raises dns.exception.SyntaxError: + @rtype: int + """ + + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError('expecting an identifier') + if not token.value.isdigit(): + raise dns.exception.SyntaxError('expecting an integer') + return int(token.value) + + def get_uint8(self): + """Read the next token and interpret it as an 8-bit unsigned + integer. + + @raises dns.exception.SyntaxError: + @rtype: int + """ + + value = self.get_int() + if value < 0 or value > 255: + raise dns.exception.SyntaxError('%d is not an unsigned 8-bit integer' % value) + return value + + def get_uint16(self): + """Read the next token and interpret it as a 16-bit unsigned + integer. + + @raises dns.exception.SyntaxError: + @rtype: int + """ + + value = self.get_int() + if value < 0 or value > 65535: + raise dns.exception.SyntaxError('%d is not an unsigned 16-bit integer' % value) + return value + + def get_uint32(self): + """Read the next token and interpret it as a 32-bit unsigned + integer. + + @raises dns.exception.SyntaxError: + @rtype: int + """ + + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError('expecting an identifier') + if not token.value.isdigit(): + raise dns.exception.SyntaxError('expecting an integer') + value = long(token.value) + if value < 0 or value > 4294967296L: + raise dns.exception.SyntaxError('%d is not an unsigned 32-bit integer' % value) + return value + + def get_string(self, origin=None): + """Read the next token and interpret it as a string. + + @raises dns.exception.SyntaxError: + @rtype: string + """ + + token = self.get().unescape() + if not (token.is_identifier() or token.is_quoted_string()): + raise dns.exception.SyntaxError('expecting a string') + return token.value + + def get_identifier(self, origin=None): + """Read the next token and raise an exception if it is not an identifier. + + @raises dns.exception.SyntaxError: + @rtype: string + """ + + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError('expecting an identifier') + return token.value + + def get_name(self, origin=None): + """Read the next token and interpret it as a DNS name. + + @raises dns.exception.SyntaxError: + @rtype: dns.name.Name object""" + + token = self.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError('expecting an identifier') + return dns.name.from_text(token.value, origin) + + def get_eol(self): + """Read the next token and raise an exception if it isn't EOL or + EOF. + + @raises dns.exception.SyntaxError: + @rtype: string + """ + + token = self.get() + if not token.is_eol_or_eof(): + raise dns.exception.SyntaxError('expected EOL or EOF, got %d "%s"' % (token.ttype, token.value)) + return token.value + + def get_ttl(self): + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError('expecting an identifier') + return dns.ttl.from_text(token.value) diff --git a/third_party/dnspython/dns/tsig.py b/third_party/dnspython/dns/tsig.py new file mode 100644 index 0000000000..63b925afbb --- /dev/null +++ b/third_party/dnspython/dns/tsig.py @@ -0,0 +1,223 @@ +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS TSIG support.""" + +import hmac +import struct +import sys + +import dns.exception +import dns.hash +import dns.rdataclass +import dns.name + +class BadTime(dns.exception.DNSException): + """Raised if the current time is not within the TSIG's validity time.""" + pass + +class BadSignature(dns.exception.DNSException): + """Raised if the TSIG signature fails to verify.""" + pass + +class PeerError(dns.exception.DNSException): + """Base class for all TSIG errors generated by the remote peer""" + pass + +class PeerBadKey(PeerError): + """Raised if the peer didn't know the key we used""" + pass + +class PeerBadSignature(PeerError): + """Raised if the peer didn't like the signature we sent""" + pass + +class PeerBadTime(PeerError): + """Raised if the peer didn't like the time we sent""" + pass + +class PeerBadTruncation(PeerError): + """Raised if the peer didn't like amount of truncation in the TSIG we sent""" + pass + +# TSIG Algorithms + +HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") +HMAC_SHA1 = dns.name.from_text("hmac-sha1") +HMAC_SHA224 = dns.name.from_text("hmac-sha224") +HMAC_SHA256 = dns.name.from_text("hmac-sha256") +HMAC_SHA384 = dns.name.from_text("hmac-sha384") +HMAC_SHA512 = dns.name.from_text("hmac-sha512") + +default_algorithm = HMAC_MD5 + +BADSIG = 16 +BADKEY = 17 +BADTIME = 18 +BADTRUNC = 22 + +def sign(wire, keyname, secret, time, fudge, original_id, error, + other_data, request_mac, ctx=None, multi=False, first=True, + algorithm=default_algorithm): + """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata + for the input parameters, the HMAC MAC calculated by applying the + TSIG signature algorithm, and the TSIG digest context. + @rtype: (string, string, hmac.HMAC object) + @raises ValueError: I{other_data} is too long + @raises NotImplementedError: I{algorithm} is not supported + """ + + (algorithm_name, digestmod) = get_algorithm(algorithm) + if first: + ctx = hmac.new(secret, digestmod=digestmod) + ml = len(request_mac) + if ml > 0: + ctx.update(struct.pack('!H', ml)) + ctx.update(request_mac) + id = struct.pack('!H', original_id) + ctx.update(id) + ctx.update(wire[2:]) + if first: + ctx.update(keyname.to_digestable()) + ctx.update(struct.pack('!H', dns.rdataclass.ANY)) + ctx.update(struct.pack('!I', 0)) + long_time = time + 0L + upper_time = (long_time >> 32) & 0xffffL + lower_time = long_time & 0xffffffffL + time_mac = struct.pack('!HIH', upper_time, lower_time, fudge) + pre_mac = algorithm_name + time_mac + ol = len(other_data) + if ol > 65535: + raise ValueError('TSIG Other Data is > 65535 bytes') + post_mac = struct.pack('!HH', error, ol) + other_data + if first: + ctx.update(pre_mac) + ctx.update(post_mac) + else: + ctx.update(time_mac) + mac = ctx.digest() + mpack = struct.pack('!H', len(mac)) + tsig_rdata = pre_mac + mpack + mac + id + post_mac + if multi: + ctx = hmac.new(secret) + ml = len(mac) + ctx.update(struct.pack('!H', ml)) + ctx.update(mac) + else: + ctx = None + return (tsig_rdata, mac, ctx) + +def hmac_md5(wire, keyname, secret, time, fudge, original_id, error, + other_data, request_mac, ctx=None, multi=False, first=True, + algorithm=default_algorithm): + return sign(wire, keyname, secret, time, fudge, original_id, error, + other_data, request_mac, ctx, multi, first, algorithm) + +def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, + tsig_rdlen, ctx=None, multi=False, first=True): + """Validate the specified TSIG rdata against the other input parameters. + + @raises FormError: The TSIG is badly formed. + @raises BadTime: There is too much time skew between the client and the + server. + @raises BadSignature: The TSIG signature did not validate + @rtype: hmac.HMAC object""" + + (adcount,) = struct.unpack("!H", wire[10:12]) + if adcount == 0: + raise dns.exception.FormError + adcount -= 1 + new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] + current = tsig_rdata + (aname, used) = dns.name.from_wire(wire, current) + current = current + used + (upper_time, lower_time, fudge, mac_size) = \ + struct.unpack("!HIHH", wire[current:current + 10]) + time = ((upper_time + 0L) << 32) + (lower_time + 0L) + current += 10 + mac = wire[current:current + mac_size] + current += mac_size + (original_id, error, other_size) = \ + struct.unpack("!HHH", wire[current:current + 6]) + current += 6 + other_data = wire[current:current + other_size] + current += other_size + if current != tsig_rdata + tsig_rdlen: + raise dns.exception.FormError + if error != 0: + if error == BADSIG: + raise PeerBadSignature + elif error == BADKEY: + raise PeerBadKey + elif error == BADTIME: + raise PeerBadTime + elif error == BADTRUNC: + raise PeerBadTruncation + else: + raise PeerError('unknown TSIG error code %d' % error) + time_low = time - fudge + time_high = time + fudge + if now < time_low or now > time_high: + raise BadTime + (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge, + original_id, error, other_data, + request_mac, ctx, multi, first, aname) + if (our_mac != mac): + raise BadSignature + return ctx + +_hashes = None + +def _maybe_add_hash(tsig_alg, hash_alg): + try: + _hashes[tsig_alg] = dns.hash.get(hash_alg) + except KeyError: + pass + +def _setup_hashes(): + global _hashes + _hashes = {} + _maybe_add_hash(HMAC_SHA224, 'SHA224') + _maybe_add_hash(HMAC_SHA256, 'SHA256') + _maybe_add_hash(HMAC_SHA384, 'SHA384') + _maybe_add_hash(HMAC_SHA512, 'SHA512') + _maybe_add_hash(HMAC_SHA1, 'SHA1') + _maybe_add_hash(HMAC_MD5, 'MD5') + +def get_algorithm(algorithm): + """Returns the wire format string and the hash module to use for the + specified TSIG algorithm + + @rtype: (string, hash constructor) + @raises NotImplementedError: I{algorithm} is not supported + """ + + global _hashes + if _hashes is None: + _setup_hashes() + + if isinstance(algorithm, (str, unicode)): + algorithm = dns.name.from_text(algorithm) + + if sys.hexversion < 0x02050200 and \ + (algorithm == HMAC_SHA384 or algorithm == HMAC_SHA512): + raise NotImplementedError("TSIG algorithm " + str(algorithm) + + " requires Python 2.5.2 or later") + + try: + return (algorithm.to_digestable(), _hashes[algorithm]) + except KeyError: + raise NotImplementedError("TSIG algorithm " + str(algorithm) + + " is not supported") diff --git a/third_party/dnspython/dns/tsigkeyring.py b/third_party/dnspython/dns/tsigkeyring.py new file mode 100644 index 0000000000..0ddd93410f --- /dev/null +++ b/third_party/dnspython/dns/tsigkeyring.py @@ -0,0 +1,44 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""A place to store TSIG keys.""" + +import base64 + +import dns.name + +def from_text(textring): + """Convert a dictionary containing (textual DNS name, base64 secret) pairs + into a binary keyring which has (dns.name.Name, binary secret) pairs. + @rtype: dict""" + + keyring = {} + for keytext in textring: + keyname = dns.name.from_text(keytext) + secret = base64.decodestring(textring[keytext]) + keyring[keyname] = secret + return keyring + +def to_text(keyring): + """Convert a dictionary containing (dns.name.Name, binary secret) pairs + into a text keyring which has (textual DNS name, base64 secret) pairs. + @rtype: dict""" + + textring = {} + for keyname in keyring: + keytext = dns.name.to_text(keyname) + secret = base64.encodestring(keyring[keyname]) + textring[keytext] = secret + return textring diff --git a/third_party/dnspython/dns/ttl.py b/third_party/dnspython/dns/ttl.py new file mode 100644 index 0000000000..ab6ddf4e66 --- /dev/null +++ b/third_party/dnspython/dns/ttl.py @@ -0,0 +1,64 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS TTL conversion.""" + +import dns.exception + +class BadTTL(dns.exception.SyntaxError): + pass + +def from_text(text): + """Convert the text form of a TTL to an integer. + + The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. + + @param text: the textual TTL + @type text: string + @raises dns.ttl.BadTTL: the TTL is not well-formed + @rtype: int + """ + + if text.isdigit(): + total = long(text) + else: + if not text[0].isdigit(): + raise BadTTL + total = 0L + current = 0L + for c in text: + if c.isdigit(): + current *= 10 + current += long(c) + else: + c = c.lower() + if c == 'w': + total += current * 604800L + elif c == 'd': + total += current * 86400L + elif c == 'h': + total += current * 3600L + elif c == 'm': + total += current * 60L + elif c == 's': + total += current + else: + raise BadTTL("unknown unit '%s'" % c) + current = 0 + if not current == 0: + raise BadTTL("trailing integer") + if total < 0L or total > 2147483647L: + raise BadTTL("TTL should be between 0 and 2^31 - 1 (inclusive)") + return total diff --git a/third_party/dnspython/dns/update.py b/third_party/dnspython/dns/update.py new file mode 100644 index 0000000000..e692226918 --- /dev/null +++ b/third_party/dnspython/dns/update.py @@ -0,0 +1,245 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Dynamic Update Support""" + +import dns.message +import dns.name +import dns.opcode +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.tsig + +class Update(dns.message.Message): + def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None, + keyname=None, keyalgorithm=dns.tsig.default_algorithm): + """Initialize a new DNS Update object. + + @param zone: The zone which is being updated. + @type zone: A dns.name.Name or string + @param rdclass: The class of the zone; defaults to dns.rdataclass.IN. + @type rdclass: An int designating the class, or a string whose value + is the name of a class. + @param keyring: The TSIG keyring to use; defaults to None. + @type keyring: dict + @param keyname: The name of the TSIG key to use; defaults to None. + The key must be defined in the keyring. If a keyring is specified + but a keyname is not, then the key used will be the first key in the + keyring. Note that the order of keys in a dictionary is not defined, + so applications should supply a keyname when a keyring is used, unless + they know the keyring contains only one key. + @type keyname: dns.name.Name or string + @param keyalgorithm: The TSIG algorithm to use; defaults to + dns.tsig.default_algorithm. Constants for TSIG algorithms are defined + in dns.tsig, and the currently implemented algorithms are + HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and + HMAC_SHA512. + @type keyalgorithm: string + """ + super(Update, self).__init__() + self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) + if isinstance(zone, (str, unicode)): + zone = dns.name.from_text(zone) + self.origin = zone + if isinstance(rdclass, str): + rdclass = dns.rdataclass.from_text(rdclass) + self.zone_rdclass = rdclass + self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA, + create=True, force_unique=True) + if not keyring is None: + self.use_tsig(keyring, keyname, algorithm=keyalgorithm) + + def _add_rr(self, name, ttl, rd, deleting=None, section=None): + """Add a single RR to the update section.""" + + if section is None: + section = self.authority + covers = rd.covers() + rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype, + covers, deleting, True, True) + rrset.add(rd, ttl) + + def _add(self, replace, section, name, *args): + """Add records. The first argument is the replace mode. If + false, RRs are added to an existing RRset; if true, the RRset + is replaced with the specified contents. The second + argument is the section to add to. The third argument + is always a name. The other arguments can be: + + - rdataset... + + - ttl, rdata... + + - ttl, rdtype, string...""" + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + if isinstance(args[0], dns.rdataset.Rdataset): + for rds in args: + if replace: + self.delete(name, rds.rdtype) + for rd in rds: + self._add_rr(name, rds.ttl, rd, section=section) + else: + args = list(args) + ttl = int(args.pop(0)) + if isinstance(args[0], dns.rdata.Rdata): + if replace: + self.delete(name, args[0].rdtype) + for rd in args: + self._add_rr(name, ttl, rd, section=section) + else: + rdtype = args.pop(0) + if isinstance(rdtype, str): + rdtype = dns.rdatatype.from_text(rdtype) + if replace: + self.delete(name, rdtype) + for s in args: + rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, + self.origin) + self._add_rr(name, ttl, rd, section=section) + + def add(self, name, *args): + """Add records. The first argument is always a name. The other + arguments can be: + + - rdataset... + + - ttl, rdata... + + - ttl, rdtype, string...""" + self._add(False, self.authority, name, *args) + + def delete(self, name, *args): + """Delete records. The first argument is always a name. The other + arguments can be: + + - I{nothing} + + - rdataset... + + - rdata... + + - rdtype, [string...]""" + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + if len(args) == 0: + rrset = self.find_rrset(self.authority, name, dns.rdataclass.ANY, + dns.rdatatype.ANY, dns.rdatatype.NONE, + dns.rdatatype.ANY, True, True) + elif isinstance(args[0], dns.rdataset.Rdataset): + for rds in args: + for rd in rds: + self._add_rr(name, 0, rd, dns.rdataclass.NONE) + else: + args = list(args) + if isinstance(args[0], dns.rdata.Rdata): + for rd in args: + self._add_rr(name, 0, rd, dns.rdataclass.NONE) + else: + rdtype = args.pop(0) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if len(args) == 0: + rrset = self.find_rrset(self.authority, name, + self.zone_rdclass, rdtype, + dns.rdatatype.NONE, + dns.rdataclass.ANY, + True, True) + else: + for s in args: + rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, + self.origin) + self._add_rr(name, 0, rd, dns.rdataclass.NONE) + + def replace(self, name, *args): + """Replace records. The first argument is always a name. The other + arguments can be: + + - rdataset... + + - ttl, rdata... + + - ttl, rdtype, string... + + Note that if you want to replace the entire node, you should do + a delete of the name followed by one or more calls to add.""" + + self._add(True, self.authority, name, *args) + + def present(self, name, *args): + """Require that an owner name (and optionally an rdata type, + or specific rdataset) exists as a prerequisite to the + execution of the update. The first argument is always a name. + The other arguments can be: + + - rdataset... + + - rdata... + + - rdtype, string...""" + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + if len(args) == 0: + rrset = self.find_rrset(self.answer, name, + dns.rdataclass.ANY, dns.rdatatype.ANY, + dns.rdatatype.NONE, None, + True, True) + elif isinstance(args[0], dns.rdataset.Rdataset) or \ + isinstance(args[0], dns.rdata.Rdata) or \ + len(args) > 1: + if not isinstance(args[0], dns.rdataset.Rdataset): + # Add a 0 TTL + args = list(args) + args.insert(0, 0) + self._add(False, self.answer, name, *args) + else: + rdtype = args[0] + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + rrset = self.find_rrset(self.answer, name, + dns.rdataclass.ANY, rdtype, + dns.rdatatype.NONE, None, + True, True) + + def absent(self, name, rdtype=None): + """Require that an owner name (and optionally an rdata type) does + not exist as a prerequisite to the execution of the update.""" + + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + if rdtype is None: + rrset = self.find_rrset(self.answer, name, + dns.rdataclass.NONE, dns.rdatatype.ANY, + dns.rdatatype.NONE, None, + True, True) + else: + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + rrset = self.find_rrset(self.answer, name, + dns.rdataclass.NONE, rdtype, + dns.rdatatype.NONE, None, + True, True) + + def to_wire(self, origin=None, max_size=65535): + """Return a string containing the update in DNS compressed wire + format. + @rtype: string""" + if origin is None: + origin = self.origin + return super(Update, self).to_wire(origin, max_size) diff --git a/third_party/dnspython/dns/version.py b/third_party/dnspython/dns/version.py new file mode 100644 index 0000000000..7de430b638 --- /dev/null +++ b/third_party/dnspython/dns/version.py @@ -0,0 +1,34 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""dnspython release version information.""" + +MAJOR = 1 +MINOR = 10 +MICRO = 0 +RELEASELEVEL = 0x0f +SERIAL = 0 + +if RELEASELEVEL == 0x0f: + version = '%d.%d.%d' % (MAJOR, MINOR, MICRO) +elif RELEASELEVEL == 0x00: + version = '%d.%d.%dx%d' % \ + (MAJOR, MINOR, MICRO, SERIAL) +else: + version = '%d.%d.%d%x%d' % \ + (MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL) + +hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | \ + SERIAL diff --git a/third_party/dnspython/dns/wiredata.py b/third_party/dnspython/dns/wiredata.py new file mode 100644 index 0000000000..86d954a90a --- /dev/null +++ b/third_party/dnspython/dns/wiredata.py @@ -0,0 +1,59 @@ +# Copyright (C) 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Wire Data Helper""" + +import sys + +import dns.exception + +class WireData(str): + # WireData is a string with stricter slicing + def __getitem__(self, key): + try: + return WireData(super(WireData, self).__getitem__(key)) + except IndexError: + raise dns.exception.FormError + def __getslice__(self, i, j): + try: + if j == sys.maxint: + # handle the case where the right bound is unspecified + j = len(self) + if i < 0 or j < 0: + raise dns.exception.FormError + # If it's not an empty slice, access left and right bounds + # to make sure they're valid + if i != j: + super(WireData, self).__getitem__(i) + super(WireData, self).__getitem__(j - 1) + return WireData(super(WireData, self).__getslice__(i, j)) + except IndexError: + raise dns.exception.FormError + def __iter__(self): + i = 0 + while 1: + try: + yield self[i] + i += 1 + except dns.exception.FormError: + raise StopIteration + def unwrap(self): + return str(self) + +def maybe_wrap(wire): + if not isinstance(wire, WireData): + return WireData(wire) + else: + return wire diff --git a/third_party/dnspython/dns/zone.py b/third_party/dnspython/dns/zone.py new file mode 100644 index 0000000000..67c952d3cc --- /dev/null +++ b/third_party/dnspython/dns/zone.py @@ -0,0 +1,855 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Zones.""" + +from __future__ import generators + +import sys + +import dns.exception +import dns.name +import dns.node +import dns.rdataclass +import dns.rdatatype +import dns.rdata +import dns.rrset +import dns.tokenizer +import dns.ttl + +class BadZone(dns.exception.DNSException): + """The zone is malformed.""" + pass + +class NoSOA(BadZone): + """The zone has no SOA RR at its origin.""" + pass + +class NoNS(BadZone): + """The zone has no NS RRset at its origin.""" + pass + +class UnknownOrigin(BadZone): + """The zone's origin is unknown.""" + pass + +class Zone(object): + """A DNS zone. + + A Zone is a mapping from names to nodes. The zone object may be + treated like a Python dictionary, e.g. zone[name] will retrieve + the node associated with that name. The I{name} may be a + dns.name.Name object, or it may be a string. In the either case, + if the name is relative it is treated as relative to the origin of + the zone. + + @ivar rdclass: The zone's rdata class; the default is class IN. + @type rdclass: int + @ivar origin: The origin of the zone. + @type origin: dns.name.Name object + @ivar nodes: A dictionary mapping the names of nodes in the zone to the + nodes themselves. + @type nodes: dict + @ivar relativize: should names in the zone be relativized? + @type relativize: bool + @cvar node_factory: the factory used to create a new node + @type node_factory: class or callable + """ + + node_factory = dns.node.Node + + __slots__ = ['rdclass', 'origin', 'nodes', 'relativize'] + + def __init__(self, origin, rdclass=dns.rdataclass.IN, relativize=True): + """Initialize a zone object. + + @param origin: The origin of the zone. + @type origin: dns.name.Name object + @param rdclass: The zone's rdata class; the default is class IN. + @type rdclass: int""" + + self.rdclass = rdclass + self.origin = origin + self.nodes = {} + self.relativize = relativize + + def __eq__(self, other): + """Two zones are equal if they have the same origin, class, and + nodes. + @rtype: bool + """ + + if not isinstance(other, Zone): + return False + if self.rdclass != other.rdclass or \ + self.origin != other.origin or \ + self.nodes != other.nodes: + return False + return True + + def __ne__(self, other): + """Are two zones not equal? + @rtype: bool + """ + + return not self.__eq__(other) + + def _validate_name(self, name): + if isinstance(name, (str, unicode)): + name = dns.name.from_text(name, None) + elif not isinstance(name, dns.name.Name): + raise KeyError("name parameter must be convertable to a DNS name") + if name.is_absolute(): + if not name.is_subdomain(self.origin): + raise KeyError("name parameter must be a subdomain of the zone origin") + if self.relativize: + name = name.relativize(self.origin) + return name + + def __getitem__(self, key): + key = self._validate_name(key) + return self.nodes[key] + + def __setitem__(self, key, value): + key = self._validate_name(key) + self.nodes[key] = value + + def __delitem__(self, key): + key = self._validate_name(key) + del self.nodes[key] + + def __iter__(self): + return self.nodes.iterkeys() + + def iterkeys(self): + return self.nodes.iterkeys() + + def keys(self): + return self.nodes.keys() + + def itervalues(self): + return self.nodes.itervalues() + + def values(self): + return self.nodes.values() + + def iteritems(self): + return self.nodes.iteritems() + + def items(self): + return self.nodes.items() + + def get(self, key): + key = self._validate_name(key) + return self.nodes.get(key) + + def __contains__(self, other): + return other in self.nodes + + def find_node(self, name, create=False): + """Find a node in the zone, possibly creating it. + + @param name: the name of the node to find + @type name: dns.name.Name object or string + @param create: should the node be created if it doesn't exist? + @type create: bool + @raises KeyError: the name is not known and create was not specified. + @rtype: dns.node.Node object + """ + + name = self._validate_name(name) + node = self.nodes.get(name) + if node is None: + if not create: + raise KeyError + node = self.node_factory() + self.nodes[name] = node + return node + + def get_node(self, name, create=False): + """Get a node in the zone, possibly creating it. + + This method is like L{find_node}, except it returns None instead + of raising an exception if the node does not exist and creation + has not been requested. + + @param name: the name of the node to find + @type name: dns.name.Name object or string + @param create: should the node be created if it doesn't exist? + @type create: bool + @rtype: dns.node.Node object or None + """ + + try: + node = self.find_node(name, create) + except KeyError: + node = None + return node + + def delete_node(self, name): + """Delete the specified node if it exists. + + It is not an error if the node does not exist. + """ + + name = self._validate_name(name) + if self.nodes.has_key(name): + del self.nodes[name] + + def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, + create=False): + """Look for rdata with the specified name and type in the zone, + and return an rdataset encapsulating it. + + The I{name}, I{rdtype}, and I{covers} parameters may be + strings, in which case they will be converted to their proper + type. + + The rdataset returned is not a copy; changes to it will change + the zone. + + KeyError is raised if the name or type are not found. + Use L{get_rdataset} if you want to have None returned instead. + + @param name: the owner name to look for + @type name: DNS.name.Name object or string + @param rdtype: the rdata type desired + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + @param create: should the node and rdataset be created if they do not + exist? + @type create: bool + @raises KeyError: the node or rdata could not be found + @rtype: dns.rrset.RRset object + """ + + name = self._validate_name(name) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if isinstance(covers, (str, unicode)): + covers = dns.rdatatype.from_text(covers) + node = self.find_node(name, create) + return node.find_rdataset(self.rdclass, rdtype, covers, create) + + def get_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, + create=False): + """Look for rdata with the specified name and type in the zone, + and return an rdataset encapsulating it. + + The I{name}, I{rdtype}, and I{covers} parameters may be + strings, in which case they will be converted to their proper + type. + + The rdataset returned is not a copy; changes to it will change + the zone. + + None is returned if the name or type are not found. + Use L{find_rdataset} if you want to have KeyError raised instead. + + @param name: the owner name to look for + @type name: DNS.name.Name object or string + @param rdtype: the rdata type desired + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + @param create: should the node and rdataset be created if they do not + exist? + @type create: bool + @rtype: dns.rrset.RRset object + """ + + try: + rdataset = self.find_rdataset(name, rdtype, covers, create) + except KeyError: + rdataset = None + return rdataset + + def delete_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE): + """Delete the rdataset matching I{rdtype} and I{covers}, if it + exists at the node specified by I{name}. + + The I{name}, I{rdtype}, and I{covers} parameters may be + strings, in which case they will be converted to their proper + type. + + It is not an error if the node does not exist, or if there is no + matching rdataset at the node. + + If the node has no rdatasets after the deletion, it will itself + be deleted. + + @param name: the owner name to look for + @type name: DNS.name.Name object or string + @param rdtype: the rdata type desired + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + """ + + name = self._validate_name(name) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if isinstance(covers, (str, unicode)): + covers = dns.rdatatype.from_text(covers) + node = self.get_node(name) + if not node is None: + node.delete_rdataset(self.rdclass, rdtype, covers) + if len(node) == 0: + self.delete_node(name) + + def replace_rdataset(self, name, replacement): + """Replace an rdataset at name. + + It is not an error if there is no rdataset matching I{replacement}. + + Ownership of the I{replacement} object is transferred to the zone; + in other words, this method does not store a copy of I{replacement} + at the node, it stores I{replacement} itself. + + If the I{name} node does not exist, it is created. + + @param name: the owner name + @type name: DNS.name.Name object or string + @param replacement: the replacement rdataset + @type replacement: dns.rdataset.Rdataset + """ + + if replacement.rdclass != self.rdclass: + raise ValueError('replacement.rdclass != zone.rdclass') + node = self.find_node(name, True) + node.replace_rdataset(replacement) + + def find_rrset(self, name, rdtype, covers=dns.rdatatype.NONE): + """Look for rdata with the specified name and type in the zone, + and return an RRset encapsulating it. + + The I{name}, I{rdtype}, and I{covers} parameters may be + strings, in which case they will be converted to their proper + type. + + This method is less efficient than the similar + L{find_rdataset} because it creates an RRset instead of + returning the matching rdataset. It may be more convenient + for some uses since it returns an object which binds the owner + name to the rdata. + + This method may not be used to create new nodes or rdatasets; + use L{find_rdataset} instead. + + KeyError is raised if the name or type are not found. + Use L{get_rrset} if you want to have None returned instead. + + @param name: the owner name to look for + @type name: DNS.name.Name object or string + @param rdtype: the rdata type desired + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + @raises KeyError: the node or rdata could not be found + @rtype: dns.rrset.RRset object + """ + + name = self._validate_name(name) + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if isinstance(covers, (str, unicode)): + covers = dns.rdatatype.from_text(covers) + rdataset = self.nodes[name].find_rdataset(self.rdclass, rdtype, covers) + rrset = dns.rrset.RRset(name, self.rdclass, rdtype, covers) + rrset.update(rdataset) + return rrset + + def get_rrset(self, name, rdtype, covers=dns.rdatatype.NONE): + """Look for rdata with the specified name and type in the zone, + and return an RRset encapsulating it. + + The I{name}, I{rdtype}, and I{covers} parameters may be + strings, in which case they will be converted to their proper + type. + + This method is less efficient than the similar L{get_rdataset} + because it creates an RRset instead of returning the matching + rdataset. It may be more convenient for some uses since it + returns an object which binds the owner name to the rdata. + + This method may not be used to create new nodes or rdatasets; + use L{find_rdataset} instead. + + None is returned if the name or type are not found. + Use L{find_rrset} if you want to have KeyError raised instead. + + @param name: the owner name to look for + @type name: DNS.name.Name object or string + @param rdtype: the rdata type desired + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + @rtype: dns.rrset.RRset object + """ + + try: + rrset = self.find_rrset(name, rdtype, covers) + except KeyError: + rrset = None + return rrset + + def iterate_rdatasets(self, rdtype=dns.rdatatype.ANY, + covers=dns.rdatatype.NONE): + """Return a generator which yields (name, rdataset) tuples for + all rdatasets in the zone which have the specified I{rdtype} + and I{covers}. If I{rdtype} is dns.rdatatype.ANY, the default, + then all rdatasets will be matched. + + @param rdtype: int or string + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + """ + + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if isinstance(covers, (str, unicode)): + covers = dns.rdatatype.from_text(covers) + for (name, node) in self.iteritems(): + for rds in node: + if rdtype == dns.rdatatype.ANY or \ + (rds.rdtype == rdtype and rds.covers == covers): + yield (name, rds) + + def iterate_rdatas(self, rdtype=dns.rdatatype.ANY, + covers=dns.rdatatype.NONE): + """Return a generator which yields (name, ttl, rdata) tuples for + all rdatas in the zone which have the specified I{rdtype} + and I{covers}. If I{rdtype} is dns.rdatatype.ANY, the default, + then all rdatas will be matched. + + @param rdtype: int or string + @type rdtype: int or string + @param covers: the covered type (defaults to None) + @type covers: int or string + """ + + if isinstance(rdtype, (str, unicode)): + rdtype = dns.rdatatype.from_text(rdtype) + if isinstance(covers, (str, unicode)): + covers = dns.rdatatype.from_text(covers) + for (name, node) in self.iteritems(): + for rds in node: + if rdtype == dns.rdatatype.ANY or \ + (rds.rdtype == rdtype and rds.covers == covers): + for rdata in rds: + yield (name, rds.ttl, rdata) + + def to_file(self, f, sorted=True, relativize=True, nl=None): + """Write a zone to a file. + + @param f: file or string. If I{f} is a string, it is treated + as the name of a file to open. + @param sorted: if True, the file will be written with the + names sorted in DNSSEC order from least to greatest. Otherwise + the names will be written in whatever order they happen to have + in the zone's dictionary. + @param relativize: if True, domain names in the output will be + relativized to the zone's origin (if possible). + @type relativize: bool + @param nl: The end of line string. If not specified, the + output will use the platform's native end-of-line marker (i.e. + LF on POSIX, CRLF on Windows, CR on Macintosh). + @type nl: string or None + """ + + if sys.hexversion >= 0x02030000: + # allow Unicode filenames + str_type = basestring + else: + str_type = str + if nl is None: + opts = 'w' + else: + opts = 'wb' + if isinstance(f, str_type): + f = file(f, opts) + want_close = True + else: + want_close = False + try: + if sorted: + names = self.keys() + names.sort() + else: + names = self.iterkeys() + for n in names: + l = self[n].to_text(n, origin=self.origin, + relativize=relativize) + if nl is None: + print >> f, l + else: + f.write(l) + f.write(nl) + finally: + if want_close: + f.close() + + def check_origin(self): + """Do some simple checking of the zone's origin. + + @raises dns.zone.NoSOA: there is no SOA RR + @raises dns.zone.NoNS: there is no NS RRset + @raises KeyError: there is no origin node + """ + if self.relativize: + name = dns.name.empty + else: + name = self.origin + if self.get_rdataset(name, dns.rdatatype.SOA) is None: + raise NoSOA + if self.get_rdataset(name, dns.rdatatype.NS) is None: + raise NoNS + + +class _MasterReader(object): + """Read a DNS master file + + @ivar tok: The tokenizer + @type tok: dns.tokenizer.Tokenizer object + @ivar ttl: The default TTL + @type ttl: int + @ivar last_name: The last name read + @type last_name: dns.name.Name object + @ivar current_origin: The current origin + @type current_origin: dns.name.Name object + @ivar relativize: should names in the zone be relativized? + @type relativize: bool + @ivar zone: the zone + @type zone: dns.zone.Zone object + @ivar saved_state: saved reader state (used when processing $INCLUDE) + @type saved_state: list of (tokenizer, current_origin, last_name, file) + tuples. + @ivar current_file: the file object of the $INCLUDed file being parsed + (None if no $INCLUDE is active). + @ivar allow_include: is $INCLUDE allowed? + @type allow_include: bool + @ivar check_origin: should sanity checks of the origin node be done? + The default is True. + @type check_origin: bool + """ + + def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone, + allow_include=False, check_origin=True): + if isinstance(origin, (str, unicode)): + origin = dns.name.from_text(origin) + self.tok = tok + self.current_origin = origin + self.relativize = relativize + self.ttl = 0 + self.last_name = None + self.zone = zone_factory(origin, rdclass, relativize=relativize) + self.saved_state = [] + self.current_file = None + self.allow_include = allow_include + self.check_origin = check_origin + + def _eat_line(self): + while 1: + token = self.tok.get() + if token.is_eol_or_eof(): + break + + def _rr_line(self): + """Process one line from a DNS master file.""" + # Name + if self.current_origin is None: + raise UnknownOrigin + token = self.tok.get(want_leading = True) + if not token.is_whitespace(): + self.last_name = dns.name.from_text(token.value, self.current_origin) + else: + token = self.tok.get() + if token.is_eol_or_eof(): + # treat leading WS followed by EOL/EOF as if they were EOL/EOF. + return + self.tok.unget(token) + name = self.last_name + if not name.is_subdomain(self.zone.origin): + self._eat_line() + return + if self.relativize: + name = name.relativize(self.zone.origin) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + # TTL + try: + ttl = dns.ttl.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.ttl.BadTTL: + ttl = self.ttl + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except: + rdclass = self.zone.rdclass + if rdclass != self.zone.rdclass: + raise dns.exception.SyntaxError("RR class is not zone's class") + # Type + try: + rdtype = dns.rdatatype.from_text(token.value) + except: + raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) + n = self.zone.nodes.get(name) + if n is None: + n = self.zone.node_factory() + self.zone.nodes[name] = n + try: + rd = dns.rdata.from_text(rdclass, rdtype, self.tok, + self.current_origin, False) + except dns.exception.SyntaxError: + # Catch and reraise. + (ty, va) = sys.exc_info()[:2] + raise va + except: + # All exceptions that occur in the processing of rdata + # are treated as syntax errors. This is not strictly + # correct, but it is correct almost all of the time. + # We convert them to syntax errors so that we can emit + # helpful filename:line info. + (ty, va) = sys.exc_info()[:2] + raise dns.exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) + + rd.choose_relativity(self.zone.origin, self.relativize) + covers = rd.covers() + rds = n.find_rdataset(rdclass, rdtype, covers, True) + rds.add(rd, ttl) + + def read(self): + """Read a DNS master file and build a zone object. + + @raises dns.zone.NoSOA: No SOA RR was found at the zone origin + @raises dns.zone.NoNS: No NS RRset was found at the zone origin + """ + + try: + while 1: + token = self.tok.get(True, True).unescape() + if token.is_eof(): + if not self.current_file is None: + self.current_file.close() + if len(self.saved_state) > 0: + (self.tok, + self.current_origin, + self.last_name, + self.current_file, + self.ttl) = self.saved_state.pop(-1) + continue + break + elif token.is_eol(): + continue + elif token.is_comment(): + self.tok.get_eol() + continue + elif token.value[0] == '$': + u = token.value.upper() + if u == '$TTL': + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError("bad $TTL") + self.ttl = dns.ttl.from_text(token.value) + self.tok.get_eol() + elif u == '$ORIGIN': + self.current_origin = self.tok.get_name() + self.tok.get_eol() + if self.zone.origin is None: + self.zone.origin = self.current_origin + elif u == '$INCLUDE' and self.allow_include: + token = self.tok.get() + if not token.is_quoted_string(): + raise dns.exception.SyntaxError("bad filename in $INCLUDE") + filename = token.value + token = self.tok.get() + if token.is_identifier(): + new_origin = dns.name.from_text(token.value, \ + self.current_origin) + self.tok.get_eol() + elif not token.is_eol_or_eof(): + raise dns.exception.SyntaxError("bad origin in $INCLUDE") + else: + new_origin = self.current_origin + self.saved_state.append((self.tok, + self.current_origin, + self.last_name, + self.current_file, + self.ttl)) + self.current_file = file(filename, 'r') + self.tok = dns.tokenizer.Tokenizer(self.current_file, + filename) + self.current_origin = new_origin + else: + raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'") + continue + self.tok.unget(token) + self._rr_line() + except dns.exception.SyntaxError, detail: + (filename, line_number) = self.tok.where() + if detail is None: + detail = "syntax error" + raise dns.exception.SyntaxError("%s:%d: %s" % (filename, line_number, detail)) + + # Now that we're done reading, do some basic checking of the zone. + if self.check_origin: + self.zone.check_origin() + +def from_text(text, origin = None, rdclass = dns.rdataclass.IN, + relativize = True, zone_factory=Zone, filename=None, + allow_include=False, check_origin=True): + """Build a zone object from a master file format string. + + @param text: the master file format input + @type text: string. + @param origin: The origin of the zone; if not specified, the first + $ORIGIN statement in the master file will determine the origin of the + zone. + @type origin: dns.name.Name object or string + @param rdclass: The zone's rdata class; the default is class IN. + @type rdclass: int + @param relativize: should names be relativized? The default is True + @type relativize: bool + @param zone_factory: The zone factory to use + @type zone_factory: function returning a Zone + @param filename: The filename to emit when describing where an error + occurred; the default is ''. + @type filename: string + @param allow_include: is $INCLUDE allowed? + @type allow_include: bool + @param check_origin: should sanity checks of the origin node be done? + The default is True. + @type check_origin: bool + @raises dns.zone.NoSOA: No SOA RR was found at the zone origin + @raises dns.zone.NoNS: No NS RRset was found at the zone origin + @rtype: dns.zone.Zone object + """ + + # 'text' can also be a file, but we don't publish that fact + # since it's an implementation detail. The official file + # interface is from_file(). + + if filename is None: + filename = '' + tok = dns.tokenizer.Tokenizer(text, filename) + reader = _MasterReader(tok, origin, rdclass, relativize, zone_factory, + allow_include=allow_include, + check_origin=check_origin) + reader.read() + return reader.zone + +def from_file(f, origin = None, rdclass = dns.rdataclass.IN, + relativize = True, zone_factory=Zone, filename=None, + allow_include=True, check_origin=True): + """Read a master file and build a zone object. + + @param f: file or string. If I{f} is a string, it is treated + as the name of a file to open. + @param origin: The origin of the zone; if not specified, the first + $ORIGIN statement in the master file will determine the origin of the + zone. + @type origin: dns.name.Name object or string + @param rdclass: The zone's rdata class; the default is class IN. + @type rdclass: int + @param relativize: should names be relativized? The default is True + @type relativize: bool + @param zone_factory: The zone factory to use + @type zone_factory: function returning a Zone + @param filename: The filename to emit when describing where an error + occurred; the default is '', or the value of I{f} if I{f} is a + string. + @type filename: string + @param allow_include: is $INCLUDE allowed? + @type allow_include: bool + @param check_origin: should sanity checks of the origin node be done? + The default is True. + @type check_origin: bool + @raises dns.zone.NoSOA: No SOA RR was found at the zone origin + @raises dns.zone.NoNS: No NS RRset was found at the zone origin + @rtype: dns.zone.Zone object + """ + + if sys.hexversion >= 0x02030000: + # allow Unicode filenames; turn on universal newline support + str_type = basestring + opts = 'rU' + else: + str_type = str + opts = 'r' + if isinstance(f, str_type): + if filename is None: + filename = f + f = file(f, opts) + want_close = True + else: + if filename is None: + filename = '' + want_close = False + + try: + z = from_text(f, origin, rdclass, relativize, zone_factory, + filename, allow_include, check_origin) + finally: + if want_close: + f.close() + return z + +def from_xfr(xfr, zone_factory=Zone, relativize=True): + """Convert the output of a zone transfer generator into a zone object. + + @param xfr: The xfr generator + @type xfr: generator of dns.message.Message objects + @param relativize: should names be relativized? The default is True. + It is essential that the relativize setting matches the one specified + to dns.query.xfr(). + @type relativize: bool + @raises dns.zone.NoSOA: No SOA RR was found at the zone origin + @raises dns.zone.NoNS: No NS RRset was found at the zone origin + @rtype: dns.zone.Zone object + """ + + z = None + for r in xfr: + if z is None: + if relativize: + origin = r.origin + else: + origin = r.answer[0].name + rdclass = r.answer[0].rdclass + z = zone_factory(origin, rdclass, relativize=relativize) + for rrset in r.answer: + znode = z.nodes.get(rrset.name) + if not znode: + znode = z.node_factory() + z.nodes[rrset.name] = znode + zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype, + rrset.covers, True) + zrds.update_ttl(rrset.ttl) + for rd in rrset: + rd.choose_relativity(z.origin, relativize) + zrds.add(rd) + z.check_origin() + return z diff --git a/third_party/dnspython/examples/ddns.py b/third_party/dnspython/examples/ddns.py new file mode 100755 index 0000000000..84814b73cf --- /dev/null +++ b/third_party/dnspython/examples/ddns.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +# +# Use a TSIG-signed DDNS update to update our hostname-to-address +# mapping. +# +# usage: ddns.py +# +# On linux systems, you can automatically update your DNS any time an +# interface comes up by adding an ifup-local script that invokes this +# python code. +# +# E.g. on my systems I have this +# +# #!/bin/sh +# +# DEVICE=$1 +# +# if [ "X${DEVICE}" == "Xeth0" ]; then +# IPADDR=`LANG= LC_ALL= ifconfig ${DEVICE} | grep 'inet addr' | +# awk -F: '{ print $2 } ' | awk '{ print $1 }'` +# /usr/local/sbin/ddns.py $IPADDR +# fi +# +# in /etc/ifup-local. +# + +import sys + +import dns.update +import dns.query +import dns.tsigkeyring + +# +# Replace the keyname and secret with appropriate values for your +# configuration. +# +keyring = dns.tsigkeyring.from_text({ + 'keyname.' : 'NjHwPsMKjdN++dOfE5iAiQ==' + }) + +# +# Replace "example." with your domain, and "host" with your hostname. +# +update = dns.update.Update('example.', keyring=keyring) +update.replace('host', 300, 'A', sys.argv[1]) + +# +# Replace "10.0.0.1" with the IP address of your master server. +# +response = dns.query.tcp(update, '10.0.0.1', timeout=10) diff --git a/third_party/dnspython/examples/e164.py b/third_party/dnspython/examples/e164.py new file mode 100755 index 0000000000..ad40ccf84b --- /dev/null +++ b/third_party/dnspython/examples/e164.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +import dns.e164 +n = dns.e164.from_e164("+1 555 1212") +print n +print dns.e164.to_e164(n) diff --git a/third_party/dnspython/examples/mx.py b/third_party/dnspython/examples/mx.py new file mode 100755 index 0000000000..3036e70ddf --- /dev/null +++ b/third_party/dnspython/examples/mx.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +import dns.resolver + +answers = dns.resolver.query('nominum.com', 'MX') +for rdata in answers: + print 'Host', rdata.exchange, 'has preference', rdata.preference diff --git a/third_party/dnspython/examples/name.py b/third_party/dnspython/examples/name.py new file mode 100755 index 0000000000..b099c49d16 --- /dev/null +++ b/third_party/dnspython/examples/name.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import dns.name + +n = dns.name.from_text('www.dnspython.org') +o = dns.name.from_text('dnspython.org') +print n.is_subdomain(o) # True +print n.is_superdomain(o) # False +print n > o # True +rel = n.relativize(o) # rel is the relative name www +n2 = rel + o +print n2 == n # True +print n.labels # ['www', 'dnspython', 'org', ''] diff --git a/third_party/dnspython/examples/reverse.py b/third_party/dnspython/examples/reverse.py new file mode 100755 index 0000000000..8657baed44 --- /dev/null +++ b/third_party/dnspython/examples/reverse.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Usage: reverse.py ... +# +# This demo script will load in all of the zones specified by the +# filenames on the command line, find all the A RRs in them, and +# construct a reverse mapping table that maps each IP address used to +# the list of names mapping to that address. The table is then sorted +# nicely and printed. +# +# Note! The zone name is taken from the basename of the filename, so +# you must use filenames like "/wherever/you/like/dnspython.org" and +# not something like "/wherever/you/like/foo.db" (unless you're +# working with the ".db" GTLD, of course :)). +# +# If this weren't a demo script, there'd be a way of specifying the +# origin for each zone instead of constructing it from the filename. + +import dns.zone +import dns.ipv4 +import os.path +import sys + +reverse_map = {} + +for filename in sys.argv[1:]: + zone = dns.zone.from_file(filename, os.path.basename(filename), + relativize=False) + for (name, ttl, rdata) in zone.iterate_rdatas('A'): + try: + reverse_map[rdata.address].append(name.to_text()) + except KeyError: + reverse_map[rdata.address] = [name.to_text()] + +keys = reverse_map.keys() +keys.sort(lambda a1, a2: cmp(dns.ipv4.inet_aton(a1), dns.ipv4.inet_aton(a2))) +for k in keys: + v = reverse_map[k] + v.sort() + print k, v diff --git a/third_party/dnspython/examples/reverse_name.py b/third_party/dnspython/examples/reverse_name.py new file mode 100755 index 0000000000..351896b015 --- /dev/null +++ b/third_party/dnspython/examples/reverse_name.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +import dns.reversename +n = dns.reversename.from_address("127.0.0.1") +print n +print dns.reversename.to_address(n) diff --git a/third_party/dnspython/examples/xfr.py b/third_party/dnspython/examples/xfr.py new file mode 100755 index 0000000000..e67ab18927 --- /dev/null +++ b/third_party/dnspython/examples/xfr.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +import dns.query +import dns.resolver +import dns.zone + +soa_answer = dns.resolver.query('dnspython.org', 'SOA') +master_answer = dns.resolver.query(soa_answer[0].mname, 'A') + +z = dns.zone.from_xfr(dns.query.xfr(master_answer[0].address, 'dnspython.org')) +names = z.nodes.keys() +names.sort() +for n in names: + print z[n].to_text(n) diff --git a/third_party/dnspython/examples/zonediff.py b/third_party/dnspython/examples/zonediff.py new file mode 100755 index 0000000000..ad81fb1d2d --- /dev/null +++ b/third_party/dnspython/examples/zonediff.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +# +# Small library and commandline tool to do logical diffs of zonefiles +# ./zonediff -h gives you help output +# +# Requires dnspython to do all the heavy lifting +# +# (c)2009 Dennis Kaarsemaker +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +"""See diff_zones.__doc__ for more information""" + +__all__ = ['diff_zones', 'format_changes_plain', 'format_changes_html'] + +try: + import dns.zone +except ImportError: + import sys + sys.stderr.write("Please install dnspython") + sys.exit(1) + +def diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False): + """diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False) -> changes + Compares two dns.zone.Zone objects and returns a list of all changes + in the format (name, oldnode, newnode). + + If ignore_ttl is true, a node will not be added to this list if the + only change is its TTL. + + If ignore_soa is true, a node will not be added to this list if the + only changes is a change in a SOA Rdata set. + + The returned nodes do include all Rdata sets, including unchanged ones. + """ + + changes = [] + for name in zone1: + name = str(name) + n1 = zone1.get_node(name) + n2 = zone2.get_node(name) + if not n2: + changes.append((str(name), n1, n2)) + elif _nodes_differ(n1, n2, ignore_ttl, ignore_soa): + changes.append((str(name), n1, n2)) + + for name in zone2: + n1 = zone1.get_node(name) + if not n1: + n2 = zone2.get_node(name) + changes.append((str(name), n1, n2)) + return changes + +def _nodes_differ(n1, n2, ignore_ttl, ignore_soa): + if ignore_soa or not ignore_ttl: + # Compare datasets directly + for r in n1.rdatasets: + if ignore_soa and r.rdtype == dns.rdatatype.SOA: + continue + if r not in n2.rdatasets: + return True + if not ignore_ttl: + return r.ttl != n2.find_rdataset(r.rdclass, r.rdtype).ttl + + for r in n2.rdatasets: + if ignore_soa and r.rdtype == dns.rdatatype.SOA: + continue + if r not in n1.rdatasets: + return True + else: + return n1 != n2 + +def format_changes_plain(oldf, newf, changes, ignore_ttl=False): + """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str + Given 2 filenames and a list of changes from diff_zones, produce diff-like + output. If ignore_ttl is True, TTL-only changes are not displayed""" + + ret = "--- %s\n+++ %s\n" % (oldf, newf) + for name, old, new in changes: + ret += "@ %s\n" % name + if not old: + for r in new.rdatasets: + ret += "+ %s\n" % str(r).replace('\n','\n+ ') + elif not new: + for r in old.rdatasets: + ret += "- %s\n" % str(r).replace('\n','\n+ ') + else: + for r in old.rdatasets: + if r not in new.rdatasets or (r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): + ret += "- %s\n" % str(r).replace('\n','\n+ ') + for r in new.rdatasets: + if r not in old.rdatasets or (r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): + ret += "+ %s\n" % str(r).replace('\n','\n+ ') + return ret + +def format_changes_html(oldf, newf, changes, ignore_ttl=False): + """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str + Given 2 filenames and a list of changes from diff_zones, produce nice html + output. If ignore_ttl is True, TTL-only changes are not displayed""" + + ret = ''' + + + + + + + + \n''' % (oldf, newf) + + for name, old, new in changes: + ret += ' \n \n' % name + if not old: + for r in new.rdatasets: + ret += ' \n \n' % str(r).replace('\n','
') + elif not new: + for r in old.rdatasets: + ret += ' \n \n' % str(r).replace('\n','
') + else: + ret += ' \n' + ret += ' \n' + ret += ' \n' + return ret + ' \n
 %s%s
%s %s%s ' + for r in old.rdatasets: + if r not in new.rdatasets or (r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): + ret += str(r).replace('\n','
') + ret += '
' + for r in new.rdatasets: + if r not in old.rdatasets or (r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl): + ret += str(r).replace('\n','
') + ret += '
' + +# Make this module usable as a script too. +if __name__ == '__main__': + import optparse + import subprocess + import sys + import traceback + + usage = """%prog zonefile1 zonefile2 - Show differences between zones in a diff-like format +%prog [--git|--bzr|--rcs] zonefile rev1 [rev2] - Show differences between two revisions of a zonefile + +The differences shown will be logical differences, not textual differences. +""" + p = optparse.OptionParser(usage=usage) + p.add_option('-s', '--ignore-soa', action="store_true", default=False, dest="ignore_soa", + help="Ignore SOA-only changes to records") + p.add_option('-t', '--ignore-ttl', action="store_true", default=False, dest="ignore_ttl", + help="Ignore TTL-only changes to Rdata") + p.add_option('-T', '--traceback', action="store_true", default=False, dest="tracebacks", + help="Show python tracebacks when errors occur") + p.add_option('-H', '--html', action="store_true", default=False, dest="html", + help="Print HTML output") + p.add_option('-g', '--git', action="store_true", default=False, dest="use_git", + help="Use git revisions instead of real files") + p.add_option('-b', '--bzr', action="store_true", default=False, dest="use_bzr", + help="Use bzr revisions instead of real files") + p.add_option('-r', '--rcs', action="store_true", default=False, dest="use_rcs", + help="Use rcs revisions instead of real files") + opts, args = p.parse_args() + opts.use_vc = opts.use_git or opts.use_bzr or opts.use_rcs + + def _open(what, err): + if isinstance(what, basestring): + # Open as normal file + try: + return open(what, 'rb') + except: + sys.stderr.write(err + "\n") + if opts.tracebacks: + traceback.print_exc() + else: + # Must be a list, open subprocess + try: + proc = subprocess.Popen(what, stdout=subprocess.PIPE) + proc.wait() + if proc.returncode == 0: + return proc.stdout + sys.stderr.write(err + "\n") + except: + sys.stderr.write(err + "\n") + if opts.tracebacks: + traceback.print_exc() + + if not opts.use_vc and len(args) != 2: + p.print_help() + sys.exit(64) + if opts.use_vc and len(args) not in (2,3): + p.print_help() + sys.exit(64) + + # Open file desriptors + if not opts.use_vc: + oldn, newn = args + else: + if len(args) == 3: + filename, oldr, newr = args + oldn = "%s:%s" % (oldr, filename) + newn = "%s:%s" % (newr, filename) + else: + filename, oldr = args + newr = None + oldn = "%s:%s" % (oldr, filename) + newn = filename + + + old, new = None, None + oldz, newz = None, None + if opts.use_bzr: + old = _open(["bzr", "cat", "-r" + oldr, filename], + "Unable to retrieve revision %s of %s" % (oldr, filename)) + if newr != None: + new = _open(["bzr", "cat", "-r" + newr, filename], + "Unable to retrieve revision %s of %s" % (newr, filename)) + elif opts.use_git: + old = _open(["git", "show", oldn], + "Unable to retrieve revision %s of %s" % (oldr, filename)) + if newr != None: + new = _open(["git", "show", newn], + "Unable to retrieve revision %s of %s" % (newr, filename)) + elif opts.use_rcs: + old = _open(["co", "-q", "-p", "-r" + oldr, filename], + "Unable to retrieve revision %s of %s" % (oldr, filename)) + if newr != None: + new = _open(["co", "-q", "-p", "-r" + newr, filename], + "Unable to retrieve revision %s of %s" % (newr, filename)) + if not opts.use_vc: + old = _open(oldn, "Unable to open %s" % oldn) + if not opts.use_vc or newr == None: + new = _open(newn, "Unable to open %s" % newn) + + if not old or not new: + sys.exit(65) + + # Parse the zones + try: + oldz = dns.zone.from_file(old, origin = '.', check_origin=False) + except dns.exception.DNSException: + sys.stderr.write("Incorrect zonefile: %s\n", old) + if opts.tracebacks: + traceback.print_exc() + try: + newz = dns.zone.from_file(new, origin = '.', check_origin=False) + except dns.exception.DNSException: + sys.stderr.write("Incorrect zonefile: %s\n" % new) + if opts.tracebacks: + traceback.print_exc() + if not oldz or not newz: + sys.exit(65) + + changes = diff_zones(oldz, newz, opts.ignore_ttl, opts.ignore_soa) + changes.sort() + + if not changes: + sys.exit(0) + if opts.html: + print format_changes_html(oldn, newn, changes, opts.ignore_ttl) + else: + print format_changes_plain(oldn, newn, changes, opts.ignore_ttl) + sys.exit(1) diff --git a/third_party/dnspython/setup.py b/third_party/dnspython/setup.py new file mode 100755 index 0000000000..33d7c20679 --- /dev/null +++ b/third_party/dnspython/setup.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import sys +from distutils.core import setup + +version = '1.10.0' + +kwargs = { + 'name' : 'dnspython', + 'version' : version, + 'description' : 'DNS toolkit', + 'long_description' : \ + """dnspython is a DNS toolkit for Python. It supports almost all +record types. It can be used for queries, zone transfers, and dynamic +updates. It supports TSIG authenticated messages and EDNS0. + +dnspython provides both high and low level access to DNS. The high +level classes perform queries for data of a given name, type, and +class, and return an answer set. The low level classes allow +direct manipulation of DNS zones, messages, names, and records.""", + 'author' : 'Bob Halley', + 'author_email' : 'halley@dnspython.org', + 'license' : 'BSD-like', + 'url' : 'http://www.dnspython.org', + 'packages' : ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY'], + 'download_url' : \ + 'http://www.dnspython.org/kits/%s/dnspython-%s.tar.gz' % (version, version), + 'classifiers' : [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "License :: Freeware", + "Operating System :: Microsoft :: Windows :: Windows 95/98/2000", + "Operating System :: POSIX", + "Programming Language :: Python", + "Topic :: Internet :: Name Service (DNS)", + "Topic :: Software Development :: Libraries :: Python Modules", + ], + } + +if sys.hexversion >= 0x02050000: + kwargs['requires'] = [] + kwargs['provides'] = ['dns'] + +setup(**kwargs) diff --git a/third_party/dnspython/tests/Makefile b/third_party/dnspython/tests/Makefile new file mode 100644 index 0000000000..6ab444f451 --- /dev/null +++ b/third_party/dnspython/tests/Makefile @@ -0,0 +1,26 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# $Id: Makefile,v 1.5 2004/03/19 00:17:27 halley Exp $ + +PYTHON=python + +check: test + +test: + @for i in *.py; do \ + echo "Running $$i:"; \ + ${PYTHON} $$i || exit 1; \ + done diff --git a/third_party/dnspython/tests/bugs.py b/third_party/dnspython/tests/bugs.py new file mode 100644 index 0000000000..c2fa6b6659 --- /dev/null +++ b/third_party/dnspython/tests/bugs.py @@ -0,0 +1,44 @@ +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.ttl + +class BugsTestCase(unittest.TestCase): + + def test_float_LOC(self): + rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.LOC, + "30 30 0.000 N 100 30 0.000 W 10.00m 20m 2000m 20m") + self.failUnless(rdata.float_latitude == 30.5) + self.failUnless(rdata.float_longitude == -100.5) + + def test_SOA_BIND8_TTL(self): + rdata1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, + "a b 100 1s 1m 1h 1d") + rdata2 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, + "a b 100 1 60 3600 86400") + self.failUnless(rdata1 == rdata2) + + def test_TTL_bounds_check(self): + def bad(): + ttl = dns.ttl.from_text("2147483648") + self.failUnlessRaises(dns.ttl.BadTTL, bad) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/dnssec.py b/third_party/dnspython/tests/dnssec.py new file mode 100644 index 0000000000..7b4546a086 --- /dev/null +++ b/third_party/dnspython/tests/dnssec.py @@ -0,0 +1,146 @@ +# Copyright (C) 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.dnssec +import dns.name +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.rrset + +abs_dnspython_org = dns.name.from_text('dnspython.org') + +abs_keys = { abs_dnspython_org : + dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'DNSKEY', + '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', + '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') + } + +rel_keys = { dns.name.empty : + dns.rrset.from_text('@', 3600, 'IN', 'DNSKEY', + '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', + '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') + } + +when = 1290250287 + +abs_soa = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'SOA', + 'howl.dnspython.org. hostmaster.dnspython.org. 2010020047 3600 1800 604800 3600') + +abs_other_soa = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'SOA', + 'foo.dnspython.org. hostmaster.dnspython.org. 2010020047 3600 1800 604800 3600') + +abs_soa_rrsig = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'RRSIG', + 'SOA 5 2 3600 20101127004331 20101119213831 61695 dnspython.org. sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=') + +rel_soa = dns.rrset.from_text('@', 3600, 'IN', 'SOA', + 'howl hostmaster 2010020047 3600 1800 604800 3600') + +rel_other_soa = dns.rrset.from_text('@', 3600, 'IN', 'SOA', + 'foo hostmaster 2010020047 3600 1800 604800 3600') + +rel_soa_rrsig = dns.rrset.from_text('@', 3600, 'IN', 'RRSIG', + 'SOA 5 2 3600 20101127004331 20101119213831 61695 @ sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=') + +sep_key = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, + '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=') + +good_ds = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, + '57349 5 2 53A79A3E7488AB44FFC56B2D1109F0699D1796DD977E72108B841F96 E47D7013') + +when2 = 1290425644 + +abs_example = dns.name.from_text('example') + +abs_dsa_keys = { abs_example : + dns.rrset.from_text('example.', 86400, 'IN', 'DNSKEY', + '257 3 3 CI3nCqyJsiCJHTjrNsJOT4RaszetzcJPYuoH3F9ZTVt3KJXncCVR3bwn 1w0iavKljb9hDlAYSfHbFCp4ic/rvg4p1L8vh5s8ToMjqDNl40A0hUGQ Ybx5hsECyK+qHoajilUX1phYSAD8d9WAGO3fDWzUPBuzR7o85NiZCDxz yXuNVfni0uhj9n1KYhEO5yAbbruDGN89wIZcxMKuQsdUY2GYD93ssnBv a55W6XRABYWayKZ90WkRVODLVYLSn53Pj/wwxGH+XdhIAZJXimrZL4yl My7rtBsLMqq8Ihs4Tows7LqYwY7cp6y/50tw6pj8tFqMYcPUjKZV36l1 M/2t5BVg3i7IK61Aidt6aoC3TDJtzAxg3ZxfjZWJfhHjMJqzQIfbW5b9 q1mjFsW5EUv39RaNnX+3JWPRLyDqD4pIwDyqfutMsdk/Py3paHn82FGp CaOg+nicqZ9TiMZURN/XXy5JoXUNQ3RNvbHCUiPUe18KUkY6mTfnyHld 1l9YCWmzXQVClkx/hOYxjJ4j8Ife58+Obu5X', + '256 3 3 CJE1yb9YRQiw5d2xZrMUMR+cGCTt1bp1KDCefmYKmS+Z1+q9f42ETVhx JRiQwXclYwmxborzIkSZegTNYIV6mrYwbNB27Q44c3UGcspb3PiOw5TC jNPRYEcdwGvDZ2wWy+vkSV/S9tHXY8O6ODiE6abZJDDg/RnITyi+eoDL R3KZ5n/V1f1T1b90rrV6EewhBGQJpQGDogaXb2oHww9Tm6NfXyo7SoMM pbwbzOckXv+GxRPJIQNSF4D4A9E8XCksuzVVdE/0lr37+uoiAiPia38U 5W2QWe/FJAEPLjIp2eTzf0TrADc1pKP1wrA2ASpdzpm/aX3IB5RPp8Ew S9U72eBFZJAUwg635HxJVxH1maG6atzorR566E+e0OZSaxXS9o1o6QqN 3oPlYLGPORDiExilKfez3C/x/yioOupW9K5eKF0gmtaqrHX0oq9s67f/ RIM2xVaKHgG9Vf2cgJIZkhv7sntujr+E4htnRmy9P9BxyFxsItYxPI6Z bzygHAZpGhlI/7ltEGlIwKxyTK3ZKBm67q7B') + } + +abs_dsa_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', + 'ns1.example. hostmaster.example. 2 10800 3600 604800 86400') + +abs_other_dsa_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', + 'ns1.example. hostmaster.example. 2 10800 3600 604800 86401') + +abs_dsa_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', + 'SOA 3 1 86400 20101129143231 20101122112731 42088 example. CGul9SuBofsktunV8cJs4eRs6u+3NCS3yaPKvBbD+pB2C76OUXDZq9U=') + +example_sep_key = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, + '257 3 3 CI3nCqyJsiCJHTjrNsJOT4RaszetzcJPYuoH3F9ZTVt3KJXncCVR3bwn 1w0iavKljb9hDlAYSfHbFCp4ic/rvg4p1L8vh5s8ToMjqDNl40A0hUGQ Ybx5hsECyK+qHoajilUX1phYSAD8d9WAGO3fDWzUPBuzR7o85NiZCDxz yXuNVfni0uhj9n1KYhEO5yAbbruDGN89wIZcxMKuQsdUY2GYD93ssnBv a55W6XRABYWayKZ90WkRVODLVYLSn53Pj/wwxGH+XdhIAZJXimrZL4yl My7rtBsLMqq8Ihs4Tows7LqYwY7cp6y/50tw6pj8tFqMYcPUjKZV36l1 M/2t5BVg3i7IK61Aidt6aoC3TDJtzAxg3ZxfjZWJfhHjMJqzQIfbW5b9 q1mjFsW5EUv39RaNnX+3JWPRLyDqD4pIwDyqfutMsdk/Py3paHn82FGp CaOg+nicqZ9TiMZURN/XXy5JoXUNQ3RNvbHCUiPUe18KUkY6mTfnyHld 1l9YCWmzXQVClkx/hOYxjJ4j8Ife58+Obu5X') + +example_ds_sha1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, + '18673 3 1 71b71d4f3e11bbd71b4eff12cde69f7f9215bbe7') + +example_ds_sha256 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, + '18673 3 2 eb8344cbbf07c9d3d3d6c81d10c76653e28d8611a65e639ef8f716e4e4e5d913') + +class DNSSECValidatorTestCase(unittest.TestCase): + + def testAbsoluteRSAGood(self): + dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) + + def testAbsoluteRSABad(self): + def bad(): + dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, + when) + self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) + + def testRelativeRSAGood(self): + dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, + abs_dnspython_org, when) + + def testRelativeRSABad(self): + def bad(): + dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, + abs_dnspython_org, when) + self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) + + def testMakeSHA256DS(self): + ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256') + self.failUnless(ds == good_ds) + + def testAbsoluteDSAGood(self): + dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, + when2) + + def testAbsoluteDSABad(self): + def bad(): + dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, + abs_dsa_keys, None, when2) + self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) + + def testMakeExampleSHA1DS(self): + ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA1') + self.failUnless(ds == example_ds_sha1) + + def testMakeExampleSHA256DS(self): + ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA256') + self.failUnless(ds == example_ds_sha256) + +if __name__ == '__main__': + import_ok = False + try: + import Crypto.Util.number + import_ok = True + except: + pass + if import_ok: + unittest.main() + else: + print 'skipping DNSSEC tests because pycrypto is not installed' diff --git a/third_party/dnspython/tests/example b/third_party/dnspython/tests/example new file mode 100644 index 0000000000..2f753a2f75 --- /dev/null +++ b/third_party/dnspython/tests/example @@ -0,0 +1,226 @@ +; Copyright (C) 2000, 2001 Internet Software Consortium. +; +; Permission to use, copy, modify, and distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM +; DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +; INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +; FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +; WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +; $Id: example,v 1.13 2004/03/19 00:06:37 halley Exp $ + +$ORIGIN . +$TTL 300 ; 5 minutes +example IN SOA ns1.example. hostmaster.example. ( + 1 ; serial + 2000 ; refresh (2000 seconds) + 2000 ; retry (2000 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) +example. NS ns1.example. +ns1.example. A 10.53.0.1 +example. NS ns2.example. +ns2.example. A 10.53.0.2 + +$ORIGIN example. +* MX 10 mail +a TXT "foo foo foo" + PTR foo.net. +;; The next line not starting with ';;' is leading whitespace followed by +;; EOL. We want to treat that as if EOL had appeared alone. + +;; The next line not starting with ';;' is leading whitespace followed by +;; a comment followed by EOL. We want to treat that as if EOL had appeared +;; alone. + ; foo +$TTL 3600 ; 1 hour +a01 A 0.0.0.0 +a02 A 255.255.255.255 +;; +;; XXXRTH dnspython doesn't currently implement A6, and since +;; A6 records are effectively dead, it may never do so. +;; +;;a601 A6 0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +;; A6 64 ::ffff:ffff:ffff:ffff foo. +;; A6 127 ::1 foo. +;; A6 128 . +aaaa01 AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +aaaa02 AAAA ::1 +afsdb01 AFSDB 0 hostname +afsdb02 AFSDB 65535 . +$TTL 300 ; 5 minutes +b CNAME foo.net. +c A 73.80.65.49 +$TTL 3600 ; 1 hour +cert01 CERT 65534 65535 PRIVATEOID ( + MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi + WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl + d80jEeC8aTrO+KKmCaY= ) +cname01 CNAME cname-target. +cname02 CNAME cname-target +cname03 CNAME . +$TTL 300 ; 5 minutes +d A 73.80.65.49 +$TTL 3600 ; 1 hour +dhcid01 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69l + OjxfNuVAA2kjEA= ) +dhcid02 DHCID ( AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdW + L3b/NaiUDlW2No= ) +dhcid03 DHCID ( AAABxLmlskllE0MVjd57zHcWmEH3pCQ6V + ytcKD//7es/deY= ) +dname01 DNAME dname-target. +dname02 DNAME dname-target +dname03 DNAME . +$TTL 300 ; 5 minutes +e MX 10 mail + TXT "one" + TXT "three" + TXT "two" + A 73.80.65.49 + A 73.80.65.50 + A 73.80.65.52 + A 73.80.65.51 +f A 73.80.65.52 +$TTL 3600 ; 1 hour +gpos01 GPOS "-22.6882" "116.8652" "250.0" +;; +;; XXXRTH I have commented out the following line because I don't think +;; it is a valid GPOS record. +;; +;;gpos02 GPOS "" "" "" +hinfo01 HINFO "Generic PC clone" "NetBSD-1.4" +hinfo02 HINFO "PC" "NetBSD" +isdn01 ISDN "isdn-address" +isdn02 ISDN "isdn-address" "subaddress" +isdn03 ISDN "isdn-address" +isdn04 ISDN "isdn-address" "subaddress" +;; dnspython no longer supports old DNSSEC +;;key01 KEY 512 255 1 ( +;; AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR +;; yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 +;; GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o +;; jqf0BaqHT+8= ) +;;key02 KEY HOST|FLAG4 DNSSEC RSAMD5 ( +;; AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR +;; yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 +;; GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o +;; jqf0BaqHT+8= ) +kx01 KX 10 kdc +kx02 KX 10 . +loc01 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20m 2000m 20m +loc02 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20m 2000m 20m +loc03 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m +loc04 LOC 60 9 1.5 N 24 39 0.000 E 10.00m 20m 2000m 20m +loc05 LOC 60 9 1.51 N 24 39 0.000 E 10.00m 20m 2000m 20m +;; +;; XXXRTH These are all obsolete and unused. dnspython doesn't implement +;; them +;;mb01 MG madname +;;mb02 MG . +;;mg01 MG mgmname +;;mg02 MG . +;;minfo01 MINFO rmailbx emailbx +;;minfo02 MINFO . . +;;mr01 MR mrname +;;mr02 MR . +mx01 MX 10 mail +mx02 MX 10 . +naptr01 NAPTR 0 0 "" "" "" . +naptr02 NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. +nsap-ptr01 NSAP-PTR foo. + NSAP-PTR . +nsap01 NSAP 0x47000580005a0000000001e133ffffff00016100 +nsap02 NSAP 0x47.000580005a0000000001e133ffffff000161.00 +;;nxt01 NXT a.secure ( NS SOA MX SIG KEY LOC NXT ) +;;nxt02 NXT . ( NSAP-PTR NXT ) +;;nxt03 NXT . ( A ) +;;nxt04 NXT . ( 127 ) +ptr01 PTR example. +px01 PX 65535 foo. bar. +px02 PX 65535 . . +rp01 RP mbox-dname txt-dname +rp02 RP . . +rt01 RT 0 intermediate-host +rt02 RT 65535 . +$TTL 300 ; 5 minutes +s NS ns.s +$ORIGIN s.example. +ns A 73.80.65.49 +$ORIGIN example. +$TTL 3600 ; 1 hour +;;sig01 SIG NXT 1 3 3600 ( +;; 20200101000000 20030101000000 2143 foo +;; MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi +;; WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl +;; d80jEeC8aTrO+KKmCaY= ) +srv01 SRV 0 0 0 . +srv02 SRV 65535 65535 65535 old-slow-box.example.com. +$TTL 301 ; 5 minutes 1 second +t A 73.80.65.49 +$TTL 3600 ; 1 hour +txt01 TXT "foo" +txt02 TXT "foo" "bar" +txt03 TXT "foo" +txt04 TXT "foo" "bar" +txt05 TXT "foo bar" +txt06 TXT "foo bar" +txt07 TXT "foo bar" +txt08 TXT "foo\010bar" +txt09 TXT "foo\010bar" +txt10 TXT "foo bar" +txt11 TXT "\"foo\"" +txt12 TXT "\"foo\"" +txt13 TXT foo +$TTL 300 ; 5 minutes +u TXT "txt-not-in-nxt" +$ORIGIN u.example. +a A 73.80.65.49 +b A 73.80.65.49 +$ORIGIN example. +$TTL 3600 ; 1 hour +wks01 WKS 10.0.0.1 6 ( 0 1 2 21 23 ) +wks02 WKS 10.0.0.1 17 ( 0 1 2 53 ) +wks03 WKS 10.0.0.2 6 ( 65535 ) +x2501 X25 "123456789" +dlv01 DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 +ds01 DS 12345 3 1 123456789abcdef67890123456789abcdef67890 +apl01 APL 1:192.168.32.0/21 !1:192.168.38.0/28 +apl02 APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 +unknown2 TYPE999 \# 8 0a0000010a000001 +rrsig01 RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/ vILz45IkskceFGgiWCn/GxHhai6V AuHAoNUz4YoU1tVfSCSqQYn6//11 U6Nld80jEeC8aTrO+KKmCaY= +nsec01 NSEC a.secure. A MX RRSIG NSEC TYPE1234 +nsec02 NSEC . NSAP-PTR NSEC +nsec03 NSEC . NSEC TYPE65535 +dnskey01 DNSKEY 512 255 1 ( + AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR + yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 + GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o + jqf0BaqHT+8= ) +dnskey02 DNSKEY 257 3 RSAMD5 ( + AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR + yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 + GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o + jqf0BaqHT+8= ) +; +; test known type using unknown RR syntax +; +unknown3 A \# 4 7f000002 +sshfp1 SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab +spf SPF "v=spf1 mx -all" +ipseckey01 IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== +ipseckey02 IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== +ipseckey03 IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== +ipseckey04 IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== +ipseckey05 IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== +nsec301 NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG +nsec302 NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG +nsec3param01 NSEC3PARAM 1 1 12 aabbccdd +nsec3param02 NSEC3PARAM 1 1 12 - +hip01 HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2 diff --git a/third_party/dnspython/tests/example1.good b/third_party/dnspython/tests/example1.good new file mode 100644 index 0000000000..0834d17150 --- /dev/null +++ b/third_party/dnspython/tests/example1.good @@ -0,0 +1,114 @@ +@ 300 IN SOA ns1 hostmaster 1 2000 2000 1814400 3600 +@ 300 IN NS ns1 +@ 300 IN NS ns2 +* 300 IN MX 10 mail +a 300 IN TXT "foo foo foo" +a 300 IN PTR foo.net. +a01 3600 IN A 0.0.0.0 +a02 3600 IN A 255.255.255.255 +aaaa01 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +aaaa02 3600 IN AAAA ::1 +afsdb01 3600 IN AFSDB 0 hostname +afsdb02 3600 IN AFSDB 65535 . +apl01 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 +apl02 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 +b 300 IN CNAME foo.net. +c 300 IN A 73.80.65.49 +cert01 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +cname01 3600 IN CNAME cname-target. +cname02 3600 IN CNAME cname-target +cname03 3600 IN CNAME . +d 300 IN A 73.80.65.49 +dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= +dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= +dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= +dlv01 3600 IN DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 +dname01 3600 IN DNAME dname-target. +dname02 3600 IN DNAME dname-target +dname03 3600 IN DNAME . +dnskey01 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +dnskey02 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +ds01 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 +e 300 IN MX 10 mail +e 300 IN TXT "one" +e 300 IN TXT "three" +e 300 IN TXT "two" +e 300 IN A 73.80.65.49 +e 300 IN A 73.80.65.50 +e 300 IN A 73.80.65.52 +e 300 IN A 73.80.65.51 +f 300 IN A 73.80.65.52 +gpos01 3600 IN GPOS -22.6882 116.8652 250.0 +hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" +hinfo02 3600 IN HINFO "PC" "NetBSD" +hip01 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2 +ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +isdn01 3600 IN ISDN "isdn-address" +isdn02 3600 IN ISDN "isdn-address" "subaddress" +isdn03 3600 IN ISDN "isdn-address" +isdn04 3600 IN ISDN "isdn-address" "subaddress" +kx01 3600 IN KX 10 kdc +kx02 3600 IN KX 10 . +loc01 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc02 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc03 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc04 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc05 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +mx01 3600 IN MX 10 mail +mx02 3600 IN MX 10 . +naptr01 3600 IN NAPTR 0 0 "" "" "" . +naptr02 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. +ns1 300 IN A 10.53.0.1 +ns2 300 IN A 10.53.0.2 +nsap-ptr01 3600 IN NSAP-PTR foo. +nsap-ptr01 3600 IN NSAP-PTR . +nsap01 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsap02 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsec01 3600 IN NSEC a.secure. A MX RRSIG NSEC TYPE1234 +nsec02 3600 IN NSEC . NSAP-PTR NSEC +nsec03 3600 IN NSEC . NSEC TYPE65535 +nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec3param01 3600 IN NSEC3PARAM 1 1 12 aabbccdd +nsec3param02 3600 IN NSEC3PARAM 1 1 12 - +ptr01 3600 IN PTR @ +px01 3600 IN PX 65535 foo. bar. +px02 3600 IN PX 65535 . . +rp01 3600 IN RP mbox-dname txt-dname +rp02 3600 IN RP . . +rrsig01 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +rt01 3600 IN RT 0 intermediate-host +rt02 3600 IN RT 65535 . +s 300 IN NS ns.s +ns.s 300 IN A 73.80.65.49 +spf 3600 IN SPF "v=spf1 mx -all" +srv01 3600 IN SRV 0 0 0 . +srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. +sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab +t 301 IN A 73.80.65.49 +txt01 3600 IN TXT "foo" +txt02 3600 IN TXT "foo" "bar" +txt03 3600 IN TXT "foo" +txt04 3600 IN TXT "foo" "bar" +txt05 3600 IN TXT "foo bar" +txt06 3600 IN TXT "foo bar" +txt07 3600 IN TXT "foo bar" +txt08 3600 IN TXT "foo\010bar" +txt09 3600 IN TXT "foo\010bar" +txt10 3600 IN TXT "foo bar" +txt11 3600 IN TXT "\"foo\"" +txt12 3600 IN TXT "\"foo\"" +txt13 3600 IN TXT "foo" +u 300 IN TXT "txt-not-in-nxt" +a.u 300 IN A 73.80.65.49 +b.u 300 IN A 73.80.65.49 +unknown2 3600 IN TYPE999 \# 8 0a0000010a000001 +unknown3 3600 IN A 127.0.0.2 +wks01 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 +wks02 3600 IN WKS 10.0.0.1 17 0 1 2 53 +wks03 3600 IN WKS 10.0.0.2 6 65535 +x2501 3600 IN X25 "123456789" diff --git a/third_party/dnspython/tests/example2.good b/third_party/dnspython/tests/example2.good new file mode 100644 index 0000000000..de4bcd59fd --- /dev/null +++ b/third_party/dnspython/tests/example2.good @@ -0,0 +1,114 @@ +example. 300 IN SOA ns1.example. hostmaster.example. 1 2000 2000 1814400 3600 +example. 300 IN NS ns1.example. +example. 300 IN NS ns2.example. +*.example. 300 IN MX 10 mail.example. +a.example. 300 IN TXT "foo foo foo" +a.example. 300 IN PTR foo.net. +a01.example. 3600 IN A 0.0.0.0 +a02.example. 3600 IN A 255.255.255.255 +aaaa01.example. 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +aaaa02.example. 3600 IN AAAA ::1 +afsdb01.example. 3600 IN AFSDB 0 hostname.example. +afsdb02.example. 3600 IN AFSDB 65535 . +apl01.example. 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 +apl02.example. 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 +b.example. 300 IN CNAME foo.net. +c.example. 300 IN A 73.80.65.49 +cert01.example. 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +cname01.example. 3600 IN CNAME cname-target. +cname02.example. 3600 IN CNAME cname-target.example. +cname03.example. 3600 IN CNAME . +d.example. 300 IN A 73.80.65.49 +dhcid01.example. 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= +dhcid02.example. 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= +dhcid03.example. 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= +dlv01.example. 3600 IN DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 +dname01.example. 3600 IN DNAME dname-target. +dname02.example. 3600 IN DNAME dname-target.example. +dname03.example. 3600 IN DNAME . +dnskey01.example. 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +dnskey02.example. 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +ds01.example. 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 +e.example. 300 IN MX 10 mail.example. +e.example. 300 IN TXT "one" +e.example. 300 IN TXT "three" +e.example. 300 IN TXT "two" +e.example. 300 IN A 73.80.65.49 +e.example. 300 IN A 73.80.65.50 +e.example. 300 IN A 73.80.65.52 +e.example. 300 IN A 73.80.65.51 +f.example. 300 IN A 73.80.65.52 +gpos01.example. 3600 IN GPOS -22.6882 116.8652 250.0 +hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" +hinfo02.example. 3600 IN HINFO "PC" "NetBSD" +hip01.example. 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example. +ipseckey01.example. 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey02.example. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey03.example. 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey04.example. 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey05.example. 3600 IN IPSECKEY 10 3 2 mygateway2.example. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +isdn01.example. 3600 IN ISDN "isdn-address" +isdn02.example. 3600 IN ISDN "isdn-address" "subaddress" +isdn03.example. 3600 IN ISDN "isdn-address" +isdn04.example. 3600 IN ISDN "isdn-address" "subaddress" +kx01.example. 3600 IN KX 10 kdc.example. +kx02.example. 3600 IN KX 10 . +loc01.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc02.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc03.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc04.example. 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc05.example. 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +mx01.example. 3600 IN MX 10 mail.example. +mx02.example. 3600 IN MX 10 . +naptr01.example. 3600 IN NAPTR 0 0 "" "" "" . +naptr02.example. 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. +ns1.example. 300 IN A 10.53.0.1 +ns2.example. 300 IN A 10.53.0.2 +nsap-ptr01.example. 3600 IN NSAP-PTR foo. +nsap-ptr01.example. 3600 IN NSAP-PTR . +nsap01.example. 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsap02.example. 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsec01.example. 3600 IN NSEC a.secure. A MX RRSIG NSEC TYPE1234 +nsec02.example. 3600 IN NSEC . NSAP-PTR NSEC +nsec03.example. 3600 IN NSEC . NSEC TYPE65535 +nsec301.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec302.example. 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec3param01.example. 3600 IN NSEC3PARAM 1 1 12 aabbccdd +nsec3param02.example. 3600 IN NSEC3PARAM 1 1 12 - +ptr01.example. 3600 IN PTR example. +px01.example. 3600 IN PX 65535 foo. bar. +px02.example. 3600 IN PX 65535 . . +rp01.example. 3600 IN RP mbox-dname.example. txt-dname.example. +rp02.example. 3600 IN RP . . +rrsig01.example. 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo.example. MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +rt01.example. 3600 IN RT 0 intermediate-host.example. +rt02.example. 3600 IN RT 65535 . +s.example. 300 IN NS ns.s.example. +ns.s.example. 300 IN A 73.80.65.49 +spf.example. 3600 IN SPF "v=spf1 mx -all" +srv01.example. 3600 IN SRV 0 0 0 . +srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. +sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab +t.example. 301 IN A 73.80.65.49 +txt01.example. 3600 IN TXT "foo" +txt02.example. 3600 IN TXT "foo" "bar" +txt03.example. 3600 IN TXT "foo" +txt04.example. 3600 IN TXT "foo" "bar" +txt05.example. 3600 IN TXT "foo bar" +txt06.example. 3600 IN TXT "foo bar" +txt07.example. 3600 IN TXT "foo bar" +txt08.example. 3600 IN TXT "foo\010bar" +txt09.example. 3600 IN TXT "foo\010bar" +txt10.example. 3600 IN TXT "foo bar" +txt11.example. 3600 IN TXT "\"foo\"" +txt12.example. 3600 IN TXT "\"foo\"" +txt13.example. 3600 IN TXT "foo" +u.example. 300 IN TXT "txt-not-in-nxt" +a.u.example. 300 IN A 73.80.65.49 +b.u.example. 300 IN A 73.80.65.49 +unknown2.example. 3600 IN TYPE999 \# 8 0a0000010a000001 +unknown3.example. 3600 IN A 127.0.0.2 +wks01.example. 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 +wks02.example. 3600 IN WKS 10.0.0.1 17 0 1 2 53 +wks03.example. 3600 IN WKS 10.0.0.2 6 65535 +x2501.example. 3600 IN X25 "123456789" diff --git a/third_party/dnspython/tests/flags.py b/third_party/dnspython/tests/flags.py new file mode 100644 index 0000000000..b3cf6716bc --- /dev/null +++ b/third_party/dnspython/tests/flags.py @@ -0,0 +1,59 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.flags +import dns.rcode +import dns.opcode + +class FlagsTestCase(unittest.TestCase): + + def test_rcode1(self): + self.failUnless(dns.rcode.from_text('FORMERR') == dns.rcode.FORMERR) + + def test_rcode2(self): + self.failUnless(dns.rcode.to_text(dns.rcode.FORMERR) == "FORMERR") + + def test_rcode3(self): + self.failUnless(dns.rcode.to_flags(dns.rcode.FORMERR) == (1, 0)) + + def test_rcode4(self): + self.failUnless(dns.rcode.to_flags(dns.rcode.BADVERS) == \ + (0, 0x01000000)) + + def test_rcode6(self): + self.failUnless(dns.rcode.from_flags(0, 0x01000000) == \ + dns.rcode.BADVERS) + + def test_rcode6(self): + self.failUnless(dns.rcode.from_flags(5, 0) == dns.rcode.REFUSED) + + def test_rcode7(self): + def bad(): + dns.rcode.to_flags(4096) + self.failUnlessRaises(ValueError, bad) + + def test_flags1(self): + self.failUnless(dns.flags.from_text("RA RD AA QR") == \ + dns.flags.QR|dns.flags.AA|dns.flags.RD|dns.flags.RA) + + def test_flags2(self): + flags = dns.flags.QR|dns.flags.AA|dns.flags.RD|dns.flags.RA + self.failUnless(dns.flags.to_text(flags) == "QR AA RD RA") + + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/message.py b/third_party/dnspython/tests/message.py new file mode 100644 index 0000000000..931bb1977d --- /dev/null +++ b/third_party/dnspython/tests/message.py @@ -0,0 +1,179 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import os +import unittest + +import dns.exception +import dns.message + +query_text = """id 1234 +opcode QUERY +rcode NOERROR +flags RD +edns 0 +eflags DO +payload 4096 +;QUESTION +wwww.dnspython.org. IN A +;ANSWER +;AUTHORITY +;ADDITIONAL""" + +goodhex = '04d201000001000000000001047777777709646e73707974686f6e' \ + '036f726700000100010000291000000080000000' + +goodwire = goodhex.decode('hex_codec') + +answer_text = """id 1234 +opcode QUERY +rcode NOERROR +flags QR AA RD +;QUESTION +dnspython.org. IN SOA +;ANSWER +dnspython.org. 3600 IN SOA woof.dnspython.org. hostmaster.dnspython.org. 2003052700 3600 1800 604800 3600 +;AUTHORITY +dnspython.org. 3600 IN NS ns1.staff.nominum.org. +dnspython.org. 3600 IN NS ns2.staff.nominum.org. +dnspython.org. 3600 IN NS woof.play-bow.org. +;ADDITIONAL +woof.play-bow.org. 3600 IN A 204.152.186.150 +""" + +goodhex2 = '04d2 8500 0001 0001 0003 0001' \ + '09646e73707974686f6e036f726700 0006 0001' \ + 'c00c 0006 0001 00000e10 0028 ' \ + '04776f6f66c00c 0a686f73746d6173746572c00c' \ + '7764289c 00000e10 00000708 00093a80 00000e10' \ + 'c00c 0002 0001 00000e10 0014' \ + '036e7331057374616666076e6f6d696e756dc016' \ + 'c00c 0002 0001 00000e10 0006 036e7332c063' \ + 'c00c 0002 0001 00000e10 0010 04776f6f6608706c61792d626f77c016' \ + 'c091 0001 0001 00000e10 0004 cc98ba96' + + +goodwire2 = goodhex2.replace(' ', '').decode('hex_codec') + +query_text_2 = """id 1234 +opcode QUERY +rcode 4095 +flags RD +edns 0 +eflags DO +payload 4096 +;QUESTION +wwww.dnspython.org. IN A +;ANSWER +;AUTHORITY +;ADDITIONAL""" + +goodhex3 = '04d2010f0001000000000001047777777709646e73707974686f6e' \ + '036f726700000100010000291000ff0080000000' + +goodwire3 = goodhex3.decode('hex_codec') + +class MessageTestCase(unittest.TestCase): + + def test_comparison_eq1(self): + q1 = dns.message.from_text(query_text) + q2 = dns.message.from_text(query_text) + self.failUnless(q1 == q2) + + def test_comparison_ne1(self): + q1 = dns.message.from_text(query_text) + q2 = dns.message.from_text(query_text) + q2.id = 10 + self.failUnless(q1 != q2) + + def test_comparison_ne2(self): + q1 = dns.message.from_text(query_text) + q2 = dns.message.from_text(query_text) + q2.question = [] + self.failUnless(q1 != q2) + + def test_comparison_ne3(self): + q1 = dns.message.from_text(query_text) + self.failUnless(q1 != 1) + + def test_EDNS_to_wire1(self): + q = dns.message.from_text(query_text) + w = q.to_wire() + self.failUnless(w == goodwire) + + def test_EDNS_from_wire1(self): + m = dns.message.from_wire(goodwire) + self.failUnless(str(m) == query_text) + + def test_EDNS_to_wire2(self): + q = dns.message.from_text(query_text_2) + w = q.to_wire() + self.failUnless(w == goodwire3) + + def test_EDNS_from_wire2(self): + m = dns.message.from_wire(goodwire3) + self.failUnless(str(m) == query_text_2) + + def test_TooBig(self): + def bad(): + q = dns.message.from_text(query_text) + for i in xrange(0, 25): + rrset = dns.rrset.from_text('foo%d.' % i, 3600, + dns.rdataclass.IN, + dns.rdatatype.A, + '10.0.0.%d' % i) + q.additional.append(rrset) + w = q.to_wire(max_size=512) + self.failUnlessRaises(dns.exception.TooBig, bad) + + def test_answer1(self): + a = dns.message.from_text(answer_text) + wire = a.to_wire(want_shuffle=False) + self.failUnless(wire == goodwire2) + + def test_TrailingJunk(self): + def bad(): + badwire = goodwire + '\x00' + m = dns.message.from_wire(badwire) + self.failUnlessRaises(dns.message.TrailingJunk, bad) + + def test_ShortHeader(self): + def bad(): + badwire = '\x00' * 11 + m = dns.message.from_wire(badwire) + self.failUnlessRaises(dns.message.ShortHeader, bad) + + def test_RespondingToResponse(self): + def bad(): + q = dns.message.make_query('foo', 'A') + r1 = dns.message.make_response(q) + r2 = dns.message.make_response(r1) + self.failUnlessRaises(dns.exception.FormError, bad) + + def test_ExtendedRcodeSetting(self): + m = dns.message.make_query('foo', 'A') + m.set_rcode(4095) + self.failUnless(m.rcode() == 4095) + m.set_rcode(2) + self.failUnless(m.rcode() == 2) + + def test_EDNSVersionCoherence(self): + m = dns.message.make_query('foo', 'A') + m.use_edns(1) + self.failUnless((m.ednsflags >> 16) & 0xFF == 1) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/name.py b/third_party/dnspython/tests/name.py new file mode 100644 index 0000000000..e30e43d3a2 --- /dev/null +++ b/third_party/dnspython/tests/name.py @@ -0,0 +1,697 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import cStringIO +import socket + +import dns.name +import dns.reversename +import dns.e164 + +class NameTestCase(unittest.TestCase): + def setUp(self): + self.origin = dns.name.from_text('example.') + + def testFromTextRel1(self): + n = dns.name.from_text('foo.bar') + self.failUnless(n.labels == ('foo', 'bar', '')) + + def testFromTextRel2(self): + n = dns.name.from_text('foo.bar', origin=self.origin) + self.failUnless(n.labels == ('foo', 'bar', 'example', '')) + + def testFromTextRel3(self): + n = dns.name.from_text('foo.bar', origin=None) + self.failUnless(n.labels == ('foo', 'bar')) + + def testFromTextRel4(self): + n = dns.name.from_text('@', origin=None) + self.failUnless(n == dns.name.empty) + + def testFromTextRel5(self): + n = dns.name.from_text('@', origin=self.origin) + self.failUnless(n == self.origin) + + def testFromTextAbs1(self): + n = dns.name.from_text('foo.bar.') + self.failUnless(n.labels == ('foo', 'bar', '')) + + def testTortureFromText(self): + good = [ + r'.', + r'a', + r'a.', + r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + r'\000.\008.\010.\032.\046.\092.\099.\255', + r'\\', + r'\..\.', + r'\\.\\', + r'!"#%&/()=+-', + r'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255', + ] + bad = [ + r'..', + r'.a', + r'\\..', + '\\', # yes, we don't want the 'r' prefix! + r'\0', + r'\00', + r'\00Z', + r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + r'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + r'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255', + ] + for t in good: + try: + n = dns.name.from_text(t) + except: + self.fail("good test '%s' raised an exception" % t) + for t in bad: + caught = False + try: + n = dns.name.from_text(t) + except: + caught = True + if not caught: + self.fail("bad test '%s' did not raise an exception" % t) + + def testImmutable1(self): + def bad(): + self.origin.labels = () + self.failUnlessRaises(TypeError, bad) + + def testImmutable2(self): + def bad(): + self.origin.labels[0] = 'foo' + self.failUnlessRaises(TypeError, bad) + + def testAbs1(self): + self.failUnless(dns.name.root.is_absolute()) + + def testAbs2(self): + self.failUnless(not dns.name.empty.is_absolute()) + + def testAbs3(self): + self.failUnless(self.origin.is_absolute()) + + def testAbs3(self): + n = dns.name.from_text('foo', origin=None) + self.failUnless(not n.is_absolute()) + + def testWild1(self): + n = dns.name.from_text('*.foo', origin=None) + self.failUnless(n.is_wild()) + + def testWild2(self): + n = dns.name.from_text('*a.foo', origin=None) + self.failUnless(not n.is_wild()) + + def testWild3(self): + n = dns.name.from_text('a.*.foo', origin=None) + self.failUnless(not n.is_wild()) + + def testWild4(self): + self.failUnless(not dns.name.root.is_wild()) + + def testWild5(self): + self.failUnless(not dns.name.empty.is_wild()) + + def testHash1(self): + n1 = dns.name.from_text('fOo.COM') + n2 = dns.name.from_text('foo.com') + self.failUnless(hash(n1) == hash(n2)) + + def testCompare1(self): + n1 = dns.name.from_text('a') + n2 = dns.name.from_text('b') + self.failUnless(n1 < n2) + self.failUnless(n2 > n1) + + def testCompare2(self): + n1 = dns.name.from_text('') + n2 = dns.name.from_text('b') + self.failUnless(n1 < n2) + self.failUnless(n2 > n1) + + def testCompare3(self): + self.failUnless(dns.name.empty < dns.name.root) + self.failUnless(dns.name.root > dns.name.empty) + + def testCompare4(self): + self.failUnless(dns.name.root != 1) + + def testCompare5(self): + self.failUnless(dns.name.root < 1 or dns.name.root > 1) + + def testSubdomain1(self): + self.failUnless(not dns.name.empty.is_subdomain(dns.name.root)) + + def testSubdomain2(self): + self.failUnless(not dns.name.root.is_subdomain(dns.name.empty)) + + def testSubdomain3(self): + n = dns.name.from_text('foo', origin=self.origin) + self.failUnless(n.is_subdomain(self.origin)) + + def testSubdomain4(self): + n = dns.name.from_text('foo', origin=self.origin) + self.failUnless(n.is_subdomain(dns.name.root)) + + def testSubdomain5(self): + n = dns.name.from_text('foo', origin=self.origin) + self.failUnless(n.is_subdomain(n)) + + def testSuperdomain1(self): + self.failUnless(not dns.name.empty.is_superdomain(dns.name.root)) + + def testSuperdomain2(self): + self.failUnless(not dns.name.root.is_superdomain(dns.name.empty)) + + def testSuperdomain3(self): + n = dns.name.from_text('foo', origin=self.origin) + self.failUnless(self.origin.is_superdomain(n)) + + def testSuperdomain4(self): + n = dns.name.from_text('foo', origin=self.origin) + self.failUnless(dns.name.root.is_superdomain(n)) + + def testSuperdomain5(self): + n = dns.name.from_text('foo', origin=self.origin) + self.failUnless(n.is_superdomain(n)) + + def testCanonicalize1(self): + n = dns.name.from_text('FOO.bar', origin=self.origin) + c = n.canonicalize() + self.failUnless(c.labels == ('foo', 'bar', 'example', '')) + + def testToText1(self): + n = dns.name.from_text('FOO.bar', origin=self.origin) + t = n.to_text() + self.failUnless(t == 'FOO.bar.example.') + + def testToText2(self): + n = dns.name.from_text('FOO.bar', origin=self.origin) + t = n.to_text(True) + self.failUnless(t == 'FOO.bar.example') + + def testToText3(self): + n = dns.name.from_text('FOO.bar', origin=None) + t = n.to_text() + self.failUnless(t == 'FOO.bar') + + def testToText4(self): + t = dns.name.empty.to_text() + self.failUnless(t == '@') + + def testToText5(self): + t = dns.name.root.to_text() + self.failUnless(t == '.') + + def testToText6(self): + n = dns.name.from_text('FOO bar', origin=None) + t = n.to_text() + self.failUnless(t == r'FOO\032bar') + + def testToText7(self): + n = dns.name.from_text(r'FOO\.bar', origin=None) + t = n.to_text() + self.failUnless(t == r'FOO\.bar') + + def testToText8(self): + n = dns.name.from_text(r'\070OO\.bar', origin=None) + t = n.to_text() + self.failUnless(t == r'FOO\.bar') + + def testSlice1(self): + n = dns.name.from_text(r'a.b.c.', origin=None) + s = n[:] + self.failUnless(s == ('a', 'b', 'c', '')) + + def testSlice2(self): + n = dns.name.from_text(r'a.b.c.', origin=None) + s = n[:2] + self.failUnless(s == ('a', 'b')) + + def testSlice3(self): + n = dns.name.from_text(r'a.b.c.', origin=None) + s = n[2:] + self.failUnless(s == ('c', '')) + + def testEmptyLabel1(self): + def bad(): + n = dns.name.Name(['a', '', 'b']) + self.failUnlessRaises(dns.name.EmptyLabel, bad) + + def testEmptyLabel2(self): + def bad(): + n = dns.name.Name(['', 'b']) + self.failUnlessRaises(dns.name.EmptyLabel, bad) + + def testEmptyLabel3(self): + n = dns.name.Name(['b', '']) + self.failUnless(n) + + def testLongLabel(self): + n = dns.name.Name(['a' * 63]) + self.failUnless(n) + + def testLabelTooLong(self): + def bad(): + n = dns.name.Name(['a' * 64, 'b']) + self.failUnlessRaises(dns.name.LabelTooLong, bad) + + def testLongName(self): + n = dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 62]) + self.failUnless(n) + + def testNameTooLong(self): + def bad(): + n = dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 63]) + self.failUnlessRaises(dns.name.NameTooLong, bad) + + def testConcat1(self): + n1 = dns.name.Name(['a', 'b']) + n2 = dns.name.Name(['c', 'd']) + e = dns.name.Name(['a', 'b', 'c', 'd']) + r = n1 + n2 + self.failUnless(r == e) + + def testConcat2(self): + n1 = dns.name.Name(['a', 'b']) + n2 = dns.name.Name([]) + e = dns.name.Name(['a', 'b']) + r = n1 + n2 + self.failUnless(r == e) + + def testConcat2(self): + n1 = dns.name.Name([]) + n2 = dns.name.Name(['a', 'b']) + e = dns.name.Name(['a', 'b']) + r = n1 + n2 + self.failUnless(r == e) + + def testConcat3(self): + n1 = dns.name.Name(['a', 'b', '']) + n2 = dns.name.Name([]) + e = dns.name.Name(['a', 'b', '']) + r = n1 + n2 + self.failUnless(r == e) + + def testConcat4(self): + n1 = dns.name.Name(['a', 'b']) + n2 = dns.name.Name(['c', '']) + e = dns.name.Name(['a', 'b', 'c', '']) + r = n1 + n2 + self.failUnless(r == e) + + def testConcat5(self): + def bad(): + n1 = dns.name.Name(['a', 'b', '']) + n2 = dns.name.Name(['c']) + r = n1 + n2 + self.failUnlessRaises(dns.name.AbsoluteConcatenation, bad) + + def testBadEscape(self): + def bad(): + n = dns.name.from_text(r'a.b\0q1.c.') + print n + self.failUnlessRaises(dns.name.BadEscape, bad) + + def testDigestable1(self): + n = dns.name.from_text('FOO.bar') + d = n.to_digestable() + self.failUnless(d == '\x03foo\x03bar\x00') + + def testDigestable2(self): + n1 = dns.name.from_text('FOO.bar') + n2 = dns.name.from_text('foo.BAR.') + d1 = n1.to_digestable() + d2 = n2.to_digestable() + self.failUnless(d1 == d2) + + def testDigestable3(self): + d = dns.name.root.to_digestable() + self.failUnless(d == '\x00') + + def testDigestable4(self): + n = dns.name.from_text('FOO.bar', None) + d = n.to_digestable(dns.name.root) + self.failUnless(d == '\x03foo\x03bar\x00') + + def testBadDigestable(self): + def bad(): + n = dns.name.from_text('FOO.bar', None) + d = n.to_digestable() + self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) + + def testToWire1(self): + n = dns.name.from_text('FOO.bar') + f = cStringIO.StringIO() + compress = {} + n.to_wire(f, compress) + self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00') + + def testToWire2(self): + n = dns.name.from_text('FOO.bar') + f = cStringIO.StringIO() + compress = {} + n.to_wire(f, compress) + n.to_wire(f, compress) + self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00\xc0\x00') + + def testToWire3(self): + n1 = dns.name.from_text('FOO.bar') + n2 = dns.name.from_text('foo.bar') + f = cStringIO.StringIO() + compress = {} + n1.to_wire(f, compress) + n2.to_wire(f, compress) + self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00\xc0\x00') + + def testToWire4(self): + n1 = dns.name.from_text('FOO.bar') + n2 = dns.name.from_text('a.foo.bar') + f = cStringIO.StringIO() + compress = {} + n1.to_wire(f, compress) + n2.to_wire(f, compress) + self.failUnless(f.getvalue() == '\x03FOO\x03bar\x00\x01\x61\xc0\x00') + + def testToWire5(self): + n1 = dns.name.from_text('FOO.bar') + n2 = dns.name.from_text('a.foo.bar') + f = cStringIO.StringIO() + compress = {} + n1.to_wire(f, compress) + n2.to_wire(f, None) + self.failUnless(f.getvalue() == \ + '\x03FOO\x03bar\x00\x01\x61\x03foo\x03bar\x00') + + def testToWire6(self): + n = dns.name.from_text('FOO.bar') + v = n.to_wire() + self.failUnless(v == '\x03FOO\x03bar\x00') + + def testBadToWire(self): + def bad(): + n = dns.name.from_text('FOO.bar', None) + f = cStringIO.StringIO() + compress = {} + n.to_wire(f, compress) + self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) + + def testSplit1(self): + n = dns.name.from_text('foo.bar.') + (prefix, suffix) = n.split(2) + ep = dns.name.from_text('foo', None) + es = dns.name.from_text('bar.', None) + self.failUnless(prefix == ep and suffix == es) + + def testSplit2(self): + n = dns.name.from_text('foo.bar.') + (prefix, suffix) = n.split(1) + ep = dns.name.from_text('foo.bar', None) + es = dns.name.from_text('.', None) + self.failUnless(prefix == ep and suffix == es) + + def testSplit3(self): + n = dns.name.from_text('foo.bar.') + (prefix, suffix) = n.split(0) + ep = dns.name.from_text('foo.bar.', None) + es = dns.name.from_text('', None) + self.failUnless(prefix == ep and suffix == es) + + def testSplit4(self): + n = dns.name.from_text('foo.bar.') + (prefix, suffix) = n.split(3) + ep = dns.name.from_text('', None) + es = dns.name.from_text('foo.bar.', None) + self.failUnless(prefix == ep and suffix == es) + + def testBadSplit1(self): + def bad(): + n = dns.name.from_text('foo.bar.') + (prefix, suffix) = n.split(-1) + self.failUnlessRaises(ValueError, bad) + + def testBadSplit2(self): + def bad(): + n = dns.name.from_text('foo.bar.') + (prefix, suffix) = n.split(4) + self.failUnlessRaises(ValueError, bad) + + def testRelativize1(self): + n = dns.name.from_text('a.foo.bar.', None) + o = dns.name.from_text('bar.', None) + e = dns.name.from_text('a.foo', None) + self.failUnless(n.relativize(o) == e) + + def testRelativize2(self): + n = dns.name.from_text('a.foo.bar.', None) + o = n + e = dns.name.empty + self.failUnless(n.relativize(o) == e) + + def testRelativize3(self): + n = dns.name.from_text('a.foo.bar.', None) + o = dns.name.from_text('blaz.', None) + e = n + self.failUnless(n.relativize(o) == e) + + def testRelativize4(self): + n = dns.name.from_text('a.foo', None) + o = dns.name.root + e = n + self.failUnless(n.relativize(o) == e) + + def testDerelativize1(self): + n = dns.name.from_text('a.foo', None) + o = dns.name.from_text('bar.', None) + e = dns.name.from_text('a.foo.bar.', None) + self.failUnless(n.derelativize(o) == e) + + def testDerelativize2(self): + n = dns.name.empty + o = dns.name.from_text('a.foo.bar.', None) + e = o + self.failUnless(n.derelativize(o) == e) + + def testDerelativize3(self): + n = dns.name.from_text('a.foo.bar.', None) + o = dns.name.from_text('blaz.', None) + e = n + self.failUnless(n.derelativize(o) == e) + + def testChooseRelativity1(self): + n = dns.name.from_text('a.foo.bar.', None) + o = dns.name.from_text('bar.', None) + e = dns.name.from_text('a.foo', None) + self.failUnless(n.choose_relativity(o, True) == e) + + def testChooseRelativity2(self): + n = dns.name.from_text('a.foo.bar.', None) + o = dns.name.from_text('bar.', None) + e = n + self.failUnless(n.choose_relativity(o, False) == e) + + def testChooseRelativity3(self): + n = dns.name.from_text('a.foo', None) + o = dns.name.from_text('bar.', None) + e = dns.name.from_text('a.foo.bar.', None) + self.failUnless(n.choose_relativity(o, False) == e) + + def testChooseRelativity4(self): + n = dns.name.from_text('a.foo', None) + o = None + e = n + self.failUnless(n.choose_relativity(o, True) == e) + + def testChooseRelativity5(self): + n = dns.name.from_text('a.foo', None) + o = None + e = n + self.failUnless(n.choose_relativity(o, False) == e) + + def testChooseRelativity6(self): + n = dns.name.from_text('a.foo.', None) + o = None + e = n + self.failUnless(n.choose_relativity(o, True) == e) + + def testChooseRelativity7(self): + n = dns.name.from_text('a.foo.', None) + o = None + e = n + self.failUnless(n.choose_relativity(o, False) == e) + + def testFromWire1(self): + w = '\x03foo\x00\xc0\x00' + (n1, cused1) = dns.name.from_wire(w, 0) + (n2, cused2) = dns.name.from_wire(w, cused1) + en1 = dns.name.from_text('foo.') + en2 = en1 + ecused1 = 5 + ecused2 = 2 + self.failUnless(n1 == en1 and cused1 == ecused1 and \ + n2 == en2 and cused2 == ecused2) + + def testFromWire1(self): + w = '\x03foo\x00\x01a\xc0\x00\x01b\xc0\x05' + current = 0 + (n1, cused1) = dns.name.from_wire(w, current) + current += cused1 + (n2, cused2) = dns.name.from_wire(w, current) + current += cused2 + (n3, cused3) = dns.name.from_wire(w, current) + en1 = dns.name.from_text('foo.') + en2 = dns.name.from_text('a.foo.') + en3 = dns.name.from_text('b.a.foo.') + ecused1 = 5 + ecused2 = 4 + ecused3 = 4 + self.failUnless(n1 == en1 and cused1 == ecused1 and \ + n2 == en2 and cused2 == ecused2 and \ + n3 == en3 and cused3 == ecused3) + + def testBadFromWire1(self): + def bad(): + w = '\x03foo\xc0\x04' + (n, cused) = dns.name.from_wire(w, 0) + self.failUnlessRaises(dns.name.BadPointer, bad) + + def testBadFromWire2(self): + def bad(): + w = '\x03foo\xc0\x05' + (n, cused) = dns.name.from_wire(w, 0) + self.failUnlessRaises(dns.name.BadPointer, bad) + + def testBadFromWire3(self): + def bad(): + w = '\xbffoo' + (n, cused) = dns.name.from_wire(w, 0) + self.failUnlessRaises(dns.name.BadLabelType, bad) + + def testBadFromWire4(self): + def bad(): + w = '\x41foo' + (n, cused) = dns.name.from_wire(w, 0) + self.failUnlessRaises(dns.name.BadLabelType, bad) + + def testParent1(self): + n = dns.name.from_text('foo.bar.') + self.failUnless(n.parent() == dns.name.from_text('bar.')) + self.failUnless(n.parent().parent() == dns.name.root) + + def testParent2(self): + n = dns.name.from_text('foo.bar', None) + self.failUnless(n.parent() == dns.name.from_text('bar', None)) + self.failUnless(n.parent().parent() == dns.name.empty) + + def testParent3(self): + def bad(): + n = dns.name.root + n.parent() + self.failUnlessRaises(dns.name.NoParent, bad) + + def testParent4(self): + def bad(): + n = dns.name.empty + n.parent() + self.failUnlessRaises(dns.name.NoParent, bad) + + def testFromUnicode1(self): + n = dns.name.from_text(u'foo.bar') + self.failUnless(n.labels == ('foo', 'bar', '')) + + def testFromUnicode2(self): + n = dns.name.from_text(u'foo\u1234bar.bar') + self.failUnless(n.labels == ('xn--foobar-r5z', 'bar', '')) + + def testFromUnicodeAlternateDot1(self): + n = dns.name.from_text(u'foo\u3002bar') + self.failUnless(n.labels == ('foo', 'bar', '')) + + def testFromUnicodeAlternateDot2(self): + n = dns.name.from_text(u'foo\uff0ebar') + self.failUnless(n.labels == ('foo', 'bar', '')) + + def testFromUnicodeAlternateDot3(self): + n = dns.name.from_text(u'foo\uff61bar') + self.failUnless(n.labels == ('foo', 'bar', '')) + + def testToUnicode1(self): + n = dns.name.from_text(u'foo.bar') + s = n.to_unicode() + self.failUnless(s == u'foo.bar.') + + def testToUnicode2(self): + n = dns.name.from_text(u'foo\u1234bar.bar') + s = n.to_unicode() + self.failUnless(s == u'foo\u1234bar.bar.') + + def testToUnicode3(self): + n = dns.name.from_text('foo.bar') + s = n.to_unicode() + self.failUnless(s == u'foo.bar.') + + def testReverseIPv4(self): + e = dns.name.from_text('1.0.0.127.in-addr.arpa.') + n = dns.reversename.from_address('127.0.0.1') + self.failUnless(e == n) + + def testReverseIPv6(self): + e = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') + n = dns.reversename.from_address('::1') + self.failUnless(e == n) + + def testBadReverseIPv4(self): + def bad(): + n = dns.reversename.from_address('127.0.foo.1') + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def testBadReverseIPv6(self): + def bad(): + n = dns.reversename.from_address('::1::1') + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def testForwardIPv4(self): + n = dns.name.from_text('1.0.0.127.in-addr.arpa.') + e = '127.0.0.1' + text = dns.reversename.to_address(n) + self.failUnless(text == e) + + def testForwardIPv6(self): + n = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') + e = '::1' + text = dns.reversename.to_address(n) + self.failUnless(text == e) + + def testE164ToEnum(self): + text = '+1 650 555 1212' + e = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') + n = dns.e164.from_e164(text) + self.failUnless(n == e) + + def testEnumToE164(self): + n = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') + e = '+16505551212' + text = dns.e164.to_e164(n) + self.failUnless(text == e) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/namedict.py b/third_party/dnspython/tests/namedict.py new file mode 100644 index 0000000000..e256bfe679 --- /dev/null +++ b/third_party/dnspython/tests/namedict.py @@ -0,0 +1,102 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.name +import dns.namedict + +class NameTestCase(unittest.TestCase): + + def setUp(self): + self.ndict = dns.namedict.NameDict() + n1 = dns.name.from_text('foo.bar.') + n2 = dns.name.from_text('bar.') + self.ndict[n1] = 1 + self.ndict[n2] = 2 + self.rndict = dns.namedict.NameDict() + n1 = dns.name.from_text('foo.bar', None) + n2 = dns.name.from_text('bar', None) + self.rndict[n1] = 1 + self.rndict[n2] = 2 + + def testDepth(self): + self.failUnless(self.ndict.max_depth == 3) + + def testLookup1(self): + k = dns.name.from_text('foo.bar.') + self.failUnless(self.ndict[k] == 1) + + def testLookup2(self): + k = dns.name.from_text('foo.bar.') + self.failUnless(self.ndict.get_deepest_match(k)[1] == 1) + + def testLookup3(self): + k = dns.name.from_text('a.b.c.foo.bar.') + self.failUnless(self.ndict.get_deepest_match(k)[1] == 1) + + def testLookup4(self): + k = dns.name.from_text('a.b.c.bar.') + self.failUnless(self.ndict.get_deepest_match(k)[1] == 2) + + def testLookup5(self): + def bad(): + n = dns.name.from_text('a.b.c.') + (k, v) = self.ndict.get_deepest_match(n) + self.failUnlessRaises(KeyError, bad) + + def testLookup6(self): + def bad(): + (k, v) = self.ndict.get_deepest_match(dns.name.empty) + self.failUnlessRaises(KeyError, bad) + + def testLookup7(self): + self.ndict[dns.name.empty] = 100 + n = dns.name.from_text('a.b.c.') + (k, v) = self.ndict.get_deepest_match(n) + self.failUnless(v == 100) + + def testLookup8(self): + def bad(): + self.ndict['foo'] = 100 + self.failUnlessRaises(ValueError, bad) + + def testRelDepth(self): + self.failUnless(self.rndict.max_depth == 2) + + def testRelLookup1(self): + k = dns.name.from_text('foo.bar', None) + self.failUnless(self.rndict[k] == 1) + + def testRelLookup2(self): + k = dns.name.from_text('foo.bar', None) + self.failUnless(self.rndict.get_deepest_match(k)[1] == 1) + + def testRelLookup3(self): + k = dns.name.from_text('a.b.c.foo.bar', None) + self.failUnless(self.rndict.get_deepest_match(k)[1] == 1) + + def testRelLookup4(self): + k = dns.name.from_text('a.b.c.bar', None) + self.failUnless(self.rndict.get_deepest_match(k)[1] == 2) + + def testRelLookup7(self): + self.rndict[dns.name.empty] = 100 + n = dns.name.from_text('a.b.c', None) + (k, v) = self.rndict.get_deepest_match(n) + self.failUnless(v == 100) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/ntoaaton.py b/third_party/dnspython/tests/ntoaaton.py new file mode 100644 index 0000000000..9d8bedd702 --- /dev/null +++ b/third_party/dnspython/tests/ntoaaton.py @@ -0,0 +1,197 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.exception +import dns.ipv4 +import dns.ipv6 + +# for convenience +aton4 = dns.ipv4.inet_aton +ntoa4 = dns.ipv4.inet_ntoa +aton6 = dns.ipv6.inet_aton +ntoa6 = dns.ipv6.inet_ntoa + +v4_bad_addrs = ['256.1.1.1', '1.1.1', '1.1.1.1.1', '01.1.1.1', + '+1.1.1.1', '1.1.1.1+', '1..2.3.4', '.1.2.3.4', + '1.2.3.4.'] + +class NtoAAtoNTestCase(unittest.TestCase): + + def test_aton1(self): + a = aton6('::') + self.failUnless(a == '\x00' * 16) + + def test_aton2(self): + a = aton6('::1') + self.failUnless(a == '\x00' * 15 + '\x01') + + def test_aton3(self): + a = aton6('::10.0.0.1') + self.failUnless(a == '\x00' * 12 + '\x0a\x00\x00\x01') + + def test_aton4(self): + a = aton6('abcd::dcba') + self.failUnless(a == '\xab\xcd' + '\x00' * 12 + '\xdc\xba') + + def test_aton5(self): + a = aton6('1:2:3:4:5:6:7:8') + self.failUnless(a == \ + '00010002000300040005000600070008'.decode('hex_codec')) + + def test_bad_aton1(self): + def bad(): + a = aton6('abcd:dcba') + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def test_bad_aton2(self): + def bad(): + a = aton6('abcd::dcba::1') + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def test_bad_aton3(self): + def bad(): + a = aton6('1:2:3:4:5:6:7:8:9') + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def test_aton1(self): + a = aton6('::') + self.failUnless(a == '\x00' * 16) + + def test_aton2(self): + a = aton6('::1') + self.failUnless(a == '\x00' * 15 + '\x01') + + def test_aton3(self): + a = aton6('::10.0.0.1') + self.failUnless(a == '\x00' * 12 + '\x0a\x00\x00\x01') + + def test_aton4(self): + a = aton6('abcd::dcba') + self.failUnless(a == '\xab\xcd' + '\x00' * 12 + '\xdc\xba') + + def test_ntoa1(self): + b = '00010002000300040005000600070008'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '1:2:3:4:5:6:7:8') + + def test_ntoa2(self): + b = '\x00' * 16 + t = ntoa6(b) + self.failUnless(t == '::') + + def test_ntoa3(self): + b = '\x00' * 15 + '\x01' + t = ntoa6(b) + self.failUnless(t == '::1') + + def test_ntoa4(self): + b = '\x80' + '\x00' * 15 + t = ntoa6(b) + self.failUnless(t == '8000::') + + def test_ntoa5(self): + b = '\x01\xcd' + '\x00' * 12 + '\x03\xef' + t = ntoa6(b) + self.failUnless(t == '1cd::3ef') + + def test_ntoa6(self): + b = 'ffff00000000ffff000000000000ffff'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == 'ffff:0:0:ffff::ffff') + + def test_ntoa7(self): + b = '00000000ffff000000000000ffffffff'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '0:0:ffff::ffff:ffff') + + def test_ntoa8(self): + b = 'ffff0000ffff00000000ffff00000000'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == 'ffff:0:ffff::ffff:0:0') + + def test_ntoa9(self): + b = '0000000000000000000000000a000001'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '::10.0.0.1') + + def test_ntoa10(self): + b = '0000000000000000000000010a000001'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '::1:a00:1') + + def test_ntoa11(self): + b = '00000000000000000000ffff0a000001'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '::ffff:10.0.0.1') + + def test_ntoa12(self): + b = '000000000000000000000000ffffffff'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '::255.255.255.255') + + def test_ntoa13(self): + b = '00000000000000000000ffffffffffff'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '::ffff:255.255.255.255') + + def test_ntoa14(self): + b = '0000000000000000000000000001ffff'.decode('hex_codec') + t = ntoa6(b) + self.failUnless(t == '::0.1.255.255') + + def test_bad_ntoa1(self): + def bad(): + a = ntoa6('') + self.failUnlessRaises(ValueError, bad) + + def test_bad_ntoa2(self): + def bad(): + a = ntoa6('\x00' * 17) + self.failUnlessRaises(ValueError, bad) + + def test_good_v4_aton(self): + pairs = [('1.2.3.4', '\x01\x02\x03\x04'), + ('255.255.255.255', '\xff\xff\xff\xff'), + ('0.0.0.0', '\x00\x00\x00\x00')] + for (t, b) in pairs: + b1 = aton4(t) + t1 = ntoa4(b1) + self.failUnless(b1 == b) + self.failUnless(t1 == t) + + def test_bad_v4_aton(self): + def make_bad(a): + def bad(): + return aton4(a) + return bad + for addr in v4_bad_addrs: + self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) + + def test_bad_v6_aton(self): + addrs = ['+::0', '0::0::', '::0::', '1:2:3:4:5:6:7:8:9', + ':::::::'] + embedded = ['::' + x for x in v4_bad_addrs] + addrs.extend(embedded) + def make_bad(a): + def bad(): + x = aton6(a) + return bad + for addr in addrs: + self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/rdtypeandclass.py b/third_party/dnspython/tests/rdtypeandclass.py new file mode 100644 index 0000000000..f3c0628d36 --- /dev/null +++ b/third_party/dnspython/tests/rdtypeandclass.py @@ -0,0 +1,123 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.rdataclass +import dns.rdatatype + +class RdTypeAndClassTestCase(unittest.TestCase): + + # Classes + + def test_class_meta1(self): + self.failUnless(dns.rdataclass.is_metaclass(dns.rdataclass.ANY)) + + def test_class_meta2(self): + self.failUnless(not dns.rdataclass.is_metaclass(dns.rdataclass.IN)) + + def test_class_bytext1(self): + self.failUnless(dns.rdataclass.from_text('IN') == dns.rdataclass.IN) + + def test_class_bytext2(self): + self.failUnless(dns.rdataclass.from_text('CLASS1') == + dns.rdataclass.IN) + + def test_class_bytext_bounds1(self): + self.failUnless(dns.rdataclass.from_text('CLASS0') == 0) + self.failUnless(dns.rdataclass.from_text('CLASS65535') == 65535) + + def test_class_bytext_bounds2(self): + def bad(): + junk = dns.rdataclass.from_text('CLASS65536') + self.failUnlessRaises(ValueError, bad) + + def test_class_bytext_unknown(self): + def bad(): + junk = dns.rdataclass.from_text('XXX') + self.failUnlessRaises(dns.rdataclass.UnknownRdataclass, bad) + + def test_class_totext1(self): + self.failUnless(dns.rdataclass.to_text(dns.rdataclass.IN) == 'IN') + + def test_class_totext1(self): + self.failUnless(dns.rdataclass.to_text(999) == 'CLASS999') + + def test_class_totext_bounds1(self): + def bad(): + junk = dns.rdataclass.to_text(-1) + self.failUnlessRaises(ValueError, bad) + + def test_class_totext_bounds2(self): + def bad(): + junk = dns.rdataclass.to_text(65536) + self.failUnlessRaises(ValueError, bad) + + # Types + + def test_type_meta1(self): + self.failUnless(dns.rdatatype.is_metatype(dns.rdatatype.ANY)) + + def test_type_meta2(self): + self.failUnless(dns.rdatatype.is_metatype(dns.rdatatype.OPT)) + + def test_type_meta3(self): + self.failUnless(not dns.rdatatype.is_metatype(dns.rdatatype.A)) + + def test_type_singleton1(self): + self.failUnless(dns.rdatatype.is_singleton(dns.rdatatype.SOA)) + + def test_type_singleton2(self): + self.failUnless(not dns.rdatatype.is_singleton(dns.rdatatype.A)) + + def test_type_bytext1(self): + self.failUnless(dns.rdatatype.from_text('A') == dns.rdatatype.A) + + def test_type_bytext2(self): + self.failUnless(dns.rdatatype.from_text('TYPE1') == + dns.rdatatype.A) + + def test_type_bytext_bounds1(self): + self.failUnless(dns.rdatatype.from_text('TYPE0') == 0) + self.failUnless(dns.rdatatype.from_text('TYPE65535') == 65535) + + def test_type_bytext_bounds2(self): + def bad(): + junk = dns.rdatatype.from_text('TYPE65536') + self.failUnlessRaises(ValueError, bad) + + def test_type_bytext_unknown(self): + def bad(): + junk = dns.rdatatype.from_text('XXX') + self.failUnlessRaises(dns.rdatatype.UnknownRdatatype, bad) + + def test_type_totext1(self): + self.failUnless(dns.rdatatype.to_text(dns.rdatatype.A) == 'A') + + def test_type_totext1(self): + self.failUnless(dns.rdatatype.to_text(999) == 'TYPE999') + + def test_type_totext_bounds1(self): + def bad(): + junk = dns.rdatatype.to_text(-1) + self.failUnlessRaises(ValueError, bad) + + def test_type_totext_bounds2(self): + def bad(): + junk = dns.rdatatype.to_text(65536) + self.failUnlessRaises(ValueError, bad) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/resolver.py b/third_party/dnspython/tests/resolver.py new file mode 100644 index 0000000000..28d5a42cdf --- /dev/null +++ b/third_party/dnspython/tests/resolver.py @@ -0,0 +1,127 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import select +import sys +import time +import unittest + +import dns.name +import dns.message +import dns.name +import dns.rdataclass +import dns.rdatatype +import dns.resolver + +resolv_conf = """ + /t/t +# comment 1 +; comment 2 +domain foo +nameserver 10.0.0.1 +nameserver 10.0.0.2 +""" + +message_text = """id 1234 +opcode QUERY +rcode NOERROR +flags QR AA RD +;QUESTION +example. IN A +;ANSWER +example. 1 IN A 10.0.0.1 +;AUTHORITY +;ADDITIONAL +""" + +class BaseResolverTests(object): + + if sys.platform != 'win32': + def testRead(self): + f = cStringIO.StringIO(resolv_conf) + r = dns.resolver.Resolver(f) + self.failUnless(r.nameservers == ['10.0.0.1', '10.0.0.2'] and + r.domain == dns.name.from_text('foo')) + + def testCacheExpiration(self): + message = dns.message.from_text(message_text) + name = dns.name.from_text('example.') + answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, + message) + cache = dns.resolver.Cache() + cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) + time.sleep(2) + self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) + is None) + + def testCacheCleaning(self): + message = dns.message.from_text(message_text) + name = dns.name.from_text('example.') + answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, + message) + cache = dns.resolver.Cache(cleaning_interval=1.0) + cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) + time.sleep(2) + self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) + is None) + + def testZoneForName1(self): + name = dns.name.from_text('www.dnspython.org.') + ezname = dns.name.from_text('dnspython.org.') + zname = dns.resolver.zone_for_name(name) + self.failUnless(zname == ezname) + + def testZoneForName2(self): + name = dns.name.from_text('a.b.www.dnspython.org.') + ezname = dns.name.from_text('dnspython.org.') + zname = dns.resolver.zone_for_name(name) + self.failUnless(zname == ezname) + + def testZoneForName3(self): + name = dns.name.from_text('dnspython.org.') + ezname = dns.name.from_text('dnspython.org.') + zname = dns.resolver.zone_for_name(name) + self.failUnless(zname == ezname) + + def testZoneForName4(self): + def bad(): + name = dns.name.from_text('dnspython.org', None) + zname = dns.resolver.zone_for_name(name) + self.failUnlessRaises(dns.resolver.NotAbsolute, bad) + +class PollingMonkeyPatchMixin(object): + def setUp(self): + self.__native_polling_backend = dns.query._polling_backend + dns.query._set_polling_backend(self.polling_backend()) + + unittest.TestCase.setUp(self) + + def tearDown(self): + dns.query._set_polling_backend(self.__native_polling_backend) + + unittest.TestCase.tearDown(self) + +class SelectResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase): + def polling_backend(self): + return dns.query._select_for + +if hasattr(select, 'poll'): + class PollResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase): + def polling_backend(self): + return dns.query._poll_for + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/rrset.py b/third_party/dnspython/tests/rrset.py new file mode 100644 index 0000000000..be1324b0ec --- /dev/null +++ b/third_party/dnspython/tests/rrset.py @@ -0,0 +1,54 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.rrset + +class RRsetTestCase(unittest.TestCase): + + def testEqual1(self): + r1 = dns.rrset.from_text('foo', 300, 'in', 'a', '10.0.0.1', '10.0.0.2') + r2 = dns.rrset.from_text('FOO', 300, 'in', 'a', '10.0.0.2', '10.0.0.1') + self.failUnless(r1 == r2) + + def testEqual2(self): + r1 = dns.rrset.from_text('foo', 300, 'in', 'a', '10.0.0.1', '10.0.0.2') + r2 = dns.rrset.from_text('FOO', 600, 'in', 'a', '10.0.0.2', '10.0.0.1') + self.failUnless(r1 == r2) + + def testNotEqual1(self): + r1 = dns.rrset.from_text('fooa', 30, 'in', 'a', '10.0.0.1', '10.0.0.2') + r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') + self.failUnless(r1 != r2) + + def testNotEqual2(self): + r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1', '10.0.0.3') + r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') + self.failUnless(r1 != r2) + + def testNotEqual3(self): + r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1', '10.0.0.2', + '10.0.0.3') + r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') + self.failUnless(r1 != r2) + + def testNotEqual4(self): + r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1') + r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') + self.failUnless(r1 != r2) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/set.py b/third_party/dnspython/tests/set.py new file mode 100644 index 0000000000..583d20cfa0 --- /dev/null +++ b/third_party/dnspython/tests/set.py @@ -0,0 +1,208 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.set + +# for convenience +S = dns.set.Set + +class SimpleSetTestCase(unittest.TestCase): + + def testLen1(self): + s1 = S() + self.failUnless(len(s1) == 0) + + def testLen2(self): + s1 = S([1, 2, 3]) + self.failUnless(len(s1) == 3) + + def testLen3(self): + s1 = S([1, 2, 3, 3, 3]) + self.failUnless(len(s1) == 3) + + def testUnion1(self): + s1 = S([1, 2, 3]) + s2 = S([1, 2, 3]) + e = S([1, 2, 3]) + self.failUnless(s1 | s2 == e) + + def testUnion2(self): + s1 = S([1, 2, 3]) + s2 = S([]) + e = S([1, 2, 3]) + self.failUnless(s1 | s2 == e) + + def testUnion3(self): + s1 = S([1, 2, 3]) + s2 = S([3, 4]) + e = S([1, 2, 3, 4]) + self.failUnless(s1 | s2 == e) + + def testIntersection1(self): + s1 = S([1, 2, 3]) + s2 = S([1, 2, 3]) + e = S([1, 2, 3]) + self.failUnless(s1 & s2 == e) + + def testIntersection2(self): + s1 = S([0, 1, 2, 3]) + s2 = S([1, 2, 3, 4]) + e = S([1, 2, 3]) + self.failUnless(s1 & s2 == e) + + def testIntersection3(self): + s1 = S([1, 2, 3]) + s2 = S([]) + e = S([]) + self.failUnless(s1 & s2 == e) + + def testIntersection4(self): + s1 = S([1, 2, 3]) + s2 = S([5, 4]) + e = S([]) + self.failUnless(s1 & s2 == e) + + def testDifference1(self): + s1 = S([1, 2, 3]) + s2 = S([5, 4]) + e = S([1, 2, 3]) + self.failUnless(s1 - s2 == e) + + def testDifference2(self): + s1 = S([1, 2, 3]) + s2 = S([]) + e = S([1, 2, 3]) + self.failUnless(s1 - s2 == e) + + def testDifference3(self): + s1 = S([1, 2, 3]) + s2 = S([3, 2]) + e = S([1]) + self.failUnless(s1 - s2 == e) + + def testDifference4(self): + s1 = S([1, 2, 3]) + s2 = S([3, 2, 1]) + e = S([]) + self.failUnless(s1 - s2 == e) + + def testSubset1(self): + s1 = S([1, 2, 3]) + s2 = S([3, 2, 1]) + self.failUnless(s1.issubset(s2)) + + def testSubset2(self): + s1 = S([1, 2, 3]) + self.failUnless(s1.issubset(s1)) + + def testSubset3(self): + s1 = S([]) + s2 = S([1, 2, 3]) + self.failUnless(s1.issubset(s2)) + + def testSubset4(self): + s1 = S([1]) + s2 = S([1, 2, 3]) + self.failUnless(s1.issubset(s2)) + + def testSubset5(self): + s1 = S([]) + s2 = S([]) + self.failUnless(s1.issubset(s2)) + + def testSubset6(self): + s1 = S([1, 4]) + s2 = S([1, 2, 3]) + self.failUnless(not s1.issubset(s2)) + + def testSuperset1(self): + s1 = S([1, 2, 3]) + s2 = S([3, 2, 1]) + self.failUnless(s1.issuperset(s2)) + + def testSuperset2(self): + s1 = S([1, 2, 3]) + self.failUnless(s1.issuperset(s1)) + + def testSuperset3(self): + s1 = S([1, 2, 3]) + s2 = S([]) + self.failUnless(s1.issuperset(s2)) + + def testSuperset4(self): + s1 = S([1, 2, 3]) + s2 = S([1]) + self.failUnless(s1.issuperset(s2)) + + def testSuperset5(self): + s1 = S([]) + s2 = S([]) + self.failUnless(s1.issuperset(s2)) + + def testSuperset6(self): + s1 = S([1, 2, 3]) + s2 = S([1, 4]) + self.failUnless(not s1.issuperset(s2)) + + def testUpdate1(self): + s1 = S([1, 2, 3]) + u = (4, 5, 6) + e = S([1, 2, 3, 4, 5, 6]) + s1.update(u) + self.failUnless(s1 == e) + + def testUpdate2(self): + s1 = S([1, 2, 3]) + u = [] + e = S([1, 2, 3]) + s1.update(u) + self.failUnless(s1 == e) + + def testGetitem(self): + s1 = S([1, 2, 3]) + i0 = s1[0] + i1 = s1[1] + i2 = s1[2] + s2 = S([i0, i1, i2]) + self.failUnless(s1 == s2) + + def testGetslice(self): + s1 = S([1, 2, 3]) + slice = s1[0:2] + self.failUnless(len(slice) == 2) + item = s1[2] + slice.append(item) + s2 = S(slice) + self.failUnless(s1 == s2) + + def testDelitem(self): + s1 = S([1, 2, 3]) + del s1[0] + i1 = s1[0] + i2 = s1[1] + self.failUnless(i1 != i2) + self.failUnless(i1 == 1 or i1 == 2 or i1 == 3) + self.failUnless(i2 == 1 or i2 == 2 or i2 == 3) + + def testDelslice(self): + s1 = S([1, 2, 3]) + del s1[0:2] + i1 = s1[0] + self.failUnless(i1 == 1 or i1 == 2 or i1 == 3) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/tokenizer.py b/third_party/dnspython/tests/tokenizer.py new file mode 100644 index 0000000000..1d561ae1b5 --- /dev/null +++ b/third_party/dnspython/tests/tokenizer.py @@ -0,0 +1,190 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.exception +import dns.tokenizer + +Token = dns.tokenizer.Token + +class TokenizerTestCase(unittest.TestCase): + + def testQuotedString1(self): + tok = dns.tokenizer.Tokenizer(r'"foo"') + token = tok.get() + self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo')) + + def testQuotedString2(self): + tok = dns.tokenizer.Tokenizer(r'""') + token = tok.get() + self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '')) + + def testQuotedString3(self): + tok = dns.tokenizer.Tokenizer(r'"\"foo\""') + token = tok.get() + self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '"foo"')) + + def testQuotedString4(self): + tok = dns.tokenizer.Tokenizer(r'"foo\010bar"') + token = tok.get() + self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo\x0abar')) + + def testQuotedString5(self): + def bad(): + tok = dns.tokenizer.Tokenizer(r'"foo') + token = tok.get() + self.failUnlessRaises(dns.exception.UnexpectedEnd, bad) + + def testQuotedString6(self): + def bad(): + tok = dns.tokenizer.Tokenizer(r'"foo\01') + token = tok.get() + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def testQuotedString7(self): + def bad(): + tok = dns.tokenizer.Tokenizer('"foo\nbar"') + token = tok.get() + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def testEmpty1(self): + tok = dns.tokenizer.Tokenizer('') + token = tok.get() + self.failUnless(token.is_eof()) + + def testEmpty2(self): + tok = dns.tokenizer.Tokenizer('') + token1 = tok.get() + token2 = tok.get() + self.failUnless(token1.is_eof() and token2.is_eof()) + + def testEOL(self): + tok = dns.tokenizer.Tokenizer('\n') + token1 = tok.get() + token2 = tok.get() + self.failUnless(token1.is_eol() and token2.is_eof()) + + def testWS1(self): + tok = dns.tokenizer.Tokenizer(' \n') + token1 = tok.get() + self.failUnless(token1.is_eol()) + + def testWS2(self): + tok = dns.tokenizer.Tokenizer(' \n') + token1 = tok.get(want_leading=True) + self.failUnless(token1.is_whitespace()) + + def testComment1(self): + tok = dns.tokenizer.Tokenizer(' ;foo\n') + token1 = tok.get() + self.failUnless(token1.is_eol()) + + def testComment2(self): + tok = dns.tokenizer.Tokenizer(' ;foo\n') + token1 = tok.get(want_comment = True) + token2 = tok.get() + self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo') and + token2.is_eol()) + + def testComment3(self): + tok = dns.tokenizer.Tokenizer(' ;foo bar\n') + token1 = tok.get(want_comment = True) + token2 = tok.get() + self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo bar') and + token2.is_eol()) + + def testMultiline1(self): + tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)') + tokens = list(iter(tok)) + self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'), + Token(dns.tokenizer.IDENTIFIER, 'bar')]) + + def testMultiline2(self): + tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)\n') + tokens = list(iter(tok)) + self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'), + Token(dns.tokenizer.IDENTIFIER, 'bar'), + Token(dns.tokenizer.EOL, '\n')]) + def testMultiline3(self): + def bad(): + tok = dns.tokenizer.Tokenizer('foo)') + tokens = list(iter(tok)) + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def testMultiline4(self): + def bad(): + tok = dns.tokenizer.Tokenizer('((foo)') + tokens = list(iter(tok)) + self.failUnlessRaises(dns.exception.SyntaxError, bad) + + def testUnget1(self): + tok = dns.tokenizer.Tokenizer('foo') + t1 = tok.get() + tok.unget(t1) + t2 = tok.get() + self.failUnless(t1 == t2 and t1.ttype == dns.tokenizer.IDENTIFIER and \ + t1.value == 'foo') + + def testUnget2(self): + def bad(): + tok = dns.tokenizer.Tokenizer('foo') + t1 = tok.get() + tok.unget(t1) + tok.unget(t1) + self.failUnlessRaises(dns.tokenizer.UngetBufferFull, bad) + + def testGetEOL1(self): + tok = dns.tokenizer.Tokenizer('\n') + t = tok.get_eol() + self.failUnless(t == '\n') + + def testGetEOL2(self): + tok = dns.tokenizer.Tokenizer('') + t = tok.get_eol() + self.failUnless(t == '') + + def testEscapedDelimiter1(self): + tok = dns.tokenizer.Tokenizer(r'ch\ ld') + t = tok.get() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ ld') + + def testEscapedDelimiter2(self): + tok = dns.tokenizer.Tokenizer(r'ch\032ld') + t = tok.get() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\032ld') + + def testEscapedDelimiter3(self): + tok = dns.tokenizer.Tokenizer(r'ch\ild') + t = tok.get() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ild') + + def testEscapedDelimiter1u(self): + tok = dns.tokenizer.Tokenizer(r'ch\ ld') + t = tok.get().unescape() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch ld') + + def testEscapedDelimiter2u(self): + tok = dns.tokenizer.Tokenizer(r'ch\032ld') + t = tok.get().unescape() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == 'ch ld') + + def testEscapedDelimiter3u(self): + tok = dns.tokenizer.Tokenizer(r'ch\ild') + t = tok.get().unescape() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'child') + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/update.py b/third_party/dnspython/tests/update.py new file mode 100644 index 0000000000..92ddb565fc --- /dev/null +++ b/third_party/dnspython/tests/update.py @@ -0,0 +1,114 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.update +import dns.rdata +import dns.rdataset + +goodhex = '0001 2800 0001 0005 0007 0000' \ + '076578616d706c6500 0006 0001' \ + '03666f6fc00c 00ff 00ff 00000000 0000' \ + 'c019 0001 00ff 00000000 0000' \ + '03626172c00c 0001 0001 00000000 0004 0a000005' \ + '05626c617a32c00c 00ff 00fe 00000000 0000' \ + 'c049 0001 00fe 00000000 0000' \ + 'c019 0001 00ff 00000000 0000' \ + 'c019 0001 0001 0000012c 0004 0a000001' \ + 'c019 0001 0001 0000012c 0004 0a000002' \ + 'c035 0001 0001 0000012c 0004 0a000003' \ + 'c035 0001 00fe 00000000 0004 0a000004' \ + '04626c617ac00c 0001 00ff 00000000 0000' \ + 'c049 00ff 00ff 00000000 0000' + +goodwire = goodhex.replace(' ', '').decode('hex_codec') + +update_text="""id 1 +opcode UPDATE +rcode NOERROR +;ZONE +example. IN SOA +;PREREQ +foo ANY ANY +foo ANY A +bar 0 IN A 10.0.0.5 +blaz2 NONE ANY +blaz2 NONE A +;UPDATE +foo ANY A +foo 300 IN A 10.0.0.1 +foo 300 IN A 10.0.0.2 +bar 300 IN A 10.0.0.3 +bar 0 NONE A 10.0.0.4 +blaz ANY A +blaz2 ANY ANY +""" + +class UpdateTestCase(unittest.TestCase): + + def test_to_wire1(self): + update = dns.update.Update('example') + update.id = 1 + update.present('foo') + update.present('foo', 'a') + update.present('bar', 'a', '10.0.0.5') + update.absent('blaz2') + update.absent('blaz2', 'a') + update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') + update.add('bar', 300, 'a', '10.0.0.3') + update.delete('bar', 'a', '10.0.0.4') + update.delete('blaz','a') + update.delete('blaz2') + self.failUnless(update.to_wire() == goodwire) + + def test_to_wire2(self): + update = dns.update.Update('example') + update.id = 1 + update.present('foo') + update.present('foo', 'a') + update.present('bar', 'a', '10.0.0.5') + update.absent('blaz2') + update.absent('blaz2', 'a') + update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') + update.add('bar', 300, dns.rdata.from_text(1, 1, '10.0.0.3')) + update.delete('bar', 'a', '10.0.0.4') + update.delete('blaz','a') + update.delete('blaz2') + self.failUnless(update.to_wire() == goodwire) + + def test_to_wire3(self): + update = dns.update.Update('example') + update.id = 1 + update.present('foo') + update.present('foo', 'a') + update.present('bar', 'a', '10.0.0.5') + update.absent('blaz2') + update.absent('blaz2', 'a') + update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') + update.add('bar', dns.rdataset.from_text(1, 1, 300, '10.0.0.3')) + update.delete('bar', 'a', '10.0.0.4') + update.delete('blaz','a') + update.delete('blaz2') + self.failUnless(update.to_wire() == goodwire) + + def test_from_text1(self): + update = dns.message.from_text(update_text) + w = update.to_wire(origin=dns.name.from_text('example'), + want_shuffle=False) + self.failUnless(w == goodwire) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/tests/zone.py b/third_party/dnspython/tests/zone.py new file mode 100644 index 0000000000..31e7405b0f --- /dev/null +++ b/third_party/dnspython/tests/zone.py @@ -0,0 +1,389 @@ +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import cStringIO +import filecmp +import os +import unittest + +import dns.exception +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.rrset +import dns.zone + +example_text = """$TTL 3600 +$ORIGIN example. +@ soa foo bar 1 2 3 4 5 +@ ns ns1 +@ ns ns2 +ns1 a 10.0.0.1 +ns2 a 10.0.0.2 +$TTL 300 +$ORIGIN foo.example. +bar mx 0 blaz +""" + +example_text_output = """@ 3600 IN SOA foo bar 1 2 3 4 5 +@ 3600 IN NS ns1 +@ 3600 IN NS ns2 +bar.foo 300 IN MX 0 blaz.foo +ns1 3600 IN A 10.0.0.1 +ns2 3600 IN A 10.0.0.2 +""" + +something_quite_similar = """@ 3600 IN SOA foo bar 1 2 3 4 5 +@ 3600 IN NS ns1 +@ 3600 IN NS ns2 +bar.foo 300 IN MX 0 blaz.foo +ns1 3600 IN A 10.0.0.1 +ns2 3600 IN A 10.0.0.3 +""" + +something_different = """@ 3600 IN SOA fooa bar 1 2 3 4 5 +@ 3600 IN NS ns11 +@ 3600 IN NS ns21 +bar.fooa 300 IN MX 0 blaz.fooa +ns11 3600 IN A 10.0.0.11 +ns21 3600 IN A 10.0.0.21 +""" + +ttl_example_text = """$TTL 1h +$ORIGIN example. +@ soa foo bar 1 2 3 4 5 +@ ns ns1 +@ ns ns2 +ns1 1d1s a 10.0.0.1 +ns2 1w1D1h1m1S a 10.0.0.2 +""" + +no_soa_text = """$TTL 1h +$ORIGIN example. +@ ns ns1 +@ ns ns2 +ns1 1d1s a 10.0.0.1 +ns2 1w1D1h1m1S a 10.0.0.2 +""" + +no_ns_text = """$TTL 1h +$ORIGIN example. +@ soa foo bar 1 2 3 4 5 +""" + +include_text = """$INCLUDE "example" +""" + +bad_directive_text = """$FOO bar +$ORIGIN example. +@ soa foo bar 1 2 3 4 5 +@ ns ns1 +@ ns ns2 +ns1 1d1s a 10.0.0.1 +ns2 1w1D1h1m1S a 10.0.0.2 +""" + +_keep_output = False + +class ZoneTestCase(unittest.TestCase): + + def testFromFile1(self): + z = dns.zone.from_file('example', 'example') + ok = False + try: + z.to_file('example1.out', nl='\x0a') + ok = filecmp.cmp('example1.out', 'example1.good') + finally: + if not _keep_output: + os.unlink('example1.out') + self.failUnless(ok) + + def testFromFile2(self): + z = dns.zone.from_file('example', 'example', relativize=False) + ok = False + try: + z.to_file('example2.out', relativize=False, nl='\x0a') + ok = filecmp.cmp('example2.out', 'example2.good') + finally: + if not _keep_output: + os.unlink('example2.out') + self.failUnless(ok) + + def testFromText(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + f = cStringIO.StringIO() + names = z.nodes.keys() + names.sort() + for n in names: + print >> f, z[n].to_text(n) + self.failUnless(f.getvalue() == example_text_output) + + def testTorture1(self): + # + # Read a zone containing all our supported RR types, and + # for each RR in the zone, convert the rdata into wire format + # and then back out, and see if we get equal rdatas. + # + f = cStringIO.StringIO() + o = dns.name.from_text('example.') + z = dns.zone.from_file('example', o) + for (name, node) in z.iteritems(): + for rds in node: + for rd in rds: + f.seek(0) + f.truncate() + rd.to_wire(f, origin=o) + wire = f.getvalue() + rd2 = dns.rdata.from_wire(rds.rdclass, rds.rdtype, + wire, 0, len(wire), + origin = o) + self.failUnless(rd == rd2) + + def testEqual(self): + z1 = dns.zone.from_text(example_text, 'example.', relativize=True) + z2 = dns.zone.from_text(example_text_output, 'example.', + relativize=True) + self.failUnless(z1 == z2) + + def testNotEqual1(self): + z1 = dns.zone.from_text(example_text, 'example.', relativize=True) + z2 = dns.zone.from_text(something_quite_similar, 'example.', + relativize=True) + self.failUnless(z1 != z2) + + def testNotEqual2(self): + z1 = dns.zone.from_text(example_text, 'example.', relativize=True) + z2 = dns.zone.from_text(something_different, 'example.', + relativize=True) + self.failUnless(z1 != z2) + + def testNotEqual3(self): + z1 = dns.zone.from_text(example_text, 'example.', relativize=True) + z2 = dns.zone.from_text(something_different, 'example2.', + relativize=True) + self.failUnless(z1 != z2) + + def testFindRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rds = z.find_rdataset('@', 'soa') + exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') + self.failUnless(rds == exrds) + + def testFindRdataset2(self): + def bad(): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rds = z.find_rdataset('@', 'loc') + self.failUnlessRaises(KeyError, bad) + + def testFindRRset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rrs = z.find_rrset('@', 'soa') + exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') + self.failUnless(rrs == exrrs) + + def testFindRRset2(self): + def bad(): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rrs = z.find_rrset('@', 'loc') + self.failUnlessRaises(KeyError, bad) + + def testGetRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rds = z.get_rdataset('@', 'soa') + exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') + self.failUnless(rds == exrds) + + def testGetRdataset2(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rds = z.get_rdataset('@', 'loc') + self.failUnless(rds == None) + + def testGetRRset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rrs = z.get_rrset('@', 'soa') + exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') + self.failUnless(rrs == exrrs) + + def testGetRRset2(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rrs = z.get_rrset('@', 'loc') + self.failUnless(rrs == None) + + def testReplaceRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rdataset = dns.rdataset.from_text('in', 'ns', 300, 'ns3', 'ns4') + z.replace_rdataset('@', rdataset) + rds = z.get_rdataset('@', 'ns') + self.failUnless(rds is rdataset) + + def testReplaceRdataset2(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + rdataset = dns.rdataset.from_text('in', 'txt', 300, '"foo"') + z.replace_rdataset('@', rdataset) + rds = z.get_rdataset('@', 'txt') + self.failUnless(rds is rdataset) + + def testDeleteRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + z.delete_rdataset('@', 'ns') + rds = z.get_rdataset('@', 'ns') + self.failUnless(rds is None) + + def testDeleteRdataset2(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + z.delete_rdataset('ns1', 'a') + node = z.get_node('ns1') + self.failUnless(node is None) + + def testNodeFindRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + node = z['@'] + rds = node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) + exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') + self.failUnless(rds == exrds) + + def testNodeFindRdataset2(self): + def bad(): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + node = z['@'] + rds = node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) + self.failUnlessRaises(KeyError, bad) + + def testNodeGetRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + node = z['@'] + rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) + exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') + self.failUnless(rds == exrds) + + def testNodeGetRdataset2(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + node = z['@'] + rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) + self.failUnless(rds == None) + + def testNodeDeleteRdataset1(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + node = z['@'] + rds = node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) + rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) + self.failUnless(rds == None) + + def testNodeDeleteRdataset2(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + node = z['@'] + rds = node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) + rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) + self.failUnless(rds == None) + + def testIterateRdatasets(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + ns = [n for n, r in z.iterate_rdatasets('A')] + ns.sort() + self.failUnless(ns == [dns.name.from_text('ns1', None), + dns.name.from_text('ns2', None)]) + + def testIterateAllRdatasets(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + ns = [n for n, r in z.iterate_rdatasets()] + ns.sort() + self.failUnless(ns == [dns.name.from_text('@', None), + dns.name.from_text('@', None), + dns.name.from_text('bar.foo', None), + dns.name.from_text('ns1', None), + dns.name.from_text('ns2', None)]) + + def testIterateRdatas(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + l = list(z.iterate_rdatas('A')) + l.sort() + exl = [(dns.name.from_text('ns1', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, + '10.0.0.1')), + (dns.name.from_text('ns2', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, + '10.0.0.2'))] + self.failUnless(l == exl) + + def testIterateAllRdatas(self): + z = dns.zone.from_text(example_text, 'example.', relativize=True) + l = list(z.iterate_rdatas()) + l.sort() + exl = [(dns.name.from_text('@', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, + 'ns1')), + (dns.name.from_text('@', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, + 'ns2')), + (dns.name.from_text('@', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, + 'foo bar 1 2 3 4 5')), + (dns.name.from_text('bar.foo', None), + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, + '0 blaz.foo')), + (dns.name.from_text('ns1', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, + '10.0.0.1')), + (dns.name.from_text('ns2', None), + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, + '10.0.0.2'))] + self.failUnless(l == exl) + + def testTTLs(self): + z = dns.zone.from_text(ttl_example_text, 'example.', relativize=True) + n = z['@'] + rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) + self.failUnless(rds.ttl == 3600) + n = z['ns1'] + rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) + self.failUnless(rds.ttl == 86401) + n = z['ns2'] + rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) + self.failUnless(rds.ttl == 694861) + + def testNoSOA(self): + def bad(): + z = dns.zone.from_text(no_soa_text, 'example.', + relativize=True) + self.failUnlessRaises(dns.zone.NoSOA, bad) + + def testNoNS(self): + def bad(): + z = dns.zone.from_text(no_ns_text, 'example.', + relativize=True) + self.failUnlessRaises(dns.zone.NoNS, bad) + + def testInclude(self): + z1 = dns.zone.from_text(include_text, 'example.', relativize=True, + allow_include=True) + z2 = dns.zone.from_file('example', 'example.', relativize=True) + self.failUnless(z1 == z2) + + def testBadDirective(self): + def bad(): + z = dns.zone.from_text(bad_directive_text, 'example.', + relativize=True) + self.failUnlessRaises(dns.exception.SyntaxError, bad) + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/dnspython/util/COPYRIGHT b/third_party/dnspython/util/COPYRIGHT new file mode 100644 index 0000000000..7390363fbb --- /dev/null +++ b/third_party/dnspython/util/COPYRIGHT @@ -0,0 +1,14 @@ +Copyright (C) @YEARS@ Nominum, Inc. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose with or without fee is hereby granted, +provided that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/third_party/dnspython/util/copyrights b/third_party/dnspython/util/copyrights new file mode 100644 index 0000000000..f73e9f6fe3 --- /dev/null +++ b/third_party/dnspython/util/copyrights @@ -0,0 +1,116 @@ +./.gitignore X 2009 +./ChangeLog X 2003,2004,2005,2006,2007 +./LICENSE X 2003,2004,2005,2006,2007 +./MANIFEST.in X 2003,2004,2005,2006,2007 +./Makefile MAKE 2003,2004,2005,2006,2007,2009,2011 +./README X 2003,2004,2005,2006,2007 +./TODO X 2003,2004,2005,2006,2007 +./dns/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2011 +./dns/dnssec.py PYTHON 2003,2004,2005,2006,2007,2009,2011 +./dns/e164.py PYTHON 2006,2007,2009,2011 +./dns/edns.py PYTHON 2009,2011 +./dns/entropy.py PYTHON 2009,2011 +./dns/exception.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/flags.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/hash.py PYTHON 2011 +./dns/inet.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/ipv4.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/ipv6.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/message.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/name.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/namedict.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/node.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/opcode.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/query.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rcode.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdata.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdataclass.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdataset.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdatatype.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/AFSDB.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/CERT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/CNAME.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/DLV.py PYTHON 2009,2010,2011 +./dns/rdtypes/ANY/DNAME.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/DNSKEY.py PYTHON 2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/DS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/GPOS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/HINFO.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/HIP.py PYTHON 2010,2011 +./dns/rdtypes/ANY/ISDN.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/LOC.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/MX.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/NS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/NSEC.py PYTHON 2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/NSEC3.py PYTHON 2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/NSEC3PARAM.py PYTHON 2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/PTR.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/RP.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/RRSIG.py PYTHON 2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/RT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/SOA.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/SPF.py PYTHON 2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/SSHFP.py PYTHON 2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/TXT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/X25.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/A.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/AAAA.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/APL.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/DHCID.py PYTHON 2006,2007,2009,2010,2011 +./dns/rdtypes/IN/IPSECKEY.py PYTHON 2006,2007,2009,2010,2011 +./dns/rdtypes/IN/KX.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/NAPTR.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/NSAP.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/NSAP_PTR.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/PX.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/SRV.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/WKS.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/IN/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/dsbase.py PYTHON 2010,2011 +./dns/rdtypes/mxbase.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/nsbase.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/rdtypes/txtbase.py PYTHON 2006,2007,2009,2010,2011 +./dns/renderer.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/resolver.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/reversename.py PYTHON 2006,2007,2009,2010,2011 +./dns/rrset.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/set.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/tokenizer.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/tsig.py PYTHON 2001,2002,2003,2004,2005,2006,2007,2009,2010,2011 +./dns/tsigkeyring.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/ttl.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/update.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/version.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./dns/wiredata.py PYTHON 2011 +./dns/zone.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./examples/ddns.py X 2006,2007 +./examples/e164.py X 2006,2007 +./examples/mx.py X 2003,2004,2005,2006,2007 +./examples/name.py X 2003,2004,2005,2006,2007 +./examples/reverse.py X 2003,2004,2005,2006,2007 +./examples/reverse_name.py X 2006,2007 +./examples/xfr.py X 2003,2004,2005,2006,2007 +./examples/zonediff.py X 2010,2011 +./setup.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/Makefile MAKE 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/bugs.py PYTHON 2006,2007,2009,2010,2011 +./tests/dnssec.py PYTHON 2011 +./tests/example X 2003,2004,2005,2006,2007 +./tests/example1.good X 2003,2004,2005,2006,2007 +./tests/example2.good X 2003,2004,2005,2006,2007 +./tests/flags.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/message.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/name.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/namedict.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/ntoaaton.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/rdtypeandclass.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/resolver.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/rrset.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/set.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/tokenizer.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/update.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./tests/zone.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 +./util/COPYRIGHT X 2003,2004,2005,2006,2007 +./util/copyrights X 2003,2004,2005,2006,2007 diff --git a/third_party/wscript_build b/third_party/wscript_build index d8b9aae015..0be84743b3 100644 --- a/third_party/wscript_build +++ b/third_party/wscript_build @@ -4,6 +4,7 @@ import os # work out what python external libraries we need to install external_libs = { + "dns.resolver": "dnspython/dns", } list = [] -- cgit