Commits

paulc committed 7ff6bbc

Switch to test_decode for encode/decode testing

Comments (0)

Files changed (8)

dnslib/digparser.py

 # -*- coding: utf-8 -*-
 from __future__ import print_function
 
+import string
+
 from dnslib.lex import WordLexer
 from dnslib.dns import (DNSRecord,DNSHeader,DNSQuestion,DNSError,
                         RR,RD,RDMAP,QR,RCODE,CLASS,QTYPE)
                 rdata = rr[4:]
                 rd = RDMAP.get(rtype,RD)
                 try:
-                    f(RR(rname=rname,
+                    if rd == RD and \
+                       any([ x not in string.hexdigits for x in rdata[-1]]):
+                        # Only support hex encoded data for fallback RD
+                        pass
+                    else:
+                        f(RR(rname=rname,
                                 ttl=int(ttl),
                                 rtype=getattr(QTYPE,rtype),
                                 rclass=getattr(CLASS,rclass),

dnslib/test/google.com-AAAA

+;; Sending:
+;; QUERY: 21590100000100000000000006676f6f676c6503636f6d00001c0001
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8537
+;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
+;; QUESTION SECTION:
+;google.com.                    IN      AAAA
+
+;; Got answer:
+;; RESPONSE: 21598180000100010000000006676f6f676c6503636f6d00001c0001c00c001c00010000009100102a001450400908050000000000001000
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8537
+;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+;; QUESTION SECTION:
+;google.com.                    IN      AAAA
+;; ANSWER SECTION:
+google.com.             145     IN      AAAA    2a00:1450:4009:805::1000
+

dnslib/test/google.com-ANY

 ;; Sending:
-;; QUERY: 86500100000100000000000006676f6f676c6503636f6d0000ff0001
-;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34384
+;; QUERY: b8b80100000100000000000006676f6f676c6503636f6d0000ff0001
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47288
 ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
 ;; QUESTION SECTION:
 ;google.com.                    IN      ANY
 
 ;; Got answer:
-;; RESPONSE: 865081800001001c0000000006676f6f676c6503636f6d0000ff0001c00c000100010000012b00043efca9bbc00c000100010000012b00043efca9b7c00c000100010000012b00043efca9a3c00c000100010000012b00043efca9a2c00c000100010000012b00043efca9adc00c000100010000012b00043efca994c00c000100010000012b00043efca999c00c000100010000012b00043efca9a8c00c000100010000012b00043efca9a7c00c000100010000012b00043efca998c00c000100010000012b00043efca99dc00c000100010000012b00043efca99ec00c000100010000012b00043efca9acc00c000100010000012b00043efca9b1c00c000100010000012b00043efca9b6c00c000100010000012b00043efca9b2c00c000200010000545f0006036e7331c00cc00c000f000100000257000c000a056173706d78016cc00cc00c000600010000545f0022c12809646e732d61646d696ec00c780b88a800001c2000000708001275000000012cc00c000f0001000002570009002804616c7433c13cc00c000f0001000002570009001404616c7431c13cc00c000f0001000002570009003204616c7434c13cc00c000200010000545f0006036e7332c00cc00c000200010000545f0006036e7334c00cc00c000200010000545f0006036e7333c00cc00c010100010000545f00130005697373756573796d616e7465632e636f6dc00c000f0001000002570009001e04616c7432c13cc00c0010000100000e0f004c4b763d7370663120696e636c7564653a5f7370662e676f6f676c652e636f6d206970343a3231362e37332e39332e37302f3331206970343a3231362e37332e39332e37322f3331207e616c6c
-;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34384
+;; RESPONSE: b8b881800001001c0000000006676f6f676c6503636f6d0000ff0001c00c000100010000007d00043efca9a2c00c000100010000007d00043efca994c00c000100010000007d00043efca999c00c000100010000007d00043efca9a7c00c000100010000007d00043efca9adc00c000100010000007d00043efca9b6c00c000100010000007d00043efca9acc00c000100010000007d00043efca998c00c000100010000007d00043efca9a3c00c000100010000007d00043efca9b2c00c000100010000007d00043efca9b7c00c000100010000007d00043efca9a8c00c000100010000007d00043efca9bbc00c000100010000007d00043efca9b1c00c000100010000007d00043efca99ec00c000100010000007d00043efca99dc00c000f0001000001a90011001e04616c7432056173706d78016cc00cc00c00020001000053b10006036e7333c00cc00c000f0001000001a90009001404616c7431c12fc00c000f0001000001a90009003204616c7434c12fc00c00020001000053b10006036e7331c00cc00c000f0001000001a90009002804616c7433c12fc00c00020001000053b10006036e7334c00cc00c0010000100000d61004c4b763d7370663120696e636c7564653a5f7370662e676f6f676c652e636f6d206970343a3231362e37332e39332e37302f3331206970343a3231362e37332e39332e37322f3331207e616c6cc00c01010001000053b100130005697373756573796d616e7465632e636f6dc00c00060001000053b10022c18109646e732d61646d696ec00c780b88a800001c2000000708001275000000012cc00c000f0001000001a90004000ac12fc00c00020001000053b10006036e7332c00c
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47288
 ;; flags: qr rd ra; QUERY: 1, ANSWER: 28, AUTHORITY: 0, ADDITIONAL: 0
 ;; QUESTION SECTION:
 ;google.com.                    IN      ANY
 ;; ANSWER SECTION:
-google.com.             299     IN      A       62.252.169.187
-google.com.             299     IN      A       62.252.169.183
-google.com.             299     IN      A       62.252.169.163
-google.com.             299     IN      A       62.252.169.162
-google.com.             299     IN      A       62.252.169.173
-google.com.             299     IN      A       62.252.169.148
-google.com.             299     IN      A       62.252.169.153
-google.com.             299     IN      A       62.252.169.168
-google.com.             299     IN      A       62.252.169.167
-google.com.             299     IN      A       62.252.169.152
-google.com.             299     IN      A       62.252.169.157
-google.com.             299     IN      A       62.252.169.158
-google.com.             299     IN      A       62.252.169.172
-google.com.             299     IN      A       62.252.169.177
-google.com.             299     IN      A       62.252.169.182
-google.com.             299     IN      A       62.252.169.178
-google.com.             21599   IN      NS      ns1.google.com.
-google.com.             599     IN      MX      10 aspmx.l.google.com.
-google.com.             21599   IN      SOA     ns1.google.com. dns-admin.google.com. 2014021800 7200 1800 1209600 300
-google.com.             599     IN      MX      40 alt3.aspmx.l.google.com.
-google.com.             599     IN      MX      20 alt1.aspmx.l.google.com.
-google.com.             599     IN      MX      50 alt4.aspmx.l.google.com.
-google.com.             21599   IN      NS      ns2.google.com.
-google.com.             21599   IN      NS      ns4.google.com.
-google.com.             21599   IN      NS      ns3.google.com.
-google.com.             21599   IN      TYPE257 0005697373756573796d616e7465632e636f6d
-google.com.             599     IN      MX      30 alt2.aspmx.l.google.com.
-google.com.             3599    IN      TXT     "v=spf1 include:_spf.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all"
+google.com.             125     IN      A       62.252.169.162
+google.com.             125     IN      A       62.252.169.148
+google.com.             125     IN      A       62.252.169.153
+google.com.             125     IN      A       62.252.169.167
+google.com.             125     IN      A       62.252.169.173
+google.com.             125     IN      A       62.252.169.182
+google.com.             125     IN      A       62.252.169.172
+google.com.             125     IN      A       62.252.169.152
+google.com.             125     IN      A       62.252.169.163
+google.com.             125     IN      A       62.252.169.178
+google.com.             125     IN      A       62.252.169.183
+google.com.             125     IN      A       62.252.169.168
+google.com.             125     IN      A       62.252.169.187
+google.com.             125     IN      A       62.252.169.177
+google.com.             125     IN      A       62.252.169.158
+google.com.             125     IN      A       62.252.169.157
+google.com.             425     IN      MX      30 alt2.aspmx.l.google.com.
+google.com.             21425   IN      NS      ns3.google.com.
+google.com.             425     IN      MX      20 alt1.aspmx.l.google.com.
+google.com.             425     IN      MX      50 alt4.aspmx.l.google.com.
+google.com.             21425   IN      NS      ns1.google.com.
+google.com.             425     IN      MX      40 alt3.aspmx.l.google.com.
+google.com.             21425   IN      NS      ns4.google.com.
+google.com.             3425    IN      TXT     "v=spf1 include:_spf.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all"
+google.com.             21425   IN      TYPE257 0005697373756573796d616e7465632e636f6d
+google.com.             21425   IN      SOA     ns1.google.com. dns-admin.google.com. 2014021800 7200 1800 1209600 300
+google.com.             425     IN      MX      10 aspmx.l.google.com.
+google.com.             21425   IN      NS      ns2.google.com.
 

dnslib/test/iana.org-ANY

+;; Sending:
+;; QUERY: 4c14010000010000000000000469616e61036f72670000ff0001
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19476
+;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
+;; QUESTION SECTION:
+;iana.org.                      IN      ANY
+
+;; Got answer:
+;; RESPONSE: 4c1481800001001a000000000469616e61036f72670000ff0001c00c0006000100000e06002a03736e7303646e73056963616e6ec011036e6f63c02a77fd866400001c2000000e100012750000000e10c00c000100010000024e0004c0002b08c00c002e00010000024e009c0001080200000258536b07db53615c818b240469616e61036f72670013c92ac5ea251d2c7f1de4cbda47fffb902b191c74eb46fc61fdebba63721c54fcfe4214937e8aea1039ed9e0f47b5f4b0423eb5b20b1162ab3d9a3a3c3637b2680ae59cfdb32b7ab704007db77fe3bdaf5cee3619cc91d40f29794ab705c80404951f2ccad14578112c16394125c351daf561e552aa12b683a1d44bce62c809c00c00020001000054560005026e73c02ec00c0002000100005456001401620c69616e612d73657276657273036e657400c00c000200010000545600040163c127c00c000200010000545600040161c127c00c002e000100005456009c0002080200015180536b12b653615c818b240469616e61036f72670040b91e127f60ba357111cf7565944d00bb34d7f309c1555753695d85e8984765d8e08c4a0957ae75ee7fb4255241dd2cbefef81ee7e6d0b7dfb4dce78ec8fad19147841879e28d6042fd2edba03cfc4d392346852bb95b83795859ea7f677e593d865edd8c2f14fe2c3a7e4af2e40ee74f6d6e3936a24b944c64569cf538f067c00c002e000100000e06009c0006080200000e10536ac25f53615c818b240469616e61036f726700b3c22997425048c834d360c039c4a0e58cb83797fae52ae3924d86062b8e95f153eaf17d5192b425f21eee59df05c5e094e0bc75daca09f23e95bc696ae699994542f10bfc55282594cec722641e2aa79c4212acfe29d0890df018b954359da66e8dee44a0ae5ce993756da553de94ab5b6472b105d8b1a88c958cdea3931095c00c000f00010000024e000d000a08706563686f726134c02ec00c000f00010000024e000d000a08706563686f726131c02ec00c000f00010000024e000d000a08706563686f726135c02ec00c000f00010000024e000d000a08706563686f726138c02ec00c000f00010000024e000d000a08706563686f726137c02ec00c000f00010000024e000d000a08706563686f726133c02ec00c002e00010000024e009c000f080200000258536b28bd53615c818b240469616e61036f7267008ed9bd529e6b96039bda14a45d2c68016a95200319c80ad3c9a4e887240b58a13e89de16113559f2ef09b0fc5b3c152fd0fd2049780cd51405f4a215ed46e7adfdeb3aa37277096925923a53a9e687230e803d95c82c6fc0190a480b5fb388e92cab300cccb1b29294189137113a9c12279dfbc4627b216ce1e459864672c3adc00c00100001000000320033322449643a2069616e612e6f7267203139333920323031332d31302d32322031383a32373a32395a2064636c6f73736f6e2024c00c002e000100000032009c001008020000003c536b3fa153615c818b240469616e61036f72670071c6b10b7cf9a70a4422cda6dedf3929cf9356bbb7126c19c5b9def7f67a423388b76c7480489c84ec77f22782d4436e6261d2d5daf8bf7a1d45a0e59250ef1d8c3d2b385e449c3b20b975c69918e034b2dbeadb2a335c3c17334a61ed92f47149fea579d615874f91ba64a211240a437e42c0654c6dadc6a5eca305be869443c00c001c00010000024e001020010500008802000000000000000008c00c002e00010000024e009c001c080200000258536b08a853615c818b240469616e61036f72670046f728d39162cb5a566c3b1354ceaf512686603dfcd465340ae9ada9fdac5724f401133da7d2268a0ea61fa39acad1e0e745679ba11d20097816746ae2c688e9a63fcd945dfc00ab30ebba68adee61938927f06a9562d09cc6a35cda4824fd5260392d31fd5d1980425377281c1e3eb8dc7d73f6cdb693fc5ae265327994492ec00c0030000100000e0601080101030803010001bed3b4d1476380436e2fdfb47da3d4ec01fbdd7587cf082ee437e7f02aca7a030e5d19053fa19f78ac2e20831c37bc1589955d47ddbe7b64271d58d1a1cff66a213c7d6c8dbc680cf920eb92a5a17f8a42cdf8d2c9eed78a9f4191ac6ba9f20d090b5fd3a7dc7e94f562dce17d4c07ef6a2bd66b7d1d90dbd432ef4e290284f8d9b44971c17466e68090685b41e404d86b93c0fcd777aae178bc1e42591963777b44e7bdb0b56734bc5333422c6e4182fadb6f7eb8417c1fc8f55a840f9bb4d810f7535112cad19ee52610ebc3f1378cf5415db82507e065ef6180edf1f3ea16e908a150a8e119c5316dee99be6edf2caabfb8023a5852c7f178940d6b3cfb7dc00c0030000100000e0600880100030803010001d7c8cde9f019a8ae616295ec1c83bef6ff35c9b127d08c0e048f70582d6513cf1427bc7c460561afaf976ce6afb0df1aaf2df3099f3da6973df81297e458176a555ce35192b8a1ff7e88f462fbe6f92bef31559bad686a1c59647f8c6803a88a4c5978c8c0806e13e014cc7ec93ffa72d35f55d5c70c675897ed07d29372ddf3c00c0030000100000e0601080101030803010001c1fb8508eeea11f7fe747aba37a397f46a3e174bad479d4e43292d99c1f22c7bb2d7d0add86b50a966f673950e35b4dba3fe3c49cd2cf45c5c32fcb914e797274b95e5501b31d5662f53c7339bbf18289715533e849508eccedfd9f637a6a695b93d0e1e16a5a4af691a7fa587f7ffed83678a37ded44b3c4549c87026541c68b20dcf5ef51e22481586374f72b031a3d562d7668b3233536acd78285dd228384e219e5b6e60f333bd024b613df9b8ecaa88b8ae977f47d2226d7e9090ff89d37c8deb8386910381bc1d546ba8019c44e866cd06ce8ec297822320a2a212225d60622ee7404d65c67d375787bd6b363421ac6cd42d9141c7687de7c5761663a9c00c002e000100000e06011c0030080200000e10536b3d9353615c811a4a0469616e61036f72670023bf2985b868a1e23b9b5e80e38946fddf76f53fbe7f1a06a02eaedd5d116e9e1be716be8afad0d3f886387966e0b7511f1be581fd2c8aba98c5930716ba2eb7d8cec2462ff035acd602c5bf1bfadb215319c787d3a4468af21bbddb63c190fbe4aeed5a9cac17e4e503edabc99e18e6bb47963b5a8eea76e0ce92997fec58cf43e9309ebbd8de026ce122bb0215c96c4d0b5ddf3bab90ed7fbacc29288d103c30f9cb58af015dc21bd5cdc05ef4c1251e504c97c75b1342eff630e37d88331c89a1b71c15207dc284becca44818fc00dedafc4092cb8b90ce3859ee38c2471dbd7818d18a147f67ed7d77f8803766578fb36dc720dbb483174d05191166d871c00c002f000100000e06001c036e733104617574680469616e61036f726700000762018008000380c00c002e000100000e06009c002f080200000e10536b13ae53615c818b240469616e61036f726700a70ee185f024c5efb766a5cfb42b19532f782c7f49ca8415f978d380d60bba97710948dccebc32b6464ff588d00f3539e1778763aa0f218cb48ed64c8671fe419f9a03f36199c7847da11fb7bb0a52fef7f612275eee6d0977fc540d30e99358c7ffe9d69adea8d29895328e39c6b5f1a65801384693e0b0e2b346d35b18e464
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19476
+;; flags: qr rd ra; QUERY: 1, ANSWER: 26, AUTHORITY: 0, ADDITIONAL: 0
+;; QUESTION SECTION:
+;iana.org.                      IN      ANY
+;; ANSWER SECTION:
+iana.org.               3590    IN      SOA     sns.dns.icann.org. noc.dns.icann.org. 2013103716 7200 3600 1209600 3600
+iana.org.               590     IN      A       192.0.43.8
+iana.org.               590     IN      RRSIG   A 8 2 600 20140508042811 20140430202641 35620 iana.org. E8kqxeolHSx/HeTL2kf/+5ArGRx060b8Yf3rumNyHFT8/kIUk36K6hA57Z4PR7X0sEI+tbILEWKrPZo6PDY3smgK5Zz9syt6twQAfbd/472vXO42GcyR1A8peUq3BcgEBJUfLMrRRXgRLBY5QSXDUdr1YeVSqhK2g6HUS85iyAk=
+iana.org.               21590   IN      NS      ns.icann.org.
+iana.org.               21590   IN      NS      b.iana-servers.net.
+iana.org.               21590   IN      NS      c.iana-servers.net.
+iana.org.               21590   IN      NS      a.iana-servers.net.
+iana.org.               21590   IN      RRSIG   NS 8 2 86400 20140508051430 20140430202641 35620 iana.org. QLkeEn9gujVxEc91ZZRNALs01/MJwVVXU2ldheiYR2XY4IxKCVeude5/tCVSQd0svv74Hufm0LfftNznjsj60ZFHhBh54o1gQv0u26A8/E05I0aFK7lbg3lYWep/Z35ZPYZe3YwvFP4sOn5K8uQO509tbjk2okuUTGRWnPU48Gc=
+iana.org.               3590    IN      RRSIG   SOA 8 2 3600 20140507233143 20140430202641 35620 iana.org. s8Ipl0JQSMg002DAOcSg5Yy4N5f65Srjkk2GBiuOlfFT6vF9UZK0JfIe7lnfBcXglOC8ddrKCfI+lbxpauaZmUVC8Qv8VSgllM7HImQeKqecQhKs/inQiQ3wGLlUNZ2mbo3uRKCuXOmTdW2lU96Uq1tkcrEF2LGojJWM3qOTEJU=
+iana.org.               590     IN      MX      10 pechora4.icann.org.
+iana.org.               590     IN      MX      10 pechora1.icann.org.
+iana.org.               590     IN      MX      10 pechora5.icann.org.
+iana.org.               590     IN      MX      10 pechora8.icann.org.
+iana.org.               590     IN      MX      10 pechora7.icann.org.
+iana.org.               590     IN      MX      10 pechora3.icann.org.
+iana.org.               590     IN      RRSIG   MX 8 2 600 20140508064829 20140430202641 35620 iana.org. jtm9Up5rlgOb2hSkXSxoAWqVIAMZyArTyaTohyQLWKE+id4WETVZ8u8JsPxbPBUv0P0gSXgM1RQF9KIV7Ubnrf3rOqNydwlpJZI6U6nmhyMOgD2VyCxvwBkKSAtfs4jpLKswDMyxspKUGJE3ETqcEied+8RieyFs4eRZhkZyw60=
+iana.org.               50      IN      TXT     "$Id: iana.org 1939 2013-10-22 18:27:29Z dclosson $"
+iana.org.               50      IN      RRSIG   TXT 8 2 60 20140508082609 20140430202641 35620 iana.org. ccaxC3z5pwpEIs2m3t85Kc+TVru3EmwZxbne9/Z6QjOIt2x0gEichOx38ieC1ENuYmHS1dr4v3odRaDlklDvHYw9KzheRJw7ILl1xpkY4DSy2+rbKjNcPBczSmHtkvRxSf6ledYVh0+RumSiESQKQ35CwGVMba3GpeyjBb6GlEM=
+iana.org.               590     IN      AAAA    2001:500:88:200::8
+iana.org.               590     IN      RRSIG   AAAA 8 2 600 20140508043136 20140430202641 35620 iana.org. Rvco05Fiy1pWbDsTVM6vUSaGYD381GU0Cumtqf2sVyT0ARM9p9Imig6mH6OaytHg50Vnm6EdIAl4FnRq4saI6aY/zZRd/ACrMOu6aK3uYZOJJ/BqlWLQnMajXNpIJP1SYDktMf1dGYBCU3coHB4+uNx9c/bNtpP8WuJlMnmUSS4=
+iana.org.               3590    IN      DNSKEY  257 3 8 AwEAAb7TtNFHY4BDbi/ftH2j1OwB+911h88ILuQ35/AqynoDDl0ZBT+hn3isLiCDHDe8FYmVXUfdvntkJx1Y0aHP9mohPH1sjbxoDPkg65KloX+KQs340snu14qfQZGsa6nyDQkLX9On3H6U9WLc4X1MB+9qK9ZrfR2Q29Qy704pAoT42bRJccF0ZuaAkGhbQeQE2GuTwPzXd6rheLweQlkZY3d7ROe9sLVnNLxTM0IsbkGC+ttvfrhBfB/I9VqED5u02BD3U1ESytGe5SYQ68PxN4z1QV24JQfgZe9hgO3x8+oW6QihUKjhGcUxbe6Zvm7fLKq/uAI6WFLH8XiUDWs8+30=
+iana.org.               3590    IN      DNSKEY  256 3 8 AwEAAdfIzenwGaiuYWKV7ByDvvb/NcmxJ9CMDgSPcFgtZRPPFCe8fEYFYa+vl2zmr7DfGq8t8wmfPaaXPfgSl+RYF2pVXONRkrih/36I9GL75vkr7zFVm61oahxZZH+MaAOoikxZeMjAgG4T4BTMfsk/+nLTX1XVxwxnWJftB9KTct3z
+iana.org.               3590    IN      DNSKEY  257 3 8 AwEAAcH7hQju6hH3/nR6ujejl/RqPhdLrUedTkMpLZnB8ix7stfQrdhrUKlm9nOVDjW026P+PEnNLPRcXDL8uRTnlydLleVQGzHVZi9TxzObvxgolxVTPoSVCOzO39n2N6amlbk9Dh4WpaSvaRp/pYf3/+2DZ4o33tRLPEVJyHAmVBxosg3PXvUeIkgVhjdPcrAxo9Vi12aLMjNTas14KF3SKDhOIZ5bbmDzM70CS2E9+bjsqoi4rpd/R9IibX6QkP+J03yN64OGkQOBvB1Ua6gBnEToZs0Gzo7Cl4IjIKKiEiJdYGIu50BNZcZ9N1eHvWs2NCGsbNQtkUHHaH3nxXYWY6k=
+iana.org.               3590    IN      RRSIG   DNSKEY 8 2 3600 20140508081723 20140430202641 6730 iana.org. I78phbhooeI7m16A44lG/d929T++fxoGoC6u3V0Rbp4b5xa+ivrQ0/iGOHlm4LdRHxvlgf0sirqYxZMHFrout9jOwkYv8DWs1gLFvxv62yFTGceH06RGivIbvdtjwZD75K7tWpysF+TlA+2ryZ4Y5rtHljtajup24M6SmX/sWM9D6TCeu9jeAmzhIrsCFclsTQtd3zurkO1/uswpKI0QPDD5y1ivAV3CG9XNwF70wSUeUEyXx1sTQu/2MON9iDMciaG3HBUgfcKEvsykSBj8AN7a/ECSy4uQzjhZ7jjCRx29eBjRihR/Z+19d/iAN2ZXj7NtxyDbtIMXTQUZEWbYcQ==
+iana.org.               3590    IN      NSEC    036e733104617574680469616e61036f726700000762018008000380
+iana.org.               3590    IN      RRSIG   NSEC 8 2 3600 20140508051838 20140430202641 35620 iana.org. pw7hhfAkxe+3ZqXPtCsZUy94LH9JyoQV+XjTgNYLupdxCUjczrwytkZP9YjQDzU54XeHY6oPIYy0jtZMhnH+QZ+aA/NhmceEfaEft7sKUv739hInXu5tCXf8VA0w6ZNYx//p1preqNKYlTKOOca18aZYAThGk+Cw4rNG01sY5GQ=
+
+;;; ERROR: Diff Response differs
+;; - iana.org.               3590    IN      NSEC    036e733104617574680469616e61036f726700000762018008000380

dnslib/test_decode.py

 # -*- coding: utf-8 -*-
+"""
+    Test dnslib packet encoding/decoding
+
+    Reads test files from dnslib/test (by default) containing 
+    dump of DNS exchange (packet dump & parse output) and test 
+    roundtrip parsing - specifically:
+
+        - Parse packet data and zone format data and compare
+        - Repack parsed packet data and compare with original
+
+    This should test the 'parse', 'fromZone' and 'pack' methods of the
+    associated record types.
+
+    The original parsed output is created using dnslib by default so systematic
+    encode/decode errors will not be found. The test data is normally checked
+    against 'dig' to ensure that it is correct when generated using the --new
+    option. 
+
+    By default the module runs in 'unittest' mode (and supports unittest
+    --verbose/--failfast options)
+
+    The module can also be run in interactive mode (--interactive) and inspect
+    failed tests (--debug) 
+
+    New test data files can be automatically created using the:
+    
+        --new <domain> <type>
+
+    option. The data is checked against dig output and an error raised if
+    this does not match. This is effectively the same as running:
+
+        python -m dnslib.client --query --hex --dig <domain> <type> 
+
+    It is possible to manually generate test data files using dnslib.client
+    even if the dig data doesn't match (this is usually due to an unsupported
+    RDATA type which dnslib will output in hex rather then parsing contents).
+    The roundtrip tests will still work in this case (the unknown RDATA is
+    handled as an opaque blob). 
+
+    Note - unittests are dynamically generated from the test directory contents
+    (matched against the --glob parameter) 
+
+"""
 
 from __future__ import print_function
 
 from dnslib.dns import DNSRecord
 from dnslib.digparser import DigParser
 
-import argparse,binascii,code,glob,os,os.path
+import argparse,binascii,code,glob,os,os.path,sys,unittest
+
+try:
+    from subprocess import getoutput
+except ImportError:
+    from commands import getoutput
 
 try: 
     input = raw_input
 except NameError: 
     pass
 
+class TestContainer(unittest.TestCase):
+    pass
+
 def new_test(domain,qtype,address="8.8.8.8",port=53):
     tcp = False
     q = DNSRecord.question(domain,qtype)
         a_pkt = q.send(address,port,tcp)
         a = DNSRecord.parse(a_pkt)
 
-    print("Writing test file: %s-%s" % (domain,qtype))
-    f = open("%s-%s" % (domain,qtype),"w")
+    dig = getoutput("dig +qr -p %d %s %s @%s" % (
+                        port, domain, qtype, address))
+    dig_reply = list(iter(DigParser(dig)))
+    # DiG might have retried in TCP mode so get last q/a
+    q_dig = dig_reply[-2]
+    a_dig = dig_reply[-1]
 
-    print(";; Sending:",file=f)
-    print(";; QUERY:",binascii.hexlify(q.pack()).decode(),file=f)
-    print(q,file=f)
-    print(file=f)
-    print(";; Got answer:",file=f)
-    print(";; RESPONSE:",binascii.hexlify(a_pkt).decode(),file=f)
-    print(a,file=f)
-    print(file=f)
+    if q != q_dig or a != a_dig:
+        if q != q_dig:
+            print(";;; ERROR: Diff Question differs")
+            for (d1,d2) in q.diff(q_dig):
+                if d1:
+                    print(";; - %s" % d1)
+                if d2:
+                    print(";; + %s" % d2)
+        if a != a_dig:
+            print(";;; ERROR: Diff Response differs")
+            for (d1,d2) in a.diff(a_dig):
+                if d1:
+                    print(";; - %s" % d1)
+                if d2:
+                    print(";; + %s" % d2)
+    else:
+        print("Writing test file: %s-%s" % (domain,qtype))
+        with open("%s-%s" % (domain,qtype),"w") as f:
+            print(";; Sending:",file=f)
+            print(";; QUERY:",binascii.hexlify(q.pack()).decode(),file=f)
+            print(q,file=f)
+            print(file=f)
+            print(";; Got answer:",file=f)
+            print(";; RESPONSE:",binascii.hexlify(a_pkt).decode(),file=f)
+            print(a,file=f)
+            print(file=f)
 
-    f.close()
+def check_decode(f,debug=False):
+    errors = []
+
+    # Parse the q/a records
+    with open(f) as x:
+        q,r = DigParser(x)
+
+    # Grab the hex data
+    with open(f,'rb') as x:
+        for l in x.readlines():
+            if l.startswith(b';; QUERY:'):
+                qdata = binascii.unhexlify(l.split()[-1])
+            elif l.startswith(b';; RESPONSE:'):
+                rdata = binascii.unhexlify(l.split()[-1])
+
+    # Parse the hex data
+    qparse = DNSRecord.parse(qdata)
+    rparse = DNSRecord.parse(rdata)
+
+    # Check records generated from DiG input matches
+    # records parsed from packet data
+    if q != qparse:
+        errors.append(('Question',q.diff(qparse)))
+    if r != rparse:
+        errors.append(('Reply',r.diff(rparse)))
+
+    # Repack the data 
+    qpack = qparse.pack()
+    rpack = rparse.pack()
+
+    # Check if repacked question data matches original 
+    # We occasionally get issues where original packet did not 
+    # compress all labels - in this case we reparse packed
+    # record, repack this and compare with the packed data
+    if qpack != qdata:
+        if len(qpack) < len(qdata):
+            # Shorter - possibly compression difference
+            if DNSRecord.parse(qpack).pack() != qpack:
+                errors.append(('Question Pack',(qdata,qpack)))
+        else:
+            errors.append(('Question Pack',(qdata,qpack)))
+    if rpack != rdata:
+        if len(rpack) < len(rdata):
+            if DNSRecord.parse(rpack).pack() != rpack:
+                errors.append(('Reply Pack',(rdata,rpack)))
+        else:
+            errors.append(('Reply Pack',(rdata,rpack)))
+
+    if debug:
+        if errors:
+            print("ERROR\n")
+            print_errors(errors)
+            print()
+            if input(">>> Inspect [y/n]? ").lower().startswith('y'):
+                code.interact(local=locals())
+            print()
+        else:
+            print("OK")
+
+    return errors
+
+def print_errors(errors):
+    for err,err_data in errors:
+        if err == 'Question':
+            print("Question error:")
+            for (d1,d2) in err_data:
+                if d1:
+                    print(";; - %s" % d1)
+                if d2:
+                    print(";; + %s" % d2)
+        elif err == 'Reply':
+            print("Reply error:")
+            for (d1,d2) in err_data:
+                if d1:
+                    print(";; - %s" % d1)
+                if d2:
+                    print(";; + %s" % d2)
+        elif err == 'Question Pack':
+            print("Question pack error")
+            print("QDATA:",binascii.hexlify(err_data[0]))
+            print(DNSRecord.parse(err_data[0]))
+            print("QPACK:",binascii.hexlify(err_data[1]))
+            print(DNSRecord.parse(err_data[1]))
+        elif err == 'Reply Pack':
+            print("Response pack error")
+            print("RDATA:",binascii.hexlify(err_data[0]))
+            print(DNSRecord.parse(err_data[0]))
+            print("RPACK:",binascii.hexlify(err_data[1]))
+            print(DNSRecord.parse(err_data[1]))
+
+def test_generator(f):
+    def test(self):
+        self.assertEqual(check_decode(f),[])
+    return test
 
 if __name__ == '__main__':
 
 
     p = argparse.ArgumentParser(description="Test Decode")
     p.add_argument("--new","-n",nargs=2,
-                    help="New test <domain> <type>")
+                    metavar="<domain/type>",
+                    help="Create new test case (args: <domain> <type>)")
+    p.add_argument("--unittest",action='store_true',default=True,
+                    help="Run unit tests")
+    p.add_argument("--verbose",action='store_true',default=False,
+                    help="Verbose unit test output")
+    p.add_argument("--failfast",action='store_true',default=False,
+                    help="Stop unit tests on first failure")
+    p.add_argument("--interactive",action='store_true',default=False,
+                    help="Run in interactive mode")
+    p.add_argument("--debug",action='store_true',default=False,
+                    help="Debug errors (interactive mode)")
     p.add_argument("--glob","-g",default="*",
                     help="Glob pattern")
     p.add_argument("--testdir","-t",default=testdir,
                     help="Test dir (%s)" % testdir)
-    p.add_argument("--debug",action='store_true',default=False,
-                    help="Debug (DiG parser)")
     args = p.parse_args()
 
     os.chdir(args.testdir)
 
     if args.new:
         new_test(*args.new)
-    else:
+    elif args.interactive:
         for f in glob.iglob(args.glob):
-            errors = 0
-            print("\n",f,"\n","-"*len(f),sep='')
-            # Parse the q/a records
-            q,r = DigParser(open(f),args.debug)
-            # Grab the hex data
-            for l in open(f,'rb').readlines():
-                if l.startswith(b';; QUERY:'):
-                    qdata = binascii.unhexlify(l.split()[-1])
-                elif l.startswith(b';; RESPONSE:'):
-                    rdata = binascii.unhexlify(l.split()[-1])
-            qparse = DNSRecord.parse(qdata)
-            rparse = DNSRecord.parse(rdata)
-            qpack = qparse.pack()
-            rpack = rparse.pack()
+            print("-- %s: " % f,end='')
+            e = check_decode(f,args.debug)
+            if not args.debug:
+                if e:
+                    print("ERROR\n")
+                    print_errors(e)
+                    print()
+                else:
+                    print("OK")
+    elif args.unittest:
+        for f in glob.iglob(args.glob):
+            test_name = 'test_%s' % f
+            test = test_generator(f)
+            setattr(TestContainer,test_name,test)
+        unittest.main(argv=[__name__],
+                      verbosity=2 if args.verbose else 1,
+                      failfast=args.failfast)
 
-            # Check records generated from DiG input matches
-            # records parsed from packet data
-            if q != qparse:
-                errors += 1
-                print("Question error:")
-                for (d1,d2) in q.diff(qparse):
-                    if d1:
-                        print(";; - %s" % d1)
-                    if d2:
-                        print(";; + %s" % d2)
-            if r != rparse:
-                errors += 1
-                print("Reply error:")
-                for (d1,d2) in r.diff(rparse):
-                    if d1:
-                        print(";; - %s" % d1)
-                    if d2:
-                        print(";; + %s" % d2)
 
-            # Check if repacked question data matches original 
-            # We occasionally get issues where original packet did not 
-            # compress all labels - in this case we reparse packed
-            # record, repack this and compare with the packed data
-            if qpack != qdata and DNSRecord.parse(qpack).pack() != qpack:
-                errors += 1
-                print("Question repack error")
-                print("QDATA:",binascii.hexlify(qdata))
-                print(qparse)
-                print("QPACK:",binascii.hexlify(qpack))
-                print(DNSRecord.parse(qpack))
-            # Same for reply
-            if rpack != rdata and DNSRecord.parse(rpack).pack() != rpack:
-                errors += 1
-                print("Response repack error")
-                print("RDATA:",binascii.hexlify(rdata))
-                print(rparse)
-                print("RPACK:",binascii.hexlify(rpack))
-                print(DNSRecord.parse(rpack))
 
-            if errors == 0:
-                print("OK")
-            elif input("Inspect [y/n]?").lower().startswith('y'):
-                code.interact(local=locals())
-
-

dnslib/test_unpack.py

-# -*- coding: utf-8 -*-
-
-from __future__ import print_function
-
-import binascii
-
-from dnslib import DNSRecord
-
-def test_unpack(s):
-    """
-    Test decoding with sample DNS packets captured from server/udp_proxy.py
-
-    >>> def _unpack(s):
-    ...     return  DNSRecord.parse(binascii.unhexlify(s))
-
-    >>> def _dump(s):
-    ...     r = _unpack(s)
-    ...     print(repr(r))
-
-    >>> def _dumpzone(s):
-    ...     r = _unpack(s)
-    ...     print(r)
-
-    Standard query A www.google.com
-
-        >>> p = b'd5ad010000010000000000000377777706676f6f676c6503636f6d0000010001'
-        >>> _dump(p)
-        <DNS Header: id=0xd5ad type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
-        <DNS Question: 'www.google.com.' qtype=A qclass=IN>
-        
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54701
-        ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;www.google.com.                IN      A
-
-    Standard query response CNAME www.l.google.com A 66.249.91.104 A 66.249.91.99 A 66.249.91.103 A 66.249.91.147
-
-        >>> p = b'd5ad818000010005000000000377777706676f6f676c6503636f6d0000010001c00c0005000100000005000803777777016cc010c02c0001000100000005000442f95b68c02c0001000100000005000442f95b63c02c0001000100000005000442f95b67c02c0001000100000005000442f95b93'
-        >>> _dump(p)
-        <DNS Header: id=0xd5ad type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=5 ns=0 ar=0>
-        <DNS Question: 'www.google.com.' qtype=A qclass=IN>
-        <DNS RR: 'www.google.com.' rtype=CNAME rclass=IN ttl=5 rdata='www.l.google.com.'>
-        <DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.104'>
-        <DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.99'>
-        <DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.103'>
-        <DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.147'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54701
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;www.google.com.                IN      A
-        ;; ANSWER SECTION:
-        www.google.com.         5       IN      CNAME   www.l.google.com.
-        www.l.google.com.       5       IN      A       66.249.91.104
-        www.l.google.com.       5       IN      A       66.249.91.99
-        www.l.google.com.       5       IN      A       66.249.91.103
-        www.l.google.com.       5       IN      A       66.249.91.147
-
-    Standard query MX google.com
-
-        >>> p = b'95370100000100000000000006676f6f676c6503636f6d00000f0001'
-        >>> _dump(p)
-        <DNS Header: id=0x9537 type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
-        <DNS Question: 'google.com.' qtype=MX qclass=IN>
-            
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38199
-        ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;google.com.                    IN      MX
-
-    Standard query response MX 10 smtp2.google.com MX 10 smtp3.google.com MX 10 smtp4.google.com MX 10 smtp1.google.com
-
-        >>> p = b'95378180000100040000000006676f6f676c6503636f6d00000f0001c00c000f000100000005000a000a05736d747032c00cc00c000f000100000005000a000a05736d747033c00cc00c000f000100000005000a000a05736d747034c00cc00c000f000100000005000a000a05736d747031c00c'
-        >>> _dump(p)
-        <DNS Header: id=0x9537 type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=4 ns=0 ar=0>
-        <DNS Question: 'google.com.' qtype=MX qclass=IN>
-        <DNS RR: 'google.com.' rtype=MX rclass=IN ttl=5 rdata='10 smtp2.google.com.'>
-        <DNS RR: 'google.com.' rtype=MX rclass=IN ttl=5 rdata='10 smtp3.google.com.'>
-        <DNS RR: 'google.com.' rtype=MX rclass=IN ttl=5 rdata='10 smtp4.google.com.'>
-        <DNS RR: 'google.com.' rtype=MX rclass=IN ttl=5 rdata='10 smtp1.google.com.'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38199
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;google.com.                    IN      MX
-        ;; ANSWER SECTION:
-        google.com.             5       IN      MX      10 smtp2.google.com.
-        google.com.             5       IN      MX      10 smtp3.google.com.
-        google.com.             5       IN      MX      10 smtp4.google.com.
-        google.com.             5       IN      MX      10 smtp1.google.com.
-
-    Standard query PTR 103.91.249.66.in-addr.arpa
-
-        >>> p = (b'b38001000001000000000000033130330239310332343902363607696e2d61646472046172706100000c0001')
-        >>> _dump(p)
-        <DNS Header: id=0xb380 type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
-        <DNS Question: '103.91.249.66.in-addr.arpa.' qtype=PTR qclass=IN>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45952
-        ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;103.91.249.66.in-addr.arpa.    IN      PTR
-
-    Standard query response PTR ik-in-f103.google.com
-
-        >>> p = (b'b38081800001000100000000033130330239310332343902363607696e2d61646472046172706100000c0001c00c000c00010000000500170a696b2d696e2d6631303306676f6f676c6503636f6d00')
-        >>> _dump(p)
-        <DNS Header: id=0xb380 type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=1 ns=0 ar=0>
-        <DNS Question: '103.91.249.66.in-addr.arpa.' qtype=PTR qclass=IN>
-        <DNS RR: '103.91.249.66.in-addr.arpa.' rtype=PTR rclass=IN ttl=5 rdata='ik-in-f103.google.com.'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45952
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;103.91.249.66.in-addr.arpa.    IN      PTR
-        ;; ANSWER SECTION:
-        103.91.249.66.in-addr.arpa. 5       IN      PTR     ik-in-f103.google.com.
-
-    Standard query TXT google.com
-
-        >>> p = (b'c89f0100000100000000000006676f6f676c6503636f6d0000100001')
-        >>> _dump(p)
-        <DNS Header: id=0xc89f type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
-        <DNS Question: 'google.com.' qtype=TXT qclass=IN>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51359
-        ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;google.com.                    IN      TXT
-
-    Standard query response TXT
-
-        >>> p = (b'c89f8180000100010000000006676f6f676c6503636f6d0000100001c00c0010000100000005002a29763d7370663120696e636c7564653a5f6e6574626c6f636b732e676f6f676c652e636f6d207e616c6c')
-        >>> _dump(p)
-        <DNS Header: id=0xc89f type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=1 ns=0 ar=0>
-        <DNS Question: 'google.com.' qtype=TXT qclass=IN>
-        <DNS RR: 'google.com.' rtype=TXT rclass=IN ttl=5 rdata='v=spf1 include:_netblocks.google.com ~all'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51359
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;google.com.                    IN      TXT
-        ;; ANSWER SECTION:
-        google.com.             5       IN      TXT     "v=spf1 include:_netblocks.google.com ~all"
-
-    Standard query SOA google.com
-
-        >>> p = (b'28fb0100000100000000000006676f6f676c6503636f6d0000060001')
-        >>> _dump(p)
-        <DNS Header: id=0x28fb type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
-        <DNS Question: 'google.com.' qtype=SOA qclass=IN>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10491
-        ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;google.com.                    IN      SOA
-
-    Standard query response SOA ns1.google.com
-
-        >>> p = (b'28fb8180000100010000000006676f6f676c6503636f6d0000060001c00c00060001000000050026036e7331c00c09646e732d61646d696ec00c77b1566d00001c2000000708001275000000012c')
-        >>> _dump(p)
-        <DNS Header: id=0x28fb type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=1 ns=0 ar=0>
-        <DNS Question: 'google.com.' qtype=SOA qclass=IN>
-        <DNS RR: 'google.com.' rtype=SOA rclass=IN ttl=5 rdata='ns1.google.com. dns-admin.google.com. 2008110701 7200 1800 1209600 300'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10491
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;google.com.                    IN      SOA
-        ;; ANSWER SECTION:
-        google.com.             5       IN      SOA     ns1.google.com. dns-admin.google.com. 2008110701 7200 1800 1209600 300
-
-    Standard query response NAPTR sip2sip.info
-
-        >>> p = (b'740481800001000300000000077369703273697004696e666f0000230001c00c0023000100000c940027001e00640173075349502b44325500045f736970045f756470077369703273697004696e666f00c00c0023000100000c940027000a00640173075349502b44325400045f736970045f746370077369703273697004696e666f00c00c0023000100000c94002900140064017308534950532b44325400055f73697073045f746370077369703273697004696e666f00')
-        >>> _dump(p)
-        <DNS Header: id=0x7404 type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=3 ns=0 ar=0>
-        <DNS Question: 'sip2sip.info.' qtype=NAPTR qclass=IN>
-        <DNS RR: 'sip2sip.info.' rtype=NAPTR rclass=IN ttl=3220 rdata='30 100 "s" "SIP+D2U" "" _sip._udp.sip2sip.info.'>
-        <DNS RR: 'sip2sip.info.' rtype=NAPTR rclass=IN ttl=3220 rdata='10 100 "s" "SIP+D2T" "" _sip._tcp.sip2sip.info.'>
-        <DNS RR: 'sip2sip.info.' rtype=NAPTR rclass=IN ttl=3220 rdata='20 100 "s" "SIPS+D2T" "" _sips._tcp.sip2sip.info.'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29700
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;sip2sip.info.                  IN      NAPTR
-        ;; ANSWER SECTION:
-        sip2sip.info.           3220    IN      NAPTR   30 100 "s" "SIP+D2U" "" _sip._udp.sip2sip.info.
-        sip2sip.info.           3220    IN      NAPTR   10 100 "s" "SIP+D2T" "" _sip._tcp.sip2sip.info.
-        sip2sip.info.           3220    IN      NAPTR   20 100 "s" "SIPS+D2T" "" _sips._tcp.sip2sip.info.
-
-    Standard query response NAPTR 0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org
-
-        >>> p = (b'aef0818000010001000000000130013001300130013101310131013301390133013001310138013701380465313634036f72670000230001c00c002300010000a6a300320064000a0175074532552b53495022215e5c2b3f282e2a2924217369703a5c5c31406677642e70756c7665722e636f6d2100')
-        >>> _dump(p)
-        <DNS Header: id=0xaef0 type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=1 ns=0 ar=0>
-        <DNS Question: '0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org.' qtype=NAPTR qclass=IN>
-        <DNS RR: '0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org.' rtype=NAPTR rclass=IN ttl=42659 rdata='100 10 "u" "E2U+SIP" "!^\\\\+?(.*)$!sip:\\\\\\\\1@fwd.pulver.com!" .'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44784
-        ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-        ;; QUESTION SECTION:
-        ;0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org. IN      NAPTR
-        ;; ANSWER SECTION:
-        0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org. 42659   IN      NAPTR   100 10 "u" "E2U+SIP" "!^\\\\+?(.*)$!sip:\\\\\\\\1@fwd.pulver.com!" .
-
-    EDNS0 OPT record 
-
-        ** Not sure if this is right but don't have any other sample data **
-
-        >>> p = (b'896f010000010000000000010661613332343703636f6d0000010001000029100000000000000c50fa000800012000d99f29cf')
-        >>> _dump(p)
-        <DNS Header: id=0x896f type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=1>
-        <DNS Question: 'aa3247.com.' qtype=A qclass=IN>
-        <DNS OPT: edns_ver=0 do=0 ext_rcode=0 udp_len=4096>
-        <EDNS Option: Code=20730 Data='00012000d99f29cf'>
-
-        >>> _dumpzone(p)
-        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35183
-        ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
-        ;; QUESTION SECTION:
-        ;aa3247.com.                    IN      A
-        ;; ADDITIONAL SECTION:
-        ;OPT PSEUDOSECTION
-        ;EDNS: version: 0, flags: ; udp: 4096
-        ;EDNS: code: 20730; data: 00012000d99f29cf
-
-    """
-    pass
-
-if __name__ == '__main__':
-    import doctest
-    doctest.testmod(optionflags=doctest.ELLIPSIS)
+#!/usr/bin/env python3
 
 from __future__ import print_function
 
 
 : ${VERSIONS:="python python3"}
 
-for src in __init__.py bimap.py bit.py buffer.py label.py dns.py lex.py server.py test_unpack.py
+for src in __init__.py bimap.py bit.py buffer.py label.py dns.py lex.py server.py test_decode.py
 do
 	echo "===" $src
 	for py in $VERSIONS