1# -*- encoding: utf-8 -*- 2""" 3 $Id$ 4 5 This file is part of the py3dns project. 6 Homepage: https://launchpad.net/py3dns 7 8 This code is covered by the standard Python License. See LICENSE for details. 9 10Changes for Python3 port © 2011-13 Scott Kitterman <scott@kitterman.com> 11 12 Library code. Largely this is packers and unpackers for various types. 13""" 14 15# 16# 17# See RFC 1035: 18# ------------------------------------------------------------------------ 19# Network Working Group P. Mockapetris 20# Request for Comments: 1035 ISI 21# November 1987 22# Obsoletes: RFCs 882, 883, 973 23# 24# DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION 25# ------------------------------------------------------------------------ 26 27 28import types 29import socket 30 31from . import Type 32from . import Class 33from . import Opcode 34from . import Status 35import DNS 36 37from .Base import DNSError 38 39try: 40 import ipaddress 41except ImportError: 42 import ipaddr as ipaddress 43 44LABEL_UTF8 = False 45LABEL_ENCODING = 'idna' 46 47class UnpackError(DNSError): pass 48class PackError(DNSError): pass 49 50# Low-level 16 and 32 bit integer packing and unpacking 51 52from struct import pack as struct_pack 53from struct import unpack as struct_unpack 54from socket import inet_ntoa, inet_aton, inet_ntop, AF_INET6 55 56def pack16bit(n): 57 return struct_pack('!H', n) 58 59def pack32bit(n): 60 return struct_pack('!L', n) 61 62def unpack16bit(s): 63 return struct_unpack('!H', s)[0] 64 65def unpack32bit(s): 66 return struct_unpack('!L', s)[0] 67 68def addr2bin(addr): 69 # Updated from pyspf 70 """Convert a string IPv4 address into an unsigned integer. 71 72 Examples:: 73 >>> addr2bin('127.0.0.1') 74 2130706433 75 76 >>> addr2bin('127.0.0.1') == socket.INADDR_LOOPBACK 77 1 78 79 >>> addr2bin('255.255.255.254') 80 4294967294L 81 82 >>> addr2bin('192.168.0.1') 83 3232235521L 84 85 Unlike old DNS.addr2bin, the n, n.n, and n.n.n forms for IP addresses 86 are handled as well:: 87 >>> addr2bin('10.65536') 88 167837696 89 >>> 10 * (2 ** 24) + 65536 90 167837696 91 92 >>> addr2bin('10.93.512') 93 173867520 94 >>> 10 * (2 ** 24) + 93 * (2 ** 16) + 512 95 173867520 96 """ 97 return struct_unpack("!L", inet_aton(addr))[0] 98 99def bin2addr(n): 100 return inet_ntoa(struct_pack('!L', n)) 101 102def bin2addr6(n): 103 return inet_ntop(AF_INET6, n) 104 105def bin2long6(str): 106 # Also from pyspf 107 h, l = struct_unpack("!QQ", str) 108 return h << 64 | l 109 110# Packing class 111 112class Packer: 113 " packer base class. supports basic byte/16bit/32bit/addr/string/name " 114 def __init__(self): 115 if DNS.LABEL_UTF8: 116 enc = 'utf8' 117 else: 118 enc = DNS.LABEL_ENCODING 119 self.buf = bytes('', enc) 120 self.index = {} 121 def getbuf(self): 122 return self.buf 123 def addbyte(self, c): 124 if len(c) != 1: raise TypeError('one character expected') 125 if DNS.LABEL_UTF8: 126 enc = 'utf8' 127 else: 128 enc = DNS.LABEL_ENCODING 129 self.buf = self.buf + bytes(c,enc) 130 def addbytes(self, abytes): 131 if DNS.LABEL_UTF8: 132 enc = 'utf8' 133 else: 134 enc = DNS.LABEL_ENCODING 135 self.buf = self.buf + bytes(abytes, enc) 136 def add16bit(self, n): 137 self.buf = self.buf + bytes(pack16bit(n)) 138 def add32bit(self, n): 139 self.buf = self.buf + bytes(pack32bit(n)) 140 def addaddr(self, addr): 141 n = addr2bin(addr) 142 self.buf = self.buf + bytes(pack32bit(n)) 143 def addstring(self, s): 144 if len(s) > 255: 145 raise ValueError("Can't encode string of length "+ \ 146 "%s (> 255)"%(len(s))) 147 self.addbyte(chr(len(s))) 148 self.addbytes(s) 149 def addname(self, name): 150 # Domain name packing (section 4.1.4) 151 # Add a domain name to the buffer, possibly using pointers. 152 # The case of the first occurrence of a name is preserved. 153 # Redundant dots are ignored. 154 nlist = [] 155 for label in name.split('.'): 156 if not label: 157 pass # Passing to ignore redundant dots per comments 158 else: 159 nlist.append(label) 160 keys = [] 161 for i in range(len(nlist)): 162 key = '.'.join(nlist[i:]) 163 key = key.upper() 164 keys.append(key) 165 if key in self.index: 166 pointer = self.index[key] 167 break 168 else: 169 i = len(nlist) 170 pointer = None 171 # Do it into temporaries first so exceptions don't 172 # mess up self.index and self.buf 173 offset = len(self.buf) 174 index = [] 175 if DNS.LABEL_UTF8: 176 enc = 'utf8' 177 else: 178 enc = DNS.LABEL_ENCODING 179 buf = bytes('', enc) 180 for j in range(i): 181 label = nlist[j] 182 try: 183 label = label.encode(enc) 184 except UnicodeEncodeError: 185 if not DNS.LABEL_UTF8: raise 186 if not label.startswith('\ufeff'): 187 label = '\ufeff'+label 188 label = label.encode(enc) 189 n = len(label) 190 if n > 63: 191 raise PackError('label too long') 192 if offset + len(buf) < 0x3FFF: 193 index.append((keys[j], offset + len(buf))) 194 else: 195 print('DNS.Lib.Packer.addname:') 196 print('warning: pointer too big') 197 buf = buf + bytes([n]) + label 198 if pointer: 199 buf = buf + (pack16bit(pointer | 0xC000)) 200 else: 201 buf = buf + bytes('\0', enc) 202 self.buf = self.buf + buf 203 for key, value in index: 204 self.index[key] = value 205 def dump(self): 206 keys = list(self.index.keys()) 207 keys.sort() 208 print('-'*40) 209 for key in keys: 210 print('%20s %3d' % (key, self.index[key])) 211 print('-'*40) 212 space = 1 213 for i in range(0, len(self.buf)+1, 2): 214 if self.buf[i:i+2] == '**': 215 if not space: print() 216 space = 1 217 continue 218 space = 0 219 print('%4d' % i) 220 for c in self.buf[i:i+2]: 221 if ' ' < c < '\177': 222 print(' %c' % c) 223 else: 224 print('%2d' % ord(c)) 225 print() 226 print('-'*40) 227 228 229# Unpacking class 230 231 232class Unpacker: 233 def __init__(self, buf): 234 # buf should be binary in Python3 235 self.buf = buf 236 self.offset = 0 237 def getbyte(self): 238 if self.offset >= len(self.buf): 239 raise UnpackError("Ran off end of data") 240 c = self.buf[self.offset] 241 self.offset = self.offset + 1 242 return c 243 def getbytes(self, n): 244 s = (self.buf[self.offset : self.offset + n]) 245 if len(s) != n: raise UnpackError('not enough data left') 246 self.offset = self.offset + n 247 return s 248 def get16bit(self): 249 return unpack16bit(self.getbytes(2)) 250 def get32bit(self): 251 return unpack32bit(self.getbytes(4)) 252 def getaddr(self): 253 if DNS.LABEL_UTF8: 254 enc = 'utf8' 255 else: 256 enc = DNS.LABEL_ENCODING 257 return bytes(bin2addr(self.get32bit()),enc) 258 def getaddr6(self): 259 return (self.getbytes(16)) 260 def getstring(self): 261 return self.getbytes(self.getbyte()) 262 def getname(self): 263 # Domain name unpacking (section 4.1.4) 264 i = self.getbyte() 265 #i = ord(i) 266 if i and i & 0xC0 == 0xC0: 267 d = self.getbyte() 268 j = d 269 pointer = ((i<<8) | j) & ~0xC000 270 save_offset = self.offset 271 try: 272 self.offset = pointer 273 domain = self.getname() 274 finally: 275 self.offset = save_offset 276 return domain 277 if i == 0: 278 return '' 279 if DNS.LABEL_UTF8: 280 enc = 'utf8' 281 else: 282 enc = DNS.LABEL_ENCODING 283 domain = str(self.getbytes(i), enc) 284 remains = self.getname() 285 if not remains: 286 return domain 287 else: 288 return domain + '.' + remains 289 290# Test program for packin/unpacking (section 4.1.4) 291 292def testpacker(): 293 N = 2500 294 R = list(range(N)) 295 import timing 296 # See section 4.1.4 of RFC 1035 297 timing.start() 298 for i in R: 299 p = Packer() 300 p.addaddr('192.168.0.1') 301 p.addbytes('*' * 20) 302 p.addname('f.ISI.ARPA') 303 p.addbytes('*' * 8) 304 p.addname('Foo.F.isi.arpa') 305 p.addbytes('*' * 18) 306 p.addname('arpa') 307 p.addbytes('*' * 26) 308 p.addname('') 309 timing.finish() 310 print(timing.milli(), "ms total for packing") 311 print(round(timing.milli() / i, 4), 'ms per packing') 312 #p.dump() 313 u = Unpacker(p.buf) 314 u.getaddr() 315 u.getbytes(20) 316 u.getname() 317 u.getbytes(8) 318 u.getname() 319 u.getbytes(18) 320 u.getname() 321 u.getbytes(26) 322 u.getname() 323 timing.start() 324 for i in R: 325 u = Unpacker(p.buf) 326 327 res = (u.getaddr(), 328 u.getbytes(20), 329 u.getname(), 330 u.getbytes(8), 331 u.getname(), 332 u.getbytes(18), 333 u.getname(), 334 u.getbytes(26), 335 u.getname()) 336 timing.finish() 337 print(timing.milli(), "ms total for unpacking") 338 print(round(timing.milli() / i, 4), 'ms per unpacking') 339 #for item in res: print item 340 341 342# Pack/unpack RR toplevel format (section 3.2.1) 343 344class RRpacker(Packer): 345 def __init__(self): 346 Packer.__init__(self) 347 self.rdstart = None 348 def addRRheader(self, name, RRtype, klass, ttl, *rest): 349 self.addname(name) 350 self.add16bit(RRtype) 351 self.add16bit(klass) 352 self.add32bit(ttl) 353 if rest: 354 if rest[1:]: raise TypeError('too many args') 355 rdlength = rest[0] 356 else: 357 rdlength = 0 358 self.add16bit(rdlength) 359 self.rdstart = len(self.buf) 360 def patchrdlength(self): 361 rdlength = unpack16bit(self.buf[self.rdstart-2:self.rdstart]) 362 if rdlength == len(self.buf) - self.rdstart: 363 return 364 rdata = self.buf[self.rdstart:] 365 save_buf = self.buf 366 ok = 0 367 try: 368 self.buf = self.buf[:self.rdstart-2] 369 self.add16bit(len(rdata)) 370 self.buf = self.buf + rdata 371 ok = 1 372 finally: 373 if not ok: self.buf = save_buf 374 def endRR(self): 375 if self.rdstart is not None: 376 self.patchrdlength() 377 self.rdstart = None 378 def getbuf(self): 379 if self.rdstart is not None: self.patchrdlength() 380 return Packer.getbuf(self) 381 # Standard RRs (section 3.3) 382 def addCNAME(self, name, klass, ttl, cname): 383 self.addRRheader(name, Type.CNAME, klass, ttl) 384 self.addname(cname) 385 self.endRR() 386 def addHINFO(self, name, klass, ttl, cpu, os): 387 self.addRRheader(name, Type.HINFO, klass, ttl) 388 self.addstring(cpu) 389 self.addstring(os) 390 self.endRR() 391 def addMX(self, name, klass, ttl, preference, exchange): 392 self.addRRheader(name, Type.MX, klass, ttl) 393 self.add16bit(preference) 394 self.addname(exchange) 395 self.endRR() 396 def addNS(self, name, klass, ttl, nsdname): 397 self.addRRheader(name, Type.NS, klass, ttl) 398 self.addname(nsdname) 399 self.endRR() 400 def addPTR(self, name, klass, ttl, ptrdname): 401 self.addRRheader(name, Type.PTR, klass, ttl) 402 self.addname(ptrdname) 403 self.endRR() 404 def addSOA(self, name, klass, ttl, 405 mname, rname, serial, refresh, retry, expire, minimum): 406 self.addRRheader(name, Type.SOA, klass, ttl) 407 self.addname(mname) 408 self.addname(rname) 409 self.add32bit(serial) 410 self.add32bit(refresh) 411 self.add32bit(retry) 412 self.add32bit(expire) 413 self.add32bit(minimum) 414 self.endRR() 415 def addTXT(self, name, klass, ttl, tlist): 416 self.addRRheader(name, Type.TXT, klass, ttl) 417 if type(tlist) is bytes or type(tlist) is str: 418 tlist = [tlist] 419 for txtdata in tlist: 420 self.addstring(txtdata) 421 self.endRR() 422 def addSPF(self, name, klass, ttl, tlist): 423 self.addRRheader(name, Type.TXT, klass, ttl) 424 if type(tlist) is bytes or type(tlist) is str: 425 tlist = [tlist] 426 for txtdata in tlist: 427 self.addstring(txtdata) 428 self.endRR() 429 # Internet specific RRs (section 3.4) -- class = IN 430 def addA(self, name, klass, ttl, address): 431 self.addRRheader(name, Type.A, klass, ttl) 432 self.addaddr(address) 433 self.endRR() 434 def addWKS(self, name, ttl, address, protocol, bitmap): 435 self.addRRheader(name, Type.WKS, Class.IN, ttl) 436 self.addaddr(address) 437 self.addbyte(chr(protocol)) 438 self.addbytes(bitmap) 439 self.endRR() 440 def addSRV(self): 441 raise NotImplementedError 442 443def prettyTime(seconds): 444 if seconds<60: 445 return seconds,"%d seconds"%(seconds) 446 if seconds<3600: 447 return seconds,"%d minutes"%(seconds/60) 448 if seconds<86400: 449 return seconds,"%d hours"%(seconds/3600) 450 if seconds<604800: 451 return seconds,"%d days"%(seconds/86400) 452 else: 453 return seconds,"%d weeks"%(seconds/604800) 454 455class RRunpacker(Unpacker): 456 def __init__(self, buf): 457 Unpacker.__init__(self, buf) 458 self.rdend = None 459 def getRRheader(self): 460 name = self.getname() 461 rrtype = self.get16bit() 462 klass = self.get16bit() 463 ttl = self.get32bit() 464 rdlength = self.get16bit() 465 self.rdend = self.offset + rdlength 466 return (name, rrtype, klass, ttl, rdlength) 467 def endRR(self): 468 if self.offset != self.rdend: 469 raise UnpackError('end of RR not reached') 470 def getCNAMEdata(self): 471 return self.getname() 472 def getHINFOdata(self): 473 if DNS.LABEL_UTF8: 474 enc = 'utf8' 475 else: 476 enc = DNS.LABEL_ENCODING 477 return str(self.getstring(), enc), str(self.getstring(),enc) 478 def getMXdata(self): 479 return self.get16bit(), self.getname() 480 def getNSdata(self): 481 return self.getname() 482 def getPTRdata(self): 483 return self.getname() 484 def getSOAdata(self): 485 return self.getname(), \ 486 self.getname(), \ 487 ('serial',)+(self.get32bit(),), \ 488 ('refresh ',)+prettyTime(self.get32bit()), \ 489 ('retry',)+prettyTime(self.get32bit()), \ 490 ('expire',)+prettyTime(self.get32bit()), \ 491 ('minimum',)+prettyTime(self.get32bit()) 492 def getTXTdata(self): 493 tlist = [] 494 while self.offset != self.rdend: 495 tlist.append(bytes(self.getstring())) 496 return tlist 497 getSPFdata = getTXTdata 498 def getAdata(self): 499 if DNS.LABEL_UTF8: 500 enc = 'utf8' 501 else: 502 enc = DNS.LABEL_ENCODING 503 return self.getaddr().decode(enc) 504 def getWKSdata(self): 505 address = self.getaddr() 506 protocol = ord(self.getbyte()) 507 bitmap = self.getbytes(self.rdend - self.offset) 508 return address, protocol, bitmap 509 def getSRVdata(self): 510 """ 511 _Service._Proto.Name TTL Class SRV Priority Weight Port Target 512 """ 513 priority = self.get16bit() 514 weight = self.get16bit() 515 port = self.get16bit() 516 target = self.getname() 517 #print '***priority, weight, port, target', priority, weight, port, target 518 return priority, weight, port, target 519 520class RRunpackerDefault(RRunpacker): 521 # Default for DNS.qry 522 def __init__(self, buf): 523 RRunpacker.__init__(self, buf) 524 self.rdend = None 525 def getAdata(self): 526 if DNS.LABEL_UTF8: 527 enc = 'utf8' 528 else: 529 enc = DNS.LABEL_ENCODING 530 x = socket.inet_aton(self.getaddr().decode(enc)) 531 return ipaddress.IPv4Address(struct_unpack("!I", x)[0]) 532 def getAAAAdata(self): 533 return ipaddress.IPv6Address(bin2addr6(self.getaddr6())) 534 535class RRunpackerText(RRunpackerDefault): 536 def __init__(self, buf): 537 RRunpackerDefault.__init__(self, buf) 538 def getAdata(self): 539 if DNS.LABEL_UTF8: 540 enc = 'utf8' 541 else: 542 enc = DNS.LABEL_ENCODING 543 return self.getaddr().decode(enc) 544 def getAAAAdata(self): 545 return bin2addr6(self.getaddr6()) 546 def getTXTdata(self): 547 if DNS.LABEL_UTF8: 548 enc = 'utf8' 549 else: 550 enc = DNS.LABEL_ENCODING 551 tlist = [] 552 while self.offset != self.rdend: 553 tlist.append(str(self.getstring(), enc)) 554 return tlist 555 556class RRunpackerInteger(RRunpackerDefault): 557 def __init__(self, buf): 558 RRunpackerDefault.__init__(self, buf) 559 def getAdata(self): 560 if DNS.LABEL_UTF8: 561 enc = 'utf8' 562 else: 563 enc = DNS.LABEL_ENCODING 564 x = socket.inet_aton(self.getaddr().decode(enc)) 565 return struct_unpack("!I", x)[0] 566 def getAAAAdata(self): 567 return bin2long6(self.getaddr6()) 568 569class RRunpackerBinary(Unpacker): 570 def __init__(self, buf): 571 Unpacker.__init__(self, buf) 572 self.rdend = None 573 def getRRheader(self): 574 name = self.getname() 575 rrtype = self.get16bit() 576 klass = self.get16bit() 577 ttl = self.get32bit() 578 rdlength = self.get16bit() 579 self.rdlength = rdlength 580 self.rdend = self.offset + rdlength 581 return (name, rrtype, klass, ttl, rdlength) 582 def endRR(self): 583 if self.offset != self.rdend: 584 raise UnpackError('end of RR not reached') 585 def getTXTdata(self): 586 tlist = [] 587 while self.offset != self.rdend: 588 tlist.append(self.getbytes(self.rdlength)) 589 return tlist 590 getSPFdata = getTXTdata 591 592# Pack/unpack Message Header (section 4.1) 593 594class Hpacker(Packer): 595 def addHeader(self, id, qr, opcode, aa, tc, rd, ra, z, rcode, 596 qdcount, ancount, nscount, arcount): 597 self.add16bit(id) 598 self.add16bit((qr&1)<<15 | (opcode&0xF)<<11 | (aa&1)<<10 599 | (tc&1)<<9 | (rd&1)<<8 | (ra&1)<<7 600 | (z&7)<<4 | (rcode&0xF)) 601 self.add16bit(qdcount) 602 self.add16bit(ancount) 603 self.add16bit(nscount) 604 self.add16bit(arcount) 605 606class Hunpacker(Unpacker): 607 def getHeader(self): 608 id = self.get16bit() 609 flags = self.get16bit() 610 qr, opcode, aa, tc, rd, ra, z, rcode = ( 611 (flags>>15)&1, 612 (flags>>11)&0xF, 613 (flags>>10)&1, 614 (flags>>9)&1, 615 (flags>>8)&1, 616 (flags>>7)&1, 617 (flags>>4)&7, 618 (flags>>0)&0xF) 619 qdcount = self.get16bit() 620 ancount = self.get16bit() 621 nscount = self.get16bit() 622 arcount = self.get16bit() 623 return (id, qr, opcode, aa, tc, rd, ra, z, rcode, 624 qdcount, ancount, nscount, arcount) 625 626 627# Pack/unpack Question (section 4.1.2) 628 629class Qpacker(Packer): 630 def addQuestion(self, qname, qtype, qclass): 631 self.addname(qname) 632 self.add16bit(qtype) 633 self.add16bit(qclass) 634 635class Qunpacker(Unpacker): 636 def getQuestion(self): 637 return self.getname(), self.get16bit(), self.get16bit() 638 639 640# Pack/unpack Message(section 4) 641# NB the order of the base classes is important for __init__()! 642 643class Mpacker(RRpacker, Qpacker, Hpacker): 644 pass 645 646class Munpacker(RRunpacker, Qunpacker, Hunpacker): 647 # Default results for DNS.req 648 pass 649 650class MunpackerDefault(RRunpackerDefault, Qunpacker, Hunpacker): 651 # Default results for DNS.qry 652 pass 653 654class MunpackerText(RRunpackerText, Qunpacker, Hunpacker): 655 pass 656 657class MunpackerBinary(RRunpackerBinary, Qunpacker, Hunpacker): 658 pass 659 660class MunpackerInteger(RRunpackerInteger, Qunpacker, Hunpacker): 661 pass 662 663# Routines to print an unpacker to stdout, for debugging. 664# These affect the unpacker's current position! 665 666def dumpM(u): 667 print('HEADER:') 668 (id, qr, opcode, aa, tc, rd, ra, z, rcode, 669 qdcount, ancount, nscount, arcount) = u.getHeader() 670 print('id=%d,' % id) 671 print('qr=%d, opcode=%d, aa=%d, tc=%d, rd=%d, ra=%d, z=%d, rcode=%d,' \ 672 % (qr, opcode, aa, tc, rd, ra, z, rcode)) 673 if tc: print('*** response truncated! ***') 674 if rcode: print('*** nonzero error code! (%d) ***' % rcode) 675 print(' qdcount=%d, ancount=%d, nscount=%d, arcount=%d' \ 676 % (qdcount, ancount, nscount, arcount)) 677 for i in range(qdcount): 678 print('QUESTION %d:' % i) 679 dumpQ(u) 680 for i in range(ancount): 681 print('ANSWER %d:' % i) 682 dumpRR(u) 683 for i in range(nscount): 684 print('AUTHORITY RECORD %d:' % i) 685 dumpRR(u) 686 for i in range(arcount): 687 print('ADDITIONAL RECORD %d:' % i) 688 dumpRR(u) 689 690class DnsResult: 691 692 def __init__(self,u,args): 693 self.header={} 694 self.questions=[] 695 self.answers=[] 696 self.authority=[] 697 self.additional=[] 698 self.args=args 699 self.storeM(u) 700 701 def show(self): 702 import time 703 print('; <<>> PDG.py 1.0 <<>> %s %s'%(self.args['name'], 704 self.args['qtype'])) 705 opt="" 706 if self.args['rd']: 707 opt=opt+'recurs ' 708 h=self.header 709 print(';; options: '+opt) 710 print(';; got answer:') 711 print(';; ->>HEADER<<- opcode %s, status %s, id %d'%( 712 h['opcode'],h['status'],h['id'])) 713 flags=list(filter(lambda x,h=h:h[x],('qr','aa','rd','ra','tc'))) 714 print(';; flags: %s; Ques: %d, Ans: %d, Auth: %d, Addit: %d'%( 715 ' '.join(flags),h['qdcount'],h['ancount'],h['nscount'], 716 h['arcount'])) 717 print(';; QUESTIONS:') 718 for q in self.questions: 719 print(';; %s, type = %s, class = %s'%(q['qname'],q['qtypestr'], 720 q['qclassstr'])) 721 print() 722 print(';; ANSWERS:') 723 for a in self.answers: 724 print('%-20s %-6s %-6s %s'%(a['name'],repr(a['ttl']),a['typename'], 725 a['data'])) 726 print() 727 print(';; AUTHORITY RECORDS:') 728 for a in self.authority: 729 print('%-20s %-6s %-6s %s'%(a['name'],repr(a['ttl']),a['typename'], 730 a['data'])) 731 print() 732 print(';; ADDITIONAL RECORDS:') 733 for a in self.additional: 734 print('%-20s %-6s %-6s %s'%(a['name'],repr(a['ttl']),a['typename'], 735 a['data'])) 736 print() 737 if 'elapsed' in self.args: 738 print(';; Total query time: %d msec'%self.args['elapsed']) 739 print(';; To SERVER: %s'%(self.args['server'])) 740 print(';; WHEN: %s'%time.ctime(time.time())) 741 742 def storeM(self,u): 743 (self.header['id'], self.header['qr'], self.header['opcode'], 744 self.header['aa'], self.header['tc'], self.header['rd'], 745 self.header['ra'], self.header['z'], self.header['rcode'], 746 self.header['qdcount'], self.header['ancount'], 747 self.header['nscount'], self.header['arcount']) = u.getHeader() 748 self.header['opcodestr']=Opcode.opcodestr(self.header['opcode']) 749 self.header['status']=Status.statusstr(self.header['rcode']) 750 for i in range(self.header['qdcount']): 751 #print 'QUESTION %d:' % i, 752 self.questions.append(self.storeQ(u)) 753 for i in range(self.header['ancount']): 754 #print 'ANSWER %d:' % i, 755 self.answers.append(self.storeRR(u)) 756 for i in range(self.header['nscount']): 757 #print 'AUTHORITY RECORD %d:' % i, 758 self.authority.append(self.storeRR(u)) 759 for i in range(self.header['arcount']): 760 #print 'ADDITIONAL RECORD %d:' % i, 761 self.additional.append(self.storeRR(u)) 762 763 def storeQ(self,u): 764 q={} 765 q['qname'], q['qtype'], q['qclass'] = u.getQuestion() 766 q['qtypestr']=Type.typestr(q['qtype']) 767 q['qclassstr']=Class.classstr(q['qclass']) 768 return q 769 770 def storeRR(self,u): 771 r={} 772 r['name'],r['type'],r['class'],r['ttl'],r['rdlength'] = u.getRRheader() 773 r['typename'] = Type.typestr(r['type']) 774 r['classstr'] = Class.classstr(r['class']) 775 #print 'name=%s, type=%d(%s), class=%d(%s), ttl=%d' \ 776 # % (name, 777 # type, typename, 778 # klass, Class.classstr(class), 779 # ttl) 780 mname = 'get%sdata' % r['typename'] 781 if hasattr(u, mname): 782 r['data']=getattr(u, mname)() 783 else: 784 r['data']=u.getbytes(r['rdlength']) 785 return r 786 787def dumpQ(u): 788 qname, qtype, qclass = u.getQuestion() 789 print('qname=%s, qtype=%d(%s), qclass=%d(%s)' \ 790 % (qname, 791 qtype, Type.typestr(qtype), 792 qclass, Class.classstr(qclass))) 793 794def dumpRR(u): 795 name, type, klass, ttl, rdlength = u.getRRheader() 796 typename = Type.typestr(type) 797 print('name=%s, type=%d(%s), class=%d(%s), ttl=%d' \ 798 % (name, 799 type, typename, 800 klass, Class.classstr(klass), 801 ttl)) 802 mname = 'get%sdata' % typename 803 if hasattr(u, mname): 804 print(' formatted rdata:', getattr(u, mname)()) 805 else: 806 print(' binary rdata:', u.getbytes(rdlength)) 807 808if __name__ == "__main__": 809 testpacker() 810