1
2
3 """Domain Name System."""
4
5 import struct
6 import dpkt
7
8 DNS_Q = 0
9 DNS_R = 1
10
11
12 DNS_QUERY = 0
13 DNS_IQUERY = 1
14 DNS_STATUS = 2
15 DNS_NOTIFY = 4
16 DNS_UPDATE = 5
17
18
19 DNS_CD = 0x0010
20 DNS_AD = 0x0020
21 DNS_Z = 0x0040
22 DNS_RA = 0x0080
23 DNS_RD = 0x0100
24 DNS_TC = 0x0200
25 DNS_AA = 0x0400
26
27
28 DNS_RCODE_NOERR = 0
29 DNS_RCODE_FORMERR = 1
30 DNS_RCODE_SERVFAIL = 2
31 DNS_RCODE_NXDOMAIN = 3
32 DNS_RCODE_NOTIMP = 4
33 DNS_RCODE_REFUSED = 5
34 DNS_RCODE_YXDOMAIN = 6
35 DNS_RCODE_YXRRSET = 7
36 DNS_RCODE_NXRRSET = 8
37 DNS_RCODE_NOTAUTH = 9
38 DNS_RCODE_NOTZONE = 10
39
40
41 DNS_A = 1
42 DNS_NS = 2
43 DNS_CNAME = 5
44 DNS_SOA = 6
45 DNS_PTR = 12
46 DNS_HINFO = 13
47 DNS_MX = 15
48 DNS_TXT = 16
49 DNS_AAAA = 28
50 DNS_SRV = 33
51
52
53 DNS_IN = 1
54 DNS_CHAOS = 3
55 DNS_HESIOD = 4
56 DNS_ANY = 255
57
59 labels = name.split('.')
60 labels.append('')
61 buf = ''
62 for i, label in enumerate(labels):
63 key = '.'.join(labels[i:]).upper()
64 ptr = label_ptrs.get(key)
65 if not ptr:
66 if len(key) > 1:
67 ptr = off + len(buf)
68 if ptr < 0xc000:
69 label_ptrs[key] = ptr
70 i = len(label)
71 buf += chr(i) + label
72 else:
73 buf += struct.pack('>H', (0xc000 | ptr))
74 break
75 return buf
76
78 name = ''
79 saved_off = 0
80 for i in range(100):
81 n = ord(buf[off])
82 if n == 0:
83 off += 1
84 break
85 elif (n & 0xc0) == 0xc0:
86 ptr = struct.unpack('>H', buf[off:off+2])[0] & 0x3fff
87 off += 2
88 if not saved_off:
89 saved_off = off
90
91 name = name + unpack_name(buf, ptr)[0] + '.'
92 break
93 else:
94 off += 1
95 name = name + buf[off:off+n] + '.'
96 if len(name) > 255:
97 raise dpkt.UnpackError('name longer than 255 bytes')
98 off += n
99 return name.strip('.'), off
100
101 -class DNS(dpkt.Packet):
102 __hdr__ = (
103 ('id', 'H', 0),
104 ('op', 'H', DNS_RD),
105
106 ('qd', 'H', []),
107 ('an', 'H', []),
108 ('ns', 'H', []),
109 ('ar', 'H', [])
110 )
112 return int((self.op & 0x8000) == 0x8000)
114 if v: self.op |= 0x8000
115 else: self.op &= ~0x8000
116 qr = property(get_qr, set_qr)
117
119 return (self.op >> 11) & 0xf
121 self.op = (self.op & ~0x7800) | ((v & 0xf) << 11)
122 opcode = property(get_opcode, set_opcode)
123
127 self.op = (self.op & ~0xf) | (v & 0xf)
128 rcode = property(get_rcode, set_rcode)
129
130 - class Q(dpkt.Packet):
131 """DNS question."""
132 __hdr__ = (
133 ('name', '1025s', ''),
134 ('type', 'H', DNS_A),
135 ('cls', 'H', DNS_IN)
136 )
137
139 raise NotImplementedError
140 __str__ = __len__
142 raise NotImplementedError
143
145 """DNS resource record."""
146 __hdr__ = (
147 ('name', '1025s', ''),
148 ('type', 'H', DNS_A),
149 ('cls', 'H', DNS_IN),
150 ('ttl', 'I', 0),
151 ('rlen', 'H', 4),
152 ('rdata', 's', '')
153 )
155
156 if self.rdata:
157 return self.rdata
158 if self.type == DNS_A:
159 return self.ip
160 elif self.type == DNS_NS:
161 return pack_name(self.nsname, off, label_ptrs)
162 elif self.type == DNS_CNAME:
163 return pack_name(self.cname, off, label_ptrs)
164 elif self.type == DNS_PTR:
165 return pack_name(self.ptrname, off, label_ptrs)
166 elif self.type == DNS_SOA:
167 l = []
168 l.append(pack_name(self.mname, off, label_ptrs))
169 l.append(pack_name(self.rname, off + len(l[0]), label_ptrs))
170 l.append(struct.pack('>IIIII', self.serial, self.refresh,
171 self.retry, self.expire, self.minimum))
172 return ''.join(l)
173 elif self.type == DNS_MX:
174 return struct.pack('>H', self.preference) + \
175 pack_name(self.mxname, off + 2, label_ptrs)
176 elif self.type == DNS_TXT or self.type == DNS_HINFO:
177 return ''.join([ '%s%s' % (chr(len(x)), x)
178 for x in self.text ])
179 elif self.type == DNS_AAAA:
180 return self.ip6
181 elif self.type == DNS_SRV:
182 return struct.pack('>HHH', self.priority, self.weight, self.port) + \
183 pack_name(self.srvname, off + 6, label_ptrs)
184
186 if self.type == DNS_A:
187 self.ip = self.rdata
188 elif self.type == DNS_NS:
189 self.nsname, off = unpack_name(buf, off)
190 elif self.type == DNS_CNAME:
191 self.cname, off = unpack_name(buf, off)
192 elif self.type == DNS_PTR:
193 self.ptrname, off = unpack_name(buf, off)
194 elif self.type == DNS_SOA:
195 self.mname, off = unpack_name(buf, off)
196 self.rname, off = unpack_name(buf, off)
197 self.serial, self.refresh, self.retry, self.expire, \
198 self.minimum = struct.unpack('>IIIII', buf[off:off+20])
199 elif self.type == DNS_MX:
200 self.preference = struct.unpack('>H', self.rdata[:2])
201 self.mxname, off = unpack_name(buf, off+2)
202 elif self.type == DNS_TXT or self.type == DNS_HINFO:
203 self.text = []
204 buf = self.rdata
205 while buf:
206 n = ord(buf[0])
207 self.text.append(buf[1:1+n])
208 buf = buf[1+n:]
209 elif self.type == DNS_AAAA:
210 self.ip6 = self.rdata
211 elif self.type == DNS_SRV:
212 self.priority, self.weight, self.port = \
213 struct.unpack('>HHH', self.rdata[:6])
214 self.srvname, off = unpack_name(buf, off+6)
215
217 """Append packed DNS question and return buf."""
218 return buf + pack_name(q.name, len(buf), self.label_ptrs) + \
219 struct.pack('>HH', q.type, q.cls)
220
228
230 """Append packed DNS RR and return buf."""
231 name = pack_name(rr.name, len(buf), self.label_ptrs)
232 rdata = rr.pack_rdata(len(buf) + len(name) + 10, self.label_ptrs)
233 return buf + name + struct.pack('>HHIH', rr.type, rr.cls, rr.ttl,
234 len(rdata)) + rdata
235
246
248 dpkt.Packet.unpack(self, buf)
249 off = self.__hdr_len__
250 cnt = self.qd
251 self.qd = []
252 for i in range(cnt):
253 q, off = self.unpack_q(buf, off)
254 self.qd.append(q)
255 for x in ('an', 'ns', 'ar'):
256 cnt = getattr(self, x, 0)
257 setattr(self, x, [])
258 for i in range(cnt):
259 rr, off = self.unpack_rr(buf, off)
260 getattr(self, x).append(rr)
261 self.data = ''
262
264
265 return len(str(self))
266
268
269 self.label_ptrs = {}
270 buf = struct.pack(self.__hdr_fmt__, self.id, self.op, len(self.qd),
271 len(self.an), len(self.ns), len(self.ar))
272 for q in self.qd:
273 buf = self.pack_q(buf, q)
274 for x in ('an', 'ns', 'ar'):
275 for rr in getattr(self, x):
276 buf = self.pack_rr(buf, rr)
277 del self.label_ptrs
278 return buf
279
280 if __name__ == '__main__':
281 import unittest
282 from ip import IP
283
286 s = 'E\x00\x02\x08\xc15\x00\x00\x80\x11\x92aBk0\x01Bk0w\x005\xc07\x01\xf4\xda\xc2d\xd2\x81\x80\x00\x01\x00\x03\x00\x0b\x00\x0b\x03www\x06google\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x03V\x00\x17\x03www\x06google\x06akadns\x03net\x00\xc0,\x00\x01\x00\x01\x00\x00\x01\xa3\x00\x04@\xe9\xabh\xc0,\x00\x01\x00\x01\x00\x00\x01\xa3\x00\x04@\xe9\xabc\xc07\x00\x02\x00\x01\x00\x00KG\x00\x0c\x04usw5\x04akam\xc0>\xc07\x00\x02\x00\x01\x00\x00KG\x00\x07\x04usw6\xc0t\xc07\x00\x02\x00\x01\x00\x00KG\x00\x07\x04usw7\xc0t\xc07\x00\x02\x00\x01\x00\x00KG\x00\x08\x05asia3\xc0t\xc07\x00\x02\x00\x01\x00\x00KG\x00\x05\x02za\xc07\xc07\x00\x02\x00\x01\x00\x00KG\x00\x0f\x02zc\x06akadns\x03org\x00\xc07\x00\x02\x00\x01\x00\x00KG\x00\x05\x02zf\xc07\xc07\x00\x02\x00\x01\x00\x00KG\x00\x05\x02zh\xc0\xd5\xc07\x00\x02\x00\x01\x00\x00KG\x00\x07\x04eur3\xc0t\xc07\x00\x02\x00\x01\x00\x00KG\x00\x07\x04use2\xc0t\xc07\x00\x02\x00\x01\x00\x00KG\x00\x07\x04use4\xc0t\xc0\xc1\x00\x01\x00\x01\x00\x00\xfb4\x00\x04\xd0\xb9\x84\xb0\xc0\xd2\x00\x01\x00\x01\x00\x001\x0c\x00\x04?\xf1\xc76\xc0\xed\x00\x01\x00\x01\x00\x00\xfb4\x00\x04?\xd7\xc6S\xc0\xfe\x00\x01\x00\x01\x00\x001\x0c\x00\x04?\xd00.\xc1\x0f\x00\x01\x00\x01\x00\x00\n\xdf\x00\x04\xc1-\x01g\xc1"\x00\x01\x00\x01\x00\x00\x101\x00\x04?\xd1\xaa\x88\xc15\x00\x01\x00\x01\x00\x00\r\x1a\x00\x04PCC\xb6\xc0o\x00\x01\x00\x01\x00\x00\x10\x7f\x00\x04?\xf1I\xd6\xc0\x87\x00\x01\x00\x01\x00\x00\n\xdf\x00\x04\xce\x84dl\xc0\x9a\x00\x01\x00\x01\x00\x00\n\xdf\x00\x04A\xcb\xea\x1b\xc0\xad\x00\x01\x00\x01\x00\x00\x0b)\x00\x04\xc1l\x9a\t'
287 ip = IP(s)
288 dns = DNS(ip.udp.data)
289 self.failUnless(dns.qd[0].name == 'www.google.com' and
290 dns.an[1].name == 'www.google.akadns.net')
291 s = '\x05\xf5\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x03cnn\x03com\x00\x00\x01\x00\x01'
292 dns = DNS(s)
293 self.failUnless(s == str(dns))
294
296 s = 'g\x02\x81\x80\x00\x01\x00\x01\x00\x03\x00\x00\x011\x011\x03211\x03141\x07in-addr\x04arpa\x00\x00\x0c\x00\x01\xc0\x0c\x00\x0c\x00\x01\x00\x00\r6\x00$\x07default\nv-umce-ifs\x05umnet\x05umich\x03edu\x00\xc0\x0e\x00\x02\x00\x01\x00\x00\r6\x00\r\x06shabby\x03ifs\xc0O\xc0\x0e\x00\x02\x00\x01\x00\x00\r6\x00\x0f\x0cfish-license\xc0m\xc0\x0e\x00\x02\x00\x01\x00\x00\r6\x00\x0b\x04dns2\x03itd\xc0O'
297 dns = DNS(s)
298 self.failUnless(dns.qd[0].name == '1.1.211.141.in-addr.arpa' and
299 dns.an[0].ptrname == 'default.v-umce-ifs.umnet.umich.edu' and
300 dns.ns[0].nsname == 'shabby.ifs.umich.edu' and
301 dns.ns[1].ttl == 3382L and
302 dns.ns[2].nsname == 'dns2.itd.umich.edu')
303 self.failUnless(s == str(dns))
304
305 unittest.main()
306