3 import sys, socket, getopt
11 def min_size(message):
12 """Calculate the minimum size this response can be without setting
13 the TC (truncation bit). This is calculated by removing all of
14 the additional section save the OPT pseudo-RR."""
16 additional = message.additional
17 # note that in dns.message.Message, the OPT RR is not stored in
18 # the additional section (where it was). Instead it is parsed
19 # into various member variables, so we don't need to do anything
20 # special to preserve it.
21 message.additional = []
22 wire = message.to_wire()
23 message.additional = additional
26 def additional_qname_length(qname):
27 """Calculate the additional size a message might take if it had
28 the maximum sized qname (e.g., a 255-octet qname). Since the
29 message already contains some qname, calculate the difference."""
30 return 255 - len(qname.to_wire())
32 def do_query(qname, qtype):
33 """Given a qname and a qtype, use the dedicated socket to query a
34 namesrver with DO=1 and BUFSIZE=4096, then calculate the full
35 response size, the minimum size, and the additional qname
38 q = dns.message.make_query(qname, qtype)
45 # wait for the response (no timeouts yet)
46 resp_wire = s.recv(65535)
47 # calculate the base length.
48 resp_len = len(resp_wire)
50 resp = dns.message.from_wire(resp_wire)
51 # calculate our minimum size
52 minimum = min_size(resp)
53 # calculate our maximum size
54 plus_qname = additional_qname_length(q.question[0].name)
55 return (resp, resp_len, minimum, plus_qname)
58 """A class representing a basic histogram for DNS response
61 def __init__(self, factor):
70 def add(self, length, label):
71 if label in self.labels_: return
72 self.labels_[label] = 1
74 index = int(length / self.factor_)
75 if index not in self.hist_:
78 self.hist_[index] += 1
80 if self.minimum_ == 0 or length < self.minimum_:
81 self.minimum_ = length
82 self.minlabel_ = label
83 if length > self.maximum_:
84 self.maximum_ = length
85 self.maxlabel_ = label
88 res = "range [%d - %d] min: %s, max: %s\n" % \
89 (self.minimum_, self.maximum_, self.minlabel_, self.maxlabel_)
90 keys = self.hist_.keys()
93 rangestr = "[%d - %d)" % (index * self.factor_,
94 (index + 1) * self.factor_)
95 res += "%20s : %d\n" % (rangestr, self.hist_[index])
98 def process_zone(zone, skip_addr=True):
99 last_name = ""; last_type = 0
101 for name, rd in zone.iterate_rdatasets():
105 # skip queries to the glue records; they are redundant with
107 if skip_addr and (t == dns.rdatatype.A or t == dns.rdatatype.AAAA):
109 label = n + "/" + dns.rdatatype.to_text(t)
111 (response, resp_len, minimum, plus_qname) = do_query(n, t)
112 print "%d\t%d\t%d\t%s" % (resp_len, minimum, plus_qname, label)
114 # Populate the referral histograms
115 if t == dns.rdatatype.NS:
116 ref_hist.add(minimum + plus_qname, label)
117 max_ref_hist.add(resp_len + plus_qname, label)
119 other_hist.add(resp_len, label)
123 (response, resp_len, minimum, plus_qname) = \
124 do_query(n , dns.rdatatype.ANY)
125 print "%d\t%d\t%d\t%s" % (resp_len, minimum, plus_qname, label)
126 other_hist.add(resp_len, label)
129 (response, resp_len, minimum, plus_qname) = \
130 do_query(n + "_", dns.rdatatype.A)
131 print "%d\t%d\t%d\t%s" % (resp_len, minimum, plus_qname, label)
132 # populate the nxdomain histogram
133 nx_hist.add(resp_len + plus_qname, label)
137 if __name__ == "__main__":
142 options, args = getopt.getopt(sys.argv[1:], "p:s:")
149 # create a query socket
150 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
151 # connect it to our server
152 s.connect((host, port))
154 ref_hist = Histogram(64)
155 max_ref_hist = Histogram(64)
156 nx_hist = Histogram(64)
157 other_hist = Histogram(64)
159 for zonefile in args:
163 zonefile, origin = zonefile.split(':', 1)
164 if origin and ':' in origin:
165 origin, skip = origin.split(':', 1)
166 skip = len(skip) == 0
167 if origin and origin == '.':
172 zone = dns.zone.from_file(zonefile, origin=origin, relativize=rel)
174 process_zone(zone, skip)
178 print >> sys.stderr, "Referral sizes (Maximum truncation sizes):"
179 print >> sys.stderr, ref_hist.to_text()
181 print >> sys.stderr, "Referral sizes (Maximum overall sizes):"
182 print >> sys.stderr, max_ref_hist.to_text()
184 print >> sys.stderr, "NXDOMAIN sizes (Maximum truncation sizes):"
185 print >> sys.stderr, nx_hist.to_text()
187 print >> sys.stderr, "Other response sizes (Full response sizes):"
188 print >> sys.stderr, other_hist.to_text()