1 # ldd - DNS implementation in Python
2 # Copyright (C) 2006 Fredrik Tolf <fredrik@dolda2000.com>
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 from random import randint
24 class malformedpacket(Exception):
25 def __init__(self, text, qid):
33 "An abstract representation of a DNS query"
35 def __init__(self, qid = None, flags = 0, addr = None):
36 if qid is None: qid = randint(0, 65535)
47 if type(flags) == int:
49 elif type(flags) == set:
52 self.flags = set(flags)
54 def setflags(self, flags):
58 def clrflags(self, flags):
62 def initflags(self, flags):
64 if flags & 0x8000: nf.add("resp")
65 if flags & 0x0400: nf.add("auth")
66 if flags & 0x0200: nf.add("trunc")
67 if flags & 0x0100: nf.add("recurse")
68 if flags & 0x0080: nf.add("recursed")
69 if flags & 0x0020: nf.add("isauthen")
70 if flags & 0x0010: nf.add("authok")
71 self.opcode = (flags & 0x7800) >> 11
72 self.rescode = flags & 0x000f
75 def encodeflags(self):
77 if "resp" in self.flags: ret |= 0x8000
78 if "auth" in self.flags: ret |= 0x0400
79 if "trunc" in self.flags: ret |= 0x0200
80 if "recurse" in self.flags: ret |= 0x0100
81 if "recursed" in self.flags: ret |= 0x0080
82 if "authok" in self.flags: ret |= 0x0010
83 ret |= self.opcode << 11
91 for rr2 in self.anlist:
92 if rr2.head == rr.head and rr2.data == rr.data:
95 self.anlist.append(rr)
98 for rr2 in self.aulist:
99 if rr2.head == rr.head and rr2.data == rr.data:
102 self.aulist.append(rr)
105 for rr2 in self.adlist:
106 if rr2.head == rr.head and rr2.data == rr.data:
109 self.adlist.append(rr)
112 return self.anlist + self.aulist + self.adlist
114 def merge(self, other):
115 for lst in ["anlist", "aulist", "adlist"]:
116 for rr in getattr(other, lst):
117 for rr2 in getattr(self, lst):
118 if rr2.head == rr.head and rr2.data == rr.data:
121 getattr(self, lst).append(rr)
123 def getanswer(self, name, rtype):
124 for rr in self.anlist + self.aulist + self.adlist:
125 if rr.head.istype(rtype) and rr.head.name == name:
129 def hasanswers(self):
131 for rr in self.anlist + self.aulist + self.adlist:
132 if rr.head.rtype == q.rtype and rr.head.name == q.name:
134 if rr.head.istype("CNAME") and rr.head.name == q.name and self.getanswer(rr.data["priname"], q.rtype) is not None:
144 ret += "ID: " + str(self.qid) + "\n"
145 ret += "Flags: " + str(self.flags) + "\n"
146 ret += "Opcode: " + str(self.opcode) + "\n"
147 ret += "Resp. code: " + str(self.rescode) + "\n"
149 for rr in self.qlist:
150 ret += "\t" + str(rr) + "\n"
152 for rr in self.anlist:
153 ret += "\t" + str(rr) + "\n"
155 for rr in self.aulist:
156 ret += "\t" + str(rr) + "\n"
157 ret += "Additional RRs:\n"
158 for rr in self.adlist:
159 ret += "\t" + str(rr) + "\n"
164 ret += struct.pack(">6H", self.qid, self.encodeflags(), len(self.qlist), len(self.anlist), len(self.aulist), len(self.adlist))
167 for rr in self.qlist:
168 rre, names = rr.encode(names, offset)
171 for rr in self.anlist:
172 rre, names = rr.encode(names, offset)
175 for rr in self.aulist:
176 rre, names = rr.encode(names, offset)
179 for rr in self.adlist:
180 rre, names = rr.encode(names, offset)
185 def decodepacket(string):
186 offset = struct.calcsize(">6H")
187 qid, flags, qno, anno, auno, adno = struct.unpack(">6H", string[0:offset])
188 ret = packet(qid, flags)
191 crr, offset = rec.rrhead.decode(string, offset)
193 for i in range(anno):
194 crr, offset = rec.rr.decode(string, offset)
196 for i in range(auno):
197 crr, offset = rec.rr.decode(string, offset)
199 for i in range(adno):
200 crr, offset = rec.rr.decode(string, offset)
202 except rec.malformedrr, inst:
203 raise malformedpacket(str(inst), qid)
206 def responsefor(pkt, rescode = 0):
207 resp = packet(pkt.qid, ["resp"])
208 resp.opcode = pkt.opcode
209 resp.rescode = rescode
210 resp.tsigctx = pkt.tsigctx
211 resp.qlist = pkt.qlist + [] # Make a copy
214 def decodename(packet, offset):
217 clen = ord(packet[offset])
220 my = dn.domainname(parts, False)
221 cont, = struct.unpack(">H", chr(clen & 0x3f) + packet[offset])
222 res, discard = decodename(packet, cont)
223 return my + res, offset + 1
225 return dn.domainname(parts, True), offset
227 parts.append(packet[offset:offset + clen])
230 def encodename(dn, names, offset):
232 for i in range(len(dn)):
233 for name, off in names:
235 ret += chr(0xc0 + (off >> 8))
236 ret += chr(off & 0xff)
240 names += [(dn[i:], offset)]
241 ret += chr(len(dn.parts[i]))
243 offset += 1 + len(dn.parts[i])
254 # Response code constants