DNS/実装/python/dnslib/client.py
1 # -*- coding: utf-8 -*-
2
3 """
4 DNS Client - DiG-like CLI utility.
5
6 Mostly useful for testing. Can optionally compare results from two
7 nameservers (--diff) or compare results against DiG (--dig).
8
9 Usage: python -m dnslib.client [options|--help]
10
11 See --help for usage.
12 """
13
14 from __future__ import print_function
15
16 try:
17 from subprocess import getoutput
18 except ImportError:
19 from commands import getoutput
20
21 import binascii,code,pprint
22
23 from dnslib.dns import DNSRecord,DNSHeader,DNSQuestion,QTYPE
24 from dnslib.digparser import DigParser
25
26 if __name__ == '__main__':
27
28 import argparse,sys,time
29
30 p = argparse.ArgumentParser(description="DNS Client")
31 p.add_argument("--server","-s",default="8.8.8.8",
32 metavar="<address:port>",
33 help="Server address:port (default:8.8.8.8:53) (port is optional)")
34 p.add_argument("--query",action='store_true',default=False,
35 help="Show query (default: False)")
36 p.add_argument("--hex",action='store_true',default=False,
37 help="Dump packet in hex (default: False)")
38 p.add_argument("--tcp",action='store_true',default=False,
39 help="Use TCP (default: UDP)")
40 p.add_argument("--noretry",action='store_true',default=False,
41 help="Don't retry query using TCP if truncated (default: false)")
42 p.add_argument("--diff",default="",
43 help="Compare response from alternate nameserver (format: address:port / default: false)")
44 p.add_argument("--dig",action='store_true',default=False,
45 help="Compare result with DiG - if ---diff also specified use alternative nameserver for DiG request (default: false)")
46 p.add_argument("--short",action='store_true',default=False,
47 help="Short output - rdata only (default: false)")
48 p.add_argument("--debug",action='store_true',default=False,
49 help="Drop into CLI after request (default: false)")
50 p.add_argument("domain",metavar="<domain>",
51 help="Query domain")
52 p.add_argument("qtype",metavar="<type>",default="A",nargs="?",
53 help="Query type (default: A)")
54 args = p.parse_args()
55
56 # Construct request
57 q = DNSRecord(q=DNSQuestion(args.domain,getattr(QTYPE,args.qtype)))
58
59 address,_,port = args.server.partition(':')
60 port = int(port or 53)
61
62 if args.query:
63 print(";; Sending%s:" % (" (TCP)" if args.tcp else ""))
64 if args.hex:
65 print(";; QUERY:",binascii.hexlify(q.pack()).decode())
66 print(q)
67 print()
68
69 a_pkt = q.send(address,port,tcp=args.tcp)
70 a = DNSRecord.parse(a_pkt)
71
72 if a.header.tc and args.noretry == False:
73 # Truncated - retry in TCP mode
74 a_pkt = q.send(address,port,tcp=True)
75 a = DNSRecord.parse(a_pkt)
76
77 if args.dig or args.diff:
78 if args.diff:
79 address,_,port = args.diff.partition(':')
80 port = int(port or 53)
81
82 if args.dig:
83 dig = getoutput("dig +qr -p %d %s %s @%s" % (
84 port, args.domain, args.qtype, address))
85 dig_reply = list(iter(DigParser(dig)))
86 # DiG might have retried in TCP mode so get last q/a
87 q_diff = dig_reply[-2]
88 a_diff = dig_reply[-1]
89 else:
90 q_diff = DNSRecord(header=DNSHeader(id=q.header.id),
91 q=DNSQuestion(args.domain,
92 getattr(QTYPE,args.qtype)))
93 q_diff = q
94 diff = q_diff.send(address,port,tcp=args.tcp)
95 a_diff = DNSRecord.parse(diff)
96 if a_diff.header.tc and args.noretry == False:
97 diff = q_diff.send(address,port,tcp=True)
98 a_diff = DNSRecord.parse(diff)
99
100 if args.short:
101 print(a.short())
102 else:
103 print(";; Got answer:")
104 if args.hex:
105 print(";; RESPONSE:",binascii.hexlify(a_pkt).decode())
106 if args.diff and not args.dig:
107 print(";; DIFF :",binascii.hexlify(diff).decode())
108 print(a)
109 print()
110
111 if args.dig or args.diff:
112 if q != q_diff:
113 print(";;; ERROR: Diff Question differs")
114 for (d1,d2) in q.diff(q_diff):
115 if d1:
116 print(";; - %s" % d1)
117 if d2:
118 print(";; + %s" % d2)
119 if a != a_diff:
120 print(";;; ERROR: Diff Response differs")
121 for (d1,d2) in a.diff(a_diff):
122 if d1:
123 print(";; - %s" % d1)
124 if d2:
125 print(";; + %s" % d2)
126
127 if args.debug:
128 code.interact(local=locals())