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 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 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 parse(self): dns = None section = None paren = False rr = [] try: while True: tok, val = next(self.i) if tok == 'COMMENT': if 'Sending:' in val or 'Got answer:' in val: if dns: self.parseQuestions(q, dns) self.parseAnswers(a, auth, ar, dns) yield (dns) dns = DNSRecord() q, a, auth, ar = [], [], [], [] elif val.startswith('; ->>HEADER<<-'): self.expect('NL') val2 = self.expect('COMMENT') dns.header = self.parseHeader(val, val2) elif val.startswith('; QUESTION'): section = q elif val.startswith('; ANSWER'): section = a elif val.startswith('; AUTHORITY'): section = auth elif val.startswith('; ADDITIONAL'): section = ar elif val.startswith(';') or tok[1].startswith('<<>>'): pass elif dns and section == q: q.append(val.split()) elif tok == 'ATOM': if val == '(': paren = True elif val == ')': paren = False else: rr.append(val) elif tok == 'NL' and not paren and rr: if self.debug: print(">>", rr) section.append(rr) rr = [] except StopIteration: if rr: self.section.append(rr) if dns: self.parseQuestions(q, dns) self.parseAnswers(a, auth, ar, dns) yield (dns)
def parse(self): dns = None section = None paren = False rr = [] try: while True: tok,val = next(self.i) if tok == 'COMMENT': if 'Sending:' in val or 'Got answer:' in val: if dns: self.parseQuestions(q,dns) self.parseAnswers(a,auth,ar,dns) yield(dns) dns = DNSRecord() q,a,auth,ar = [],[],[],[] elif val.startswith('; ->>HEADER<<-'): self.expect('NL') val2 = self.expect('COMMENT') dns.header = self.parseHeader(val,val2) elif val.startswith('; QUESTION'): section = q elif val.startswith('; ANSWER'): section = a elif val.startswith('; AUTHORITY'): section = auth elif val.startswith('; ADDITIONAL'): section = ar elif val.startswith(';') or tok[1].startswith('<<>>'): pass elif dns and section == q: q.append(val.split()) elif tok == 'ATOM': if val == '(': paren = True elif val == ')': paren = False else: rr.append(val) elif tok == 'NL' and not paren and rr: if self.debug: print(">>",rr) section.append(rr) rr = [] except StopIteration: if rr: self.section.append(rr) if dns: self.parseQuestions(q,dns) self.parseAnswers(a,auth,ar,dns) yield(dns)
def new_test(domain, qtype, address="8.8.8.8", port=53, nodig=False): tcp = False q = DNSRecord.question(domain, qtype) a_pkt = q.send(address, port) a = DNSRecord.parse(a_pkt) if a.header.tc: tcp = True a_pkt = q.send(address, port, tcp) a = DNSRecord.parse(a_pkt) if not nodig: 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] 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) return 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)
p.add_argument("--diff",default="", help="Compare response from alternate nameserver (format: address:port / default: false)") p.add_argument("--dig",action='store_true',default=False, help="Compare result with DiG - if ---diff also specified use alternative nameserver for DiG request (default: false)") p.add_argument("--short",action='store_true',default=False, help="Short output - rdata only (default: false)") p.add_argument("--debug",action='store_true',default=False, help="Drop into CLI after request (default: false)") p.add_argument("domain",metavar="<domain>", help="Query domain") p.add_argument("qtype",metavar="<type>",default="A",nargs="?", help="Query type (default: A)") args = p.parse_args() # Construct request q = DNSRecord(q=DNSQuestion(args.domain,getattr(QTYPE,args.qtype))) address,_,port = args.server.partition(':') port = int(port or 53) if args.query: print(";; Sending%s:" % (" (TCP)" if args.tcp else "")) if args.hex: print(";; QUERY:",binascii.hexlify(q.pack()).decode()) print(q) print() a_pkt = q.send(address,port,tcp=args.tcp) a = DNSRecord.parse(a_pkt) if a.header.tc and args.noretry == False:
default=False, help="Short output - rdata only (default: false)") p.add_argument("--debug", action='store_true', default=False, help="Drop into CLI after request (default: false)") p.add_argument("domain", metavar="<domain>", help="Query domain") p.add_argument("qtype", metavar="<type>", default="A", nargs="?", help="Query type (default: A)") args = p.parse_args() # Construct request q = DNSRecord(q=DNSQuestion(args.domain, getattr(QTYPE, args.qtype))) address, _, port = args.server.partition(':') port = int(port or 53) if args.query: print(";; Sending%s:" % (" (TCP)" if args.tcp else "")) if args.hex: print(";; QUERY:", binascii.hexlify(q.pack()).decode()) print(q) print() a_pkt = q.send(address, port, tcp=args.tcp) a = DNSRecord.parse(a_pkt) if a.header.tc and args.noretry == False: