pydns-2.3.6/0000755000160600001450000000000011713073473011547 5ustar stuartbmspydns-2.3.6/DNS/0000755000160600001450000000000011713073473012173 5ustar stuartbmspydns-2.3.6/DNS/Base.py0000644000160600001450000004201711663224743013425 0ustar stuartbms""" $Id: Base.py,v 1.12.2.19 2011/11/23 17:14:11 customdesigned Exp $ This file is part of the pydns project. Homepage: http://pydns.sourceforge.net This code is covered by the standard Python License. See LICENSE for details. Base functionality. Request and Response classes, that sort of thing. """ import socket, string, types, time, select import Type,Class,Opcode import asyncore # # This random generator is used for transaction ids and port selection. This # is important to prevent spurious results from lost packets, and malicious # cache poisoning. This doesn't matter if you are behind a caching nameserver # or your app is a primary DNS server only. To install your own generator, # replace DNS.Base.random. SystemRandom uses /dev/urandom or similar source. # try: from random import SystemRandom random = SystemRandom() except: import random class DNSError(Exception): pass class ArgumentError(DNSError): pass class SocketError(DNSError): pass class TimeoutError(DNSError): pass class ServerError(DNSError): def __init__(self, message, rcode): DNSError.__init__(self, message, rcode) self.message = message self.rcode = rcode class IncompleteReplyError(DNSError): pass # Lib uses some of the above exception classes, so import after defining. import Lib defaults= { 'protocol':'udp', 'port':53, 'opcode':Opcode.QUERY, 'qtype':Type.A, 'rd':1, 'timing':1, 'timeout': 30, 'server_rotate': 0 } defaults['server']=[] def ParseResolvConf(resolv_path="/etc/resolv.conf"): "parses the /etc/resolv.conf file and sets defaults for name servers" global defaults lines=open(resolv_path).readlines() for line in lines: line = string.strip(line) if not line or line[0]==';' or line[0]=='#': continue fields=string.split(line) if len(fields) < 2: continue if fields[0]=='domain' and len(fields) > 1: defaults['domain']=fields[1] if fields[0]=='search': pass if fields[0]=='options': pass if fields[0]=='sortlist': pass if fields[0]=='nameserver': defaults['server'].append(fields[1]) def DiscoverNameServers(): import sys if sys.platform in ('win32', 'nt'): import win32dns defaults['server']=win32dns.RegistryResolve() else: return ParseResolvConf() class DnsRequest: """ high level Request object """ def __init__(self,*name,**args): self.donefunc=None self.async=None self.defaults = {} self.argparse(name,args) self.defaults = self.args self.tid = 0 def argparse(self,name,args): if not name and self.defaults.has_key('name'): args['name'] = self.defaults['name'] if type(name) is types.StringType: args['name']=name else: if len(name) == 1: if name[0]: args['name']=name[0] if defaults['server_rotate'] and \ type(defaults['server']) == types.ListType: defaults['server'] = defaults['server'][1:]+defaults['server'][:1] for i in defaults.keys(): if not args.has_key(i): if self.defaults.has_key(i): args[i]=self.defaults[i] else: args[i]=defaults[i] if type(args['server']) == types.StringType: args['server'] = [args['server']] self.args=args def socketInit(self,a,b): self.s = socket.socket(a,b) def processUDPReply(self): if self.timeout > 0: r,w,e = select.select([self.s],[],[],self.timeout) if not len(r): raise TimeoutError, 'Timeout' (self.reply, self.from_address) = self.s.recvfrom(65535) self.time_finish=time.time() self.args['server']=self.ns return self.processReply() def _readall(self,f,count): res = f.read(count) while len(res) < count: if self.timeout > 0: # should we restart timeout everytime we get a dribble of data? rem = self.time_start + self.timeout - time.time() if rem <= 0: raise TimeoutError, 'Timeout' self.s.settimeout(rem) buf = f.read(count - len(res)) if not buf: raise IncompleteReplyError, 'incomplete reply - %d of %d read' % (len(res),count) res += buf return res def processTCPReply(self): if self.timeout > 0: self.s.settimeout(self.timeout) else: self.s.settimeout(None) f = self.s.makefile('rb') try: header = self._readall(f,2) count = Lib.unpack16bit(header) self.reply = self._readall(f,count) finally: f.close() self.time_finish=time.time() self.args['server']=self.ns return self.processReply() def processReply(self): self.args['elapsed']=(self.time_finish-self.time_start)*1000 u = Lib.Munpacker(self.reply) r=Lib.DnsResult(u,self.args) r.args=self.args #self.args=None # mark this DnsRequest object as used. return r #### TODO TODO TODO #### # if protocol == 'tcp' and qtype == Type.AXFR: # while 1: # header = f.read(2) # if len(header) < 2: # print '========== EOF ==========' # break # count = Lib.unpack16bit(header) # if not count: # print '========== ZERO COUNT ==========' # break # print '========== NEXT ==========' # reply = f.read(count) # if len(reply) != count: # print '*** Incomplete reply ***' # break # u = Lib.Munpacker(reply) # Lib.dumpM(u) def getSource(self): "Pick random source port to avoid DNS cache poisoning attack." while True: try: source_port = random.randint(1024,65535) self.s.bind(('', source_port)) break except socket.error, msg: # Error 98, 'Address already in use' if msg[0] != 98: raise def conn(self): self.getSource() self.s.connect((self.ns,self.port)) def req(self,*name,**args): " needs a refactoring " self.argparse(name,args) #if not self.args: # raise ArgumentError, 'reinitialize request before reuse' protocol = self.args['protocol'] self.port = self.args['port'] self.tid = random.randint(0,65535) self.timeout = self.args['timeout']; opcode = self.args['opcode'] rd = self.args['rd'] server=self.args['server'] if type(self.args['qtype']) == types.StringType: try: qtype = getattr(Type, string.upper(self.args['qtype'])) except AttributeError: raise ArgumentError, 'unknown query type' else: qtype=self.args['qtype'] if not self.args.has_key('name'): print self.args raise ArgumentError, 'nothing to lookup' qname = self.args['name'] if qtype == Type.AXFR and protocol != 'tcp': print 'Query type AXFR, protocol forced to TCP' protocol = 'tcp' #print 'QTYPE %d(%s)' % (qtype, Type.typestr(qtype)) m = Lib.Mpacker() # jesus. keywords and default args would be good. TODO. m.addHeader(self.tid, 0, opcode, 0, 0, rd, 0, 0, 0, 1, 0, 0, 0) m.addQuestion(qname, qtype, Class.IN) self.request = m.getbuf() try: if protocol == 'udp': self.sendUDPRequest(server) else: self.sendTCPRequest(server) except socket.error, reason: raise SocketError, reason if self.async: return None else: return self.response def sendUDPRequest(self, server): "refactor me" first_socket_error = None self.response=None for self.ns in server: #print "trying udp",self.ns try: if self.ns.count(':'): if hasattr(socket,'has_ipv6') and socket.has_ipv6: self.socketInit(socket.AF_INET6, socket.SOCK_DGRAM) else: continue else: self.socketInit(socket.AF_INET, socket.SOCK_DGRAM) try: # TODO. Handle timeouts &c correctly (RFC) self.time_start=time.time() self.conn() if not self.async: self.s.send(self.request) r=self.processUDPReply() # Since we bind to the source port and connect to the # destination port, we don't need to check that here, # but do make sure it's actually a DNS request that the # packet is in reply to. while r.header['id'] != self.tid \ or self.from_address[1] != self.port: r=self.processUDPReply() self.response = r # FIXME: check waiting async queries finally: if not self.async: self.s.close() except socket.error, e: # Keep trying more nameservers, but preserve the first error # that occurred so it can be reraised in case none of the # servers worked: first_socket_error = first_socket_error or e continue if not self.response and first_socket_error: raise first_socket_error def sendTCPRequest(self, server): " do the work of sending a TCP request " first_socket_error = None self.response=None for self.ns in server: #print "trying tcp",self.ns try: if self.ns.count(':'): if hasattr(socket,'has_ipv6') and socket.has_ipv6: self.socketInit(socket.AF_INET6, socket.SOCK_STREAM) else: continue else: self.socketInit(socket.AF_INET, socket.SOCK_STREAM) try: # TODO. Handle timeouts &c correctly (RFC) self.time_start=time.time() self.conn() buf = Lib.pack16bit(len(self.request))+self.request # Keep server from making sendall hang self.s.setblocking(0) # FIXME: throws WOULDBLOCK if request too large to fit in # system buffer self.s.sendall(buf) # SHUT_WR breaks blocking IO with google DNS (8.8.8.8) #self.s.shutdown(socket.SHUT_WR) r=self.processTCPReply() if r.header['id'] == self.tid: self.response = r break finally: self.s.close() except socket.error, e: first_socket_error = first_socket_error or e continue if not self.response and first_socket_error: raise first_socket_error #class DnsAsyncRequest(DnsRequest): class DnsAsyncRequest(DnsRequest,asyncore.dispatcher_with_send): " an asynchronous request object. out of date, probably broken " def __init__(self,*name,**args): DnsRequest.__init__(self, *name, **args) # XXX todo if args.has_key('done') and args['done']: self.donefunc=args['done'] else: self.donefunc=self.showResult #self.realinit(name,args) # XXX todo self.async=1 def conn(self): self.getSource() self.connect((self.ns,self.port)) self.time_start=time.time() if self.args.has_key('start') and self.args['start']: asyncore.dispatcher.go(self) def socketInit(self,a,b): self.create_socket(a,b) asyncore.dispatcher.__init__(self) self.s=self def handle_read(self): if self.args['protocol'] == 'udp': self.response=self.processUDPReply() if self.donefunc: apply(self.donefunc,(self,)) def handle_connect(self): self.send(self.request) def handle_write(self): pass def showResult(self,*s): self.response.show() # # $Log: Base.py,v $ # Revision 1.12.2.19 2011/11/23 17:14:11 customdesigned # Apply patch 3388075 from sourceforge: raise subclasses of DNSError. # # Revision 1.12.2.18 2011/05/02 16:02:36 customdesigned # Don't complain about protocol for AXFR unless it needs changing. # Reported by Ewoud Kohl van Wijngaarden. # # Revision 1.12.2.17 2011/03/21 13:01:24 customdesigned # Close file for processTCPReply # # Revision 1.12.2.16 2011/03/21 12:54:52 customdesigned # Reply is binary. # # Revision 1.12.2.15 2011/03/19 22:15:01 customdesigned # Added rotation of name servers - SF Patch ID: 2795929 # # Revision 1.12.2.14 2011/03/17 03:46:03 customdesigned # Simple test for google DNS with tcp # # Revision 1.12.2.13 2011/03/17 03:08:03 customdesigned # Use blocking IO with timeout for TCP replies. # # Revision 1.12.2.12 2011/03/16 17:50:00 customdesigned # Fix non-blocking TCP replies. (untested) # # Revision 1.12.2.11 2010/01/02 16:31:23 customdesigned # Handle large TCP replies (untested). # # Revision 1.12.2.10 2008/08/01 03:58:03 customdesigned # Don't try to close socket when never opened. # # Revision 1.12.2.9 2008/08/01 03:48:31 customdesigned # Fix more breakage from port randomization patch. Support Ipv6 queries. # # Revision 1.12.2.8 2008/07/31 18:22:59 customdesigned # Wait until tcp response at least starts coming in. # # Revision 1.12.2.7 2008/07/28 01:27:00 customdesigned # Check configured port. # # Revision 1.12.2.6 2008/07/28 00:17:10 customdesigned # Randomize source ports. # # Revision 1.12.2.5 2008/07/24 20:10:55 customdesigned # Randomize tid in requests, and check in response. # # Revision 1.12.2.4 2007/05/22 20:28:31 customdesigned # Missing import Lib # # Revision 1.12.2.3 2007/05/22 20:25:52 customdesigned # Use socket.inetntoa,inetaton. # # Revision 1.12.2.2 2007/05/22 20:21:46 customdesigned # Trap socket error # # Revision 1.12.2.1 2007/05/22 20:19:35 customdesigned # Skip bogus but non-empty lines in resolv.conf # # Revision 1.12 2002/04/23 06:04:27 anthonybaxter # attempt to refactor the DNSRequest.req method a little. after doing a bit # of this, I've decided to bite the bullet and just rewrite the puppy. will # be checkin in some design notes, then unit tests and then writing the sod. # # Revision 1.11 2002/03/19 13:05:02 anthonybaxter # converted to class based exceptions (there goes the python1.4 compatibility :) # # removed a quite gross use of 'eval()'. # # Revision 1.10 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.9 2002/03/19 12:26:13 anthonybaxter # death to leading tabs. # # Revision 1.8 2002/03/19 10:30:33 anthonybaxter # first round of major bits and pieces. The major stuff here (summarised # from my local, off-net CVS server :/ this will cause some oddities with # the # # tests/testPackers.py: # a large slab of unit tests for the packer and unpacker code in DNS.Lib # # DNS/Lib.py: # placeholder for addSRV. # added 'klass' to addA, make it the same as the other A* records. # made addTXT check for being passed a string, turn it into a length 1 list. # explicitly check for adding a string of length > 255 (prohibited). # a bunch of cleanups from a first pass with pychecker # new code for pack/unpack. the bitwise stuff uses struct, for a smallish # (disappointly small, actually) improvement, while addr2bin is much # much faster now. # # DNS/Base.py: # added DiscoverNameServers. This automatically does the right thing # on unix/ win32. No idea how MacOS handles this. *sigh* # Incompatible change: Don't use ParseResolvConf on non-unix, use this # function, instead! # a bunch of cleanups from a first pass with pychecker # # Revision 1.5 2001/08/09 09:22:28 anthonybaxter # added what I hope is win32 resolver lookup support. I'll need to try # and figure out how to get the CVS checkout onto my windows machine to # make sure it works (wow, doing something other than games on the # windows machine :) # # Code from Wolfgang.Strobl@gmd.de # win32dns.py from # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66260 # # Really, ParseResolvConf() should be renamed "FindNameServers" or # some such. # # Revision 1.4 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.3 2001/07/19 07:20:12 anthony # Handle blank resolv.conf lines. # Patch from Bastian Kleineidam # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/Class.py0000644000160600001450000000267511540214117013612 0ustar stuartbms""" $Id: Class.py,v 1.6.2.1 2011/03/16 20:06:39 customdesigned Exp $ This file is part of the pydns project. Homepage: http://pydns.sourceforge.net This code is covered by the standard Python License. See LICENSE for details. CLASS values (section 3.2.4) """ IN = 1 # the Internet CS = 2 # the CSNET class (Obsolete - used only for examples in # some obsolete RFCs) CH = 3 # the CHAOS class. When someone shows me python running on # a Symbolics Lisp machine, I'll look at implementing this. HS = 4 # Hesiod [Dyer 87] # QCLASS values (section 3.2.5) ANY = 255 # any class # Construct reverse mapping dictionary _names = dir() classmap = {} for _name in _names: if _name[0] != '_': classmap[eval(_name)] = _name def classstr(klass): if classmap.has_key(klass): return classmap[klass] else: return `klass` # # $Log: Class.py,v $ # Revision 1.6.2.1 2011/03/16 20:06:39 customdesigned # Refer to explicit LICENSE file. # # Revision 1.6 2002/04/23 12:52:19 anthonybaxter # cleanup whitespace. # # Revision 1.5 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.4 2002/03/19 12:26:13 anthonybaxter # death to leading tabs. # # Revision 1.3 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/Lib.py0000644000160600001450000005650411663224102013254 0ustar stuartbms# -*- encoding: utf-8 -*- """ $Id: Lib.py,v 1.11.2.11 2011/11/23 17:07:14 customdesigned Exp $ This file is part of the pydns project. Homepage: http://pydns.sourceforge.net This code is covered by the standard Python License. See LICENSE for details. Library code. Largely this is packers and unpackers for various types. """ # # # See RFC 1035: # ------------------------------------------------------------------------ # Network Working Group P. Mockapetris # Request for Comments: 1035 ISI # November 1987 # Obsoletes: RFCs 882, 883, 973 # # DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION # ------------------------------------------------------------------------ import types import Type import Class import Opcode import Status import DNS from Base import DNSError LABEL_UTF8 = False LABEL_ENCODING = 'idna' class UnpackError(DNSError): pass class PackError(DNSError): pass # Low-level 16 and 32 bit integer packing and unpacking from struct import pack as struct_pack from struct import unpack as struct_unpack from socket import inet_ntoa, inet_aton def pack16bit(n): return struct_pack('!H', n) def pack32bit(n): return struct_pack('!L', n) def unpack16bit(s): return struct_unpack('!H', s)[0] def unpack32bit(s): return struct_unpack('!L', s)[0] def addr2bin(addr): return struct_unpack('!l', inet_aton(addr))[0] def bin2addr(n): return inet_ntoa(struct_pack('!L', n)) # Packing class class Packer: " packer base class. supports basic byte/16bit/32bit/addr/string/name " def __init__(self): self.buf = '' self.index = {} def getbuf(self): return self.buf def addbyte(self, c): if len(c) != 1: raise TypeError, 'one character expected' self.buf = self.buf + c def addbytes(self, bytes): self.buf = self.buf + bytes def add16bit(self, n): self.buf = self.buf + pack16bit(n) def add32bit(self, n): self.buf = self.buf + pack32bit(n) def addaddr(self, addr): n = addr2bin(addr) self.buf = self.buf + pack32bit(n) def addstring(self, s): if len(s) > 255: raise ValueError, "Can't encode string of length "+ \ "%s (> 255)"%(len(s)) self.addbyte(chr(len(s))) self.addbytes(s) def addname(self, name): # Domain name packing (section 4.1.4) # Add a domain name to the buffer, possibly using pointers. # The case of the first occurrence of a name is preserved. # One trailing dot is ignored. if name.endswith('.'): name = name[:-1] if name: nlist = name.split('.') for label in nlist: if not label: raise PackError, 'empty label' else: nlist = [] keys = [] for i in range(len(nlist)): key = '.'.join(nlist[i:]).upper() keys.append(key) if self.index.has_key(key): pointer = self.index[key] break else: i = len(nlist) pointer = None # Do it into temporaries first so exceptions don't # mess up self.index and self.buf buf = '' offset = len(self.buf) index = [] if DNS.LABEL_UTF8: enc = 'utf8' else: enc = DNS.LABEL_ENCODING for j in range(i): label = nlist[j] try: label = label.encode(enc) except UnicodeEncodeError: if not DNS.LABEL_UTF8: raise if not label.startswith('\ufeff'): label = '\ufeff'+label label = label.encode(enc) n = len(label) if n > 63: raise PackError, 'label too long' if offset + len(buf) < 0x3FFF: index.append((keys[j], offset + len(buf))) else: print 'DNS.Lib.Packer.addname:', print 'warning: pointer too big' buf = buf + (chr(n) + label) if pointer: buf = buf + pack16bit(pointer | 0xC000) else: buf = buf + '\0' self.buf = self.buf + buf for key, value in index: self.index[key] = value def dump(self): keys = self.index.keys() keys.sort() print '-'*40 for key in keys: print '%20s %3d' % (key, self.index[key]) print '-'*40 space = 1 for i in range(0, len(self.buf)+1, 2): if self.buf[i:i+2] == '**': if not space: print space = 1 continue space = 0 print '%4d' % i, for c in self.buf[i:i+2]: if ' ' < c < '\177': print ' %c' % c, else: print '%2d' % ord(c), print print '-'*40 # Unpacking class class Unpacker: def __init__(self, buf): self.buf = buf self.offset = 0 def getbyte(self): if self.offset >= len(self.buf): raise UnpackError, "Ran off end of data" c = self.buf[self.offset] self.offset = self.offset + 1 return c def getbytes(self, n): s = self.buf[self.offset : self.offset + n] if len(s) != n: raise UnpackError, 'not enough data left' self.offset = self.offset + n return s def get16bit(self): return unpack16bit(self.getbytes(2)) def get32bit(self): return unpack32bit(self.getbytes(4)) def getaddr(self): return bin2addr(self.get32bit()) def getstring(self): return self.getbytes(ord(self.getbyte())) def getname(self): # Domain name unpacking (section 4.1.4) c = self.getbyte() i = ord(c) if i & 0xC0 == 0xC0: d = self.getbyte() j = ord(d) pointer = ((i<<8) | j) & ~0xC000 save_offset = self.offset try: self.offset = pointer domain = self.getname() finally: self.offset = save_offset return domain if i == 0: return '' domain = self.getbytes(i) remains = self.getname() if not remains: return domain else: return domain + '.' + remains # Test program for packin/unpacking (section 4.1.4) def testpacker(): N = 2500 R = range(N) import timing # See section 4.1.4 of RFC 1035 timing.start() for i in R: p = Packer() p.addaddr('192.168.0.1') p.addbytes('*' * 20) p.addname('f.ISI.ARPA') p.addbytes('*' * 8) p.addname('Foo.F.isi.arpa') p.addbytes('*' * 18) p.addname('arpa') p.addbytes('*' * 26) p.addname('') timing.finish() print timing.milli(), "ms total for packing" print round(timing.milli() / i, 4), 'ms per packing' #p.dump() u = Unpacker(p.buf) u.getaddr() u.getbytes(20) u.getname() u.getbytes(8) u.getname() u.getbytes(18) u.getname() u.getbytes(26) u.getname() timing.start() for i in R: u = Unpacker(p.buf) res = (u.getaddr(), u.getbytes(20), u.getname(), u.getbytes(8), u.getname(), u.getbytes(18), u.getname(), u.getbytes(26), u.getname()) timing.finish() print timing.milli(), "ms total for unpacking" print round(timing.milli() / i, 4), 'ms per unpacking' #for item in res: print item # Pack/unpack RR toplevel format (section 3.2.1) class RRpacker(Packer): def __init__(self): Packer.__init__(self) self.rdstart = None def addRRheader(self, name, type, klass, ttl, *rest): self.addname(name) self.add16bit(type) self.add16bit(klass) self.add32bit(ttl) if rest: if rest[1:]: raise TypeError, 'too many args' rdlength = rest[0] else: rdlength = 0 self.add16bit(rdlength) self.rdstart = len(self.buf) def patchrdlength(self): rdlength = unpack16bit(self.buf[self.rdstart-2:self.rdstart]) if rdlength == len(self.buf) - self.rdstart: return rdata = self.buf[self.rdstart:] save_buf = self.buf ok = 0 try: self.buf = self.buf[:self.rdstart-2] self.add16bit(len(rdata)) self.buf = self.buf + rdata ok = 1 finally: if not ok: self.buf = save_buf def endRR(self): if self.rdstart is not None: self.patchrdlength() self.rdstart = None def getbuf(self): if self.rdstart is not None: self.patchrdlength() return Packer.getbuf(self) # Standard RRs (section 3.3) def addCNAME(self, name, klass, ttl, cname): self.addRRheader(name, Type.CNAME, klass, ttl) self.addname(cname) self.endRR() def addHINFO(self, name, klass, ttl, cpu, os): self.addRRheader(name, Type.HINFO, klass, ttl) self.addstring(cpu) self.addstring(os) self.endRR() def addMX(self, name, klass, ttl, preference, exchange): self.addRRheader(name, Type.MX, klass, ttl) self.add16bit(preference) self.addname(exchange) self.endRR() def addNS(self, name, klass, ttl, nsdname): self.addRRheader(name, Type.NS, klass, ttl) self.addname(nsdname) self.endRR() def addPTR(self, name, klass, ttl, ptrdname): self.addRRheader(name, Type.PTR, klass, ttl) self.addname(ptrdname) self.endRR() def addSOA(self, name, klass, ttl, mname, rname, serial, refresh, retry, expire, minimum): self.addRRheader(name, Type.SOA, klass, ttl) self.addname(mname) self.addname(rname) self.add32bit(serial) self.add32bit(refresh) self.add32bit(retry) self.add32bit(expire) self.add32bit(minimum) self.endRR() def addTXT(self, name, klass, ttl, tlist): self.addRRheader(name, Type.TXT, klass, ttl) if type(tlist) is types.StringType: tlist = [tlist] for txtdata in tlist: self.addstring(txtdata) self.endRR() # Internet specific RRs (section 3.4) -- class = IN def addA(self, name, klass, ttl, address): self.addRRheader(name, Type.A, klass, ttl) self.addaddr(address) self.endRR() def addWKS(self, name, ttl, address, protocol, bitmap): self.addRRheader(name, Type.WKS, Class.IN, ttl) self.addaddr(address) self.addbyte(chr(protocol)) self.addbytes(bitmap) self.endRR() def addSRV(self): raise NotImplementedError def prettyTime(seconds): if seconds<60: return seconds,"%d seconds"%(seconds) if seconds<3600: return seconds,"%d minutes"%(seconds/60) if seconds<86400: return seconds,"%d hours"%(seconds/3600) if seconds<604800: return seconds,"%d days"%(seconds/86400) else: return seconds,"%d weeks"%(seconds/604800) class RRunpacker(Unpacker): def __init__(self, buf): Unpacker.__init__(self, buf) self.rdend = None def getRRheader(self): name = self.getname() rrtype = self.get16bit() klass = self.get16bit() ttl = self.get32bit() rdlength = self.get16bit() self.rdend = self.offset + rdlength return (name, rrtype, klass, ttl, rdlength) def endRR(self): if self.offset != self.rdend: raise UnpackError, 'end of RR not reached' def getCNAMEdata(self): return self.getname() def getHINFOdata(self): return self.getstring(), self.getstring() def getMXdata(self): return self.get16bit(), self.getname() def getNSdata(self): return self.getname() def getPTRdata(self): return self.getname() def getSOAdata(self): return self.getname(), \ self.getname(), \ ('serial',)+(self.get32bit(),), \ ('refresh ',)+prettyTime(self.get32bit()), \ ('retry',)+prettyTime(self.get32bit()), \ ('expire',)+prettyTime(self.get32bit()), \ ('minimum',)+prettyTime(self.get32bit()) def getTXTdata(self): txt = [] while self.offset != self.rdend: txt.append(self.getstring()) return txt getSPFdata = getTXTdata def getAdata(self): return self.getaddr() def getWKSdata(self): address = self.getaddr() protocol = ord(self.getbyte()) bitmap = self.getbytes(self.rdend - self.offset) return address, protocol, bitmap def getSRVdata(self): """ _Service._Proto.Name TTL Class SRV Priority Weight Port Target """ priority = self.get16bit() weight = self.get16bit() port = self.get16bit() target = self.getname() #print '***priority, weight, port, target', priority, weight, port, target return priority, weight, port, target # Pack/unpack Message Header (section 4.1) class Hpacker(Packer): def addHeader(self, id, qr, opcode, aa, tc, rd, ra, z, rcode, qdcount, ancount, nscount, arcount): self.add16bit(id) self.add16bit((qr&1)<<15 | (opcode&0xF)<<11 | (aa&1)<<10 | (tc&1)<<9 | (rd&1)<<8 | (ra&1)<<7 | (z&7)<<4 | (rcode&0xF)) self.add16bit(qdcount) self.add16bit(ancount) self.add16bit(nscount) self.add16bit(arcount) class Hunpacker(Unpacker): def getHeader(self): id = self.get16bit() flags = self.get16bit() qr, opcode, aa, tc, rd, ra, z, rcode = ( (flags>>15)&1, (flags>>11)&0xF, (flags>>10)&1, (flags>>9)&1, (flags>>8)&1, (flags>>7)&1, (flags>>4)&7, (flags>>0)&0xF) qdcount = self.get16bit() ancount = self.get16bit() nscount = self.get16bit() arcount = self.get16bit() return (id, qr, opcode, aa, tc, rd, ra, z, rcode, qdcount, ancount, nscount, arcount) # Pack/unpack Question (section 4.1.2) class Qpacker(Packer): def addQuestion(self, qname, qtype, qclass): self.addname(qname) self.add16bit(qtype) self.add16bit(qclass) class Qunpacker(Unpacker): def getQuestion(self): return self.getname(), self.get16bit(), self.get16bit() # Pack/unpack Message(section 4) # NB the order of the base classes is important for __init__()! class Mpacker(RRpacker, Qpacker, Hpacker): pass class Munpacker(RRunpacker, Qunpacker, Hunpacker): pass # Routines to print an unpacker to stdout, for debugging. # These affect the unpacker's current position! def dumpM(u): print 'HEADER:', (id, qr, opcode, aa, tc, rd, ra, z, rcode, qdcount, ancount, nscount, arcount) = u.getHeader() print 'id=%d,' % id, print 'qr=%d, opcode=%d, aa=%d, tc=%d, rd=%d, ra=%d, z=%d, rcode=%d,' \ % (qr, opcode, aa, tc, rd, ra, z, rcode) if tc: print '*** response truncated! ***' if rcode: print '*** nonzero error code! (%d) ***' % rcode print ' qdcount=%d, ancount=%d, nscount=%d, arcount=%d' \ % (qdcount, ancount, nscount, arcount) for i in range(qdcount): print 'QUESTION %d:' % i, dumpQ(u) for i in range(ancount): print 'ANSWER %d:' % i, dumpRR(u) for i in range(nscount): print 'AUTHORITY RECORD %d:' % i, dumpRR(u) for i in range(arcount): print 'ADDITIONAL RECORD %d:' % i, dumpRR(u) class DnsResult: def __init__(self,u,args): self.header={} self.questions=[] self.answers=[] self.authority=[] self.additional=[] self.args=args self.storeM(u) def show(self): import time print '; <<>> PDG.py 1.0 <<>> %s %s'%(self.args['name'], self.args['qtype']) opt="" if self.args['rd']: opt=opt+'recurs ' h=self.header print ';; options: '+opt print ';; got answer:' print ';; ->>HEADER<<- opcode %s, status %s, id %d'%( h['opcode'],h['status'],h['id']) flags=filter(lambda x,h=h:h[x],('qr','aa','rd','ra','tc')) print ';; flags: %s; Ques: %d, Ans: %d, Auth: %d, Addit: %d'%( ''.join(flags),h['qdcount'],h['ancount'],h['nscount'], h['arcount']) print ';; QUESTIONS:' for q in self.questions: print ';; %s, type = %s, class = %s'%(q['qname'],q['qtypestr'], q['qclassstr']) print print ';; ANSWERS:' for a in self.answers: print '%-20s %-6s %-6s %s'%(a['name'],`a['ttl']`,a['typename'], a['data']) print print ';; AUTHORITY RECORDS:' for a in self.authority: print '%-20s %-6s %-6s %s'%(a['name'],`a['ttl']`,a['typename'], a['data']) print print ';; ADDITIONAL RECORDS:' for a in self.additional: print '%-20s %-6s %-6s %s'%(a['name'],`a['ttl']`,a['typename'], a['data']) print if self.args.has_key('elapsed'): print ';; Total query time: %d msec'%self.args['elapsed'] print ';; To SERVER: %s'%(self.args['server']) print ';; WHEN: %s'%time.ctime(time.time()) def storeM(self,u): (self.header['id'], self.header['qr'], self.header['opcode'], self.header['aa'], self.header['tc'], self.header['rd'], self.header['ra'], self.header['z'], self.header['rcode'], self.header['qdcount'], self.header['ancount'], self.header['nscount'], self.header['arcount']) = u.getHeader() self.header['opcodestr']=Opcode.opcodestr(self.header['opcode']) self.header['status']=Status.statusstr(self.header['rcode']) for i in range(self.header['qdcount']): #print 'QUESTION %d:' % i, self.questions.append(self.storeQ(u)) for i in range(self.header['ancount']): #print 'ANSWER %d:' % i, self.answers.append(self.storeRR(u)) for i in range(self.header['nscount']): #print 'AUTHORITY RECORD %d:' % i, self.authority.append(self.storeRR(u)) for i in range(self.header['arcount']): #print 'ADDITIONAL RECORD %d:' % i, self.additional.append(self.storeRR(u)) def storeQ(self,u): q={} q['qname'], q['qtype'], q['qclass'] = u.getQuestion() q['qtypestr']=Type.typestr(q['qtype']) q['qclassstr']=Class.classstr(q['qclass']) return q def storeRR(self,u): r={} r['name'],r['type'],r['class'],r['ttl'],r['rdlength'] = u.getRRheader() r['typename'] = Type.typestr(r['type']) r['classstr'] = Class.classstr(r['class']) #print 'name=%s, type=%d(%s), class=%d(%s), ttl=%d' \ # % (name, # type, typename, # klass, Class.classstr(class), # ttl) mname = 'get%sdata' % r['typename'] if hasattr(u, mname): r['data']=getattr(u, mname)() else: r['data']=u.getbytes(r['rdlength']) return r def dumpQ(u): qname, qtype, qclass = u.getQuestion() print 'qname=%s, qtype=%d(%s), qclass=%d(%s)' \ % (qname, qtype, Type.typestr(qtype), qclass, Class.classstr(qclass)) def dumpRR(u): name, type, klass, ttl, rdlength = u.getRRheader() typename = Type.typestr(type) print 'name=%s, type=%d(%s), class=%d(%s), ttl=%d' \ % (name, type, typename, klass, Class.classstr(klass), ttl) mname = 'get%sdata' % typename if hasattr(u, mname): print ' formatted rdata:', getattr(u, mname)() else: print ' binary rdata:', u.getbytes(rdlength) if __name__ == "__main__": testpacker() # # $Log: Lib.py,v $ # Revision 1.11.2.11 2011/11/23 17:07:14 customdesigned # Rename some args in DNS.Lib to match py3dns. # # Revision 1.11.2.10 2011/08/04 22:23:55 customdesigned # Allow empty label at end (trailing dot), so now '.' can lookup the # root zone again. # # Revision 1.11.2.9 2011/03/21 21:03:22 customdesigned # Get rid of obsolete string module # # Revision 1.11.2.8 2011/03/16 20:06:39 customdesigned # Refer to explicit LICENSE file. # # Revision 1.11.2.7 2009/06/09 18:39:06 customdesigned # Built-in SPF support # # Revision 1.11.2.6 2008/10/15 22:34:06 customdesigned # Default to idna encoding. # # Revision 1.11.2.5 2008/09/17 17:35:14 customdesigned # Use 7-bit ascii encoding, because case folding needs to be disabled # before utf8 is safe to use, even experimentally. # # Revision 1.11.2.4 2008/09/17 16:09:53 customdesigned # Encode unicode labels as UTF-8 # # Revision 1.11.2.3 2007/05/22 20:27:40 customdesigned # Fix unpacker underflow. # # Revision 1.11.2.2 2007/05/22 20:25:53 customdesigned # Use socket.inetntoa,inetaton. # # Revision 1.11.2.1 2007/05/22 20:20:39 customdesigned # Mark utf-8 encoding # # Revision 1.11 2002/03/19 13:05:02 anthonybaxter # converted to class based exceptions (there goes the python1.4 compatibility :) # # removed a quite gross use of 'eval()'. # # Revision 1.10 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.9 2002/03/19 10:30:33 anthonybaxter # first round of major bits and pieces. The major stuff here (summarised # from my local, off-net CVS server :/ this will cause some oddities with # the # # tests/testPackers.py: # a large slab of unit tests for the packer and unpacker code in DNS.Lib # # DNS/Lib.py: # placeholder for addSRV. # added 'klass' to addA, make it the same as the other A* records. # made addTXT check for being passed a string, turn it into a length 1 list. # explicitly check for adding a string of length > 255 (prohibited). # a bunch of cleanups from a first pass with pychecker # new code for pack/unpack. the bitwise stuff uses struct, for a smallish # (disappointly small, actually) improvement, while addr2bin is much # much faster now. # # DNS/Base.py: # added DiscoverNameServers. This automatically does the right thing # on unix/ win32. No idea how MacOS handles this. *sigh* # Incompatible change: Don't use ParseResolvConf on non-unix, use this # function, instead! # a bunch of cleanups from a first pass with pychecker # # Revision 1.8 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.7 2001/07/19 07:50:44 anthony # Added SRV (RFC 2782) support. Code from Michael Ströder. # # Revision 1.6 2001/07/19 07:39:18 anthony # 'type' -> 'rrtype' in getRRheader(). Fix from Michael Ströder. # # Revision 1.5 2001/07/19 07:34:19 anthony # oops. glitch in storeRR (fixed now). # Reported by Bastian Kleineidam and by greg lin. # # Revision 1.4 2001/07/19 07:16:42 anthony # Changed (opcode&0xF)<<11 to (opcode*0xF)<<11. # Patch from Timothy J. Miller. # # Revision 1.3 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/Opcode.py0000644000160600001450000000222611540214117013746 0ustar stuartbms""" $Id: Opcode.py,v 1.6.2.1 2011/03/16 20:06:39 customdesigned Exp $ This file is part of the pydns project. Homepage: http://pydns.sourceforge.net This code is covered by the standard Python License. See LICENSE for details. Opcode values in message header. RFC 1035, 1996, 2136. """ QUERY = 0 IQUERY = 1 STATUS = 2 NOTIFY = 4 UPDATE = 5 # Construct reverse mapping dictionary _names = dir() opcodemap = {} for _name in _names: if _name[0] != '_': opcodemap[eval(_name)] = _name def opcodestr(opcode): if opcodemap.has_key(opcode): return opcodemap[opcode] else: return `opcode` # # $Log: Opcode.py,v $ # Revision 1.6.2.1 2011/03/16 20:06:39 customdesigned # Refer to explicit LICENSE file. # # Revision 1.6 2002/04/23 10:51:43 anthonybaxter # Added UPDATE, NOTIFY. # # Revision 1.5 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.4 2002/03/19 12:26:13 anthonybaxter # death to leading tabs. # # Revision 1.3 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/Status.py0000644000160600001450000000445311540214117014024 0ustar stuartbms""" $Id: Status.py,v 1.7.2.1 2011/03/16 20:06:39 customdesigned Exp $ This file is part of the pydns project. Homepage: http://pydns.sourceforge.net This code is covered by the standard Python License. See LICENSE for details. Status values in message header """ NOERROR = 0 # No Error [RFC 1035] FORMERR = 1 # Format Error [RFC 1035] SERVFAIL = 2 # Server Failure [RFC 1035] NXDOMAIN = 3 # Non-Existent Domain [RFC 1035] NOTIMP = 4 # Not Implemented [RFC 1035] REFUSED = 5 # Query Refused [RFC 1035] YXDOMAIN = 6 # Name Exists when it should not [RFC 2136] YXRRSET = 7 # RR Set Exists when it should not [RFC 2136] NXRRSET = 8 # RR Set that should exist does not [RFC 2136] NOTAUTH = 9 # Server Not Authoritative for zone [RFC 2136] NOTZONE = 10 # Name not contained in zone [RFC 2136] BADVERS = 16 # Bad OPT Version [RFC 2671] BADSIG = 16 # TSIG Signature Failure [RFC 2845] BADKEY = 17 # Key not recognized [RFC 2845] BADTIME = 18 # Signature out of time window [RFC 2845] BADMODE = 19 # Bad TKEY Mode [RFC 2930] BADNAME = 20 # Duplicate key name [RFC 2930] BADALG = 21 # Algorithm not supported [RFC 2930] # Construct reverse mapping dictionary _names = dir() statusmap = {} for _name in _names: if _name[0] != '_': statusmap[eval(_name)] = _name def statusstr(status): if statusmap.has_key(status): return statusmap[status] else: return `status` # # $Log: Status.py,v $ # Revision 1.7.2.1 2011/03/16 20:06:39 customdesigned # Refer to explicit LICENSE file. # # Revision 1.7 2002/04/23 12:52:19 anthonybaxter # cleanup whitespace. # # Revision 1.6 2002/04/23 10:57:57 anthonybaxter # update to complete the list of response codes. # # Revision 1.5 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.4 2002/03/19 12:26:13 anthonybaxter # death to leading tabs. # # Revision 1.3 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/Type.py0000644000160600001450000000475611540214117013470 0ustar stuartbms# -*- encoding: utf-8 -*- """ $Id: Type.py,v 1.6.2.3 2011/03/16 20:06:39 customdesigned Exp $ This file is part of the pydns project. Homepage: http://pydns.sourceforge.net This code is covered by the standard Python License. See LICENSE for details. TYPE values (section 3.2.2) """ A = 1 # a host address NS = 2 # an authoritative name server MD = 3 # a mail destination (Obsolete - use MX) MF = 4 # a mail forwarder (Obsolete - use MX) CNAME = 5 # the canonical name for an alias SOA = 6 # marks the start of a zone of authority MB = 7 # a mailbox domain name (EXPERIMENTAL) MG = 8 # a mail group member (EXPERIMENTAL) MR = 9 # a mail rename domain name (EXPERIMENTAL) NULL = 10 # a null RR (EXPERIMENTAL) WKS = 11 # a well known service description PTR = 12 # a domain name pointer HINFO = 13 # host information MINFO = 14 # mailbox or mail list information MX = 15 # mail exchange TXT = 16 # text strings AAAA = 28 # IPv6 AAAA records (RFC 1886) SRV = 33 # DNS RR for specifying the location of services (RFC 2782) SPF = 99 # TXT RR for Sender Policy Framework # Additional TYPE values from host.c source UNAME = 110 MP = 240 # QTYPE values (section 3.2.3) AXFR = 252 # A request for a transfer of an entire zone MAILB = 253 # A request for mailbox-related records (MB, MG or MR) MAILA = 254 # A request for mail agent RRs (Obsolete - see MX) ANY = 255 # A request for all records # Construct reverse mapping dictionary _names = dir() typemap = {} for _name in _names: if _name[0] != '_': typemap[eval(_name)] = _name def typestr(type): if typemap.has_key(type): return typemap[type] else: return `type` # # $Log: Type.py,v $ # Revision 1.6.2.3 2011/03/16 20:06:39 customdesigned # Refer to explicit LICENSE file. # # Revision 1.6.2.2 2009/06/09 18:39:06 customdesigned # Built-in SPF support # # Revision 1.6.2.1 2007/05/22 20:20:39 customdesigned # Mark utf-8 encoding # # Revision 1.6 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.5 2002/03/19 12:26:13 anthonybaxter # death to leading tabs. # # Revision 1.4 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.3 2001/07/19 07:38:28 anthony # added type code for SRV. From Michael Ströder. # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/__init__.py0000644000160600001450000000416211713063741014304 0ustar stuartbms# -*- encoding: utf-8 -*- # $Id: __init__.py,v 1.8.2.10 2012/02/03 23:04:01 customdesigned Exp $ # # This file is part of the pydns project. # Homepage: http://pydns.sourceforge.net # # This code is covered by the standard Python License. See LICENSE for details. # # __init__.py for DNS class. __version__ = '2.3.6' import Type,Opcode,Status,Class from Base import DnsRequest, DNSError from Lib import DnsResult from Base import * from Lib import * Error=DNSError from lazy import * Request = DnsRequest Result = DnsResult # # $Log: __init__.py,v $ # Revision 1.8.2.10 2012/02/03 23:04:01 customdesigned # Release 2.3.6 # # Revision 1.8.2.9 2011/03/16 20:06:39 customdesigned # Refer to explicit LICENSE file. # # Revision 1.8.2.8 2011/03/03 21:57:15 customdesigned # Release 2.3.5 # # Revision 1.8.2.7 2009/06/09 18:05:29 customdesigned # Release 2.3.4 # # Revision 1.8.2.6 2008/08/01 04:01:25 customdesigned # Release 2.3.3 # # Revision 1.8.2.5 2008/07/28 02:11:07 customdesigned # Bump version. # # Revision 1.8.2.4 2008/07/28 00:17:10 customdesigned # Randomize source ports. # # Revision 1.8.2.3 2008/07/24 20:10:55 customdesigned # Randomize tid in requests, and check in response. # # Revision 1.8.2.2 2007/05/22 21:06:52 customdesigned # utf-8 in __init__.py # # Revision 1.8.2.1 2007/05/22 20:39:20 customdesigned # Release 2.3.1 # # Revision 1.8 2002/05/06 06:17:49 anthonybaxter # found that the old README file called itself release 2.2. So make # this one 2.3... # # Revision 1.7 2002/05/06 06:16:15 anthonybaxter # make some sort of reasonable version string. releasewards ho! # # Revision 1.6 2002/03/19 13:05:02 anthonybaxter # converted to class based exceptions (there goes the python1.4 compatibility :) # # removed a quite gross use of 'eval()'. # # Revision 1.5 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.4 2001/11/26 17:57:51 stroeder # Added __version__ # # Revision 1.3 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/lazy.py0000644000160600001450000000571711663224743013540 0ustar stuartbms# $Id: lazy.py,v 1.5.2.7 2011/11/23 17:14:11 customdesigned Exp $ # # This file is part of the pydns project. # Homepage: http://pydns.sourceforge.net # # This code is covered by the standard Python License. See LICENSE for details. # # routines for lazy people. import Base from Base import ServerError def revlookup(name): "convenience routine for doing a reverse lookup of an address" names = revlookupall(name) if not names: return None return names[0] # return shortest name def revlookupall(name): "convenience routine for doing a reverse lookup of an address" # FIXME: check for IPv6 a = name.split('.') a.reverse() b = '.'.join(a)+'.in-addr.arpa' names = dnslookup(b, qtype = 'ptr') # this will return all records. names.sort(key=str.__len__) return names def dnslookup(name,qtype): "convenience routine to return just answer data for any query type" if Base.defaults['server'] == []: Base.DiscoverNameServers() result = Base.DnsRequest(name=name, qtype=qtype).req() if result.header['status'] != 'NOERROR': raise ServerError("DNS query status: %s" % result.header['status'], result.header['rcode']) elif len(result.answers) == 0 and Base.defaults['server_rotate']: # check with next DNS server result = Base.DnsRequest(name=name, qtype=qtype).req() if result.header['status'] != 'NOERROR': raise ServerError("DNS query status: %s" % result.header['status'], result.header['rcode']) return [x['data'] for x in result.answers] def mxlookup(name): """ convenience routine for doing an MX lookup of a name. returns a sorted list of (preference, mail exchanger) records """ l = dnslookup(name, qtype = 'mx') l.sort() return l # # $Log: lazy.py,v $ # Revision 1.5.2.7 2011/11/23 17:14:11 customdesigned # Apply patch 3388075 from sourceforge: raise subclasses of DNSError. # # Revision 1.5.2.6 2011/03/21 21:06:47 customdesigned # Replace map() with list comprehensions. # # Revision 1.5.2.5 2011/03/21 21:03:22 customdesigned # Get rid of obsolete string module # # Revision 1.5.2.4 2011/03/19 22:15:01 customdesigned # Added rotation of name servers - SF Patch ID: 2795929 # # Revision 1.5.2.3 2011/03/16 20:06:24 customdesigned # Expand convenience methods. # # Revision 1.5.2.2 2011/03/08 21:06:42 customdesigned # Address sourceforge patch requests 2981978, 2795932 to add revlookupall # and raise DNSError instead of IndexError on server fail. # # Revision 1.5.2.1 2007/05/22 20:23:38 customdesigned # Lazy call to DiscoverNameServers # # Revision 1.5 2002/05/06 06:14:38 anthonybaxter # reformat, move import to top of file. # # Revision 1.4 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.3 2001/08/09 09:08:55 anthonybaxter # added identifying header to top of each file # # Revision 1.2 2001/07/19 06:57:07 anthony # cvs keywords added # # pydns-2.3.6/DNS/win32dns.py0000644000160600001450000001160011541736747014223 0ustar stuartbms""" $Id: win32dns.py,v 1.3.2.3 2011/03/21 21:06:47 customdesigned Exp $ Extract a list of TCP/IP name servers from the registry 0.1 0.1 Strobl 2001-07-19 Usage: RegistryResolve() returns a list of ip numbers (dotted quads), by scouring the registry for addresses of name servers Tested on Windows NT4 Server SP6a, Windows 2000 Pro SP2 and Whistler Pro (XP) Build 2462 and Windows ME ... all having a different registry layout wrt name servers :-/ Todo: Program doesn't check whether an interface is up or down (c) 2001 Copyright by Wolfgang Strobl ws@mystrobl.de, License analog to the current Python license """ import re import _winreg def binipdisplay(s): "convert a binary array of ip adresses to a python list" if len(s)%4!= 0: raise EnvironmentError # well ... ol=[] for i in range(len(s)/4): s1=s[:4] s=s[4:] ip=[] for j in s1: ip.append(str(ord(j))) ol.append('.'.join(ip)) return ol def stringdisplay(s): '''convert "d.d.d.d,d.d.d.d" to ["d.d.d.d","d.d.d.d"]. also handle u'd.d.d.d d.d.d.d', as reporting on SF ''' import re return [str(x) for x in re.split("[ ,]",s)] def RegistryResolve(): nameservers=[] x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE) try: y= _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters") except EnvironmentError: # so it isn't NT/2000/XP # windows ME, perhaps? try: # for Windows ME y= _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Services\VxD\MSTCP") nameserver,dummytype=_winreg.QueryValueEx(y,'NameServer') if nameserver and not (nameserver in nameservers): nameservers.extend(stringdisplay(nameserver)) except EnvironmentError: pass return nameservers # no idea try: nameserver = _winreg.QueryValueEx(y, "DhcpNameServer")[0].split() except: nameserver = _winreg.QueryValueEx(y, "NameServer")[0].split() if nameserver: nameservers=nameserver nameserver = _winreg.QueryValueEx(y,"NameServer")[0] _winreg.CloseKey(y) try: # for win2000 y= _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DNSRegisteredAdapters") for i in range(1000): try: n=_winreg.EnumKey(y,i) z=_winreg.OpenKey(y,n) dnscount,dnscounttype=_winreg.QueryValueEx(z, 'DNSServerAddressCount') dnsvalues,dnsvaluestype=_winreg.QueryValueEx(z, 'DNSServerAddresses') nameservers.extend(binipdisplay(dnsvalues)) _winreg.CloseKey(z) except EnvironmentError: break _winreg.CloseKey(y) except EnvironmentError: pass # try: # for whistler y= _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces") for i in range(1000): try: n=_winreg.EnumKey(y,i) z=_winreg.OpenKey(y,n) try: nameserver,dummytype=_winreg.QueryValueEx(z,'NameServer') if nameserver and not (nameserver in nameservers): nameservers.extend(stringdisplay(nameserver)) except EnvironmentError: pass _winreg.CloseKey(z) except EnvironmentError: break _winreg.CloseKey(y) except EnvironmentError: #print "Key Interfaces not found, just do nothing" pass # _winreg.CloseKey(x) return nameservers if __name__=="__main__": print "Name servers:",RegistryResolve() # # $Log: win32dns.py,v $ # Revision 1.3.2.3 2011/03/21 21:06:47 customdesigned # Replace map() with list comprehensions. # # Revision 1.3.2.2 2011/03/21 21:03:22 customdesigned # Get rid of obsolete string module # # Revision 1.3.2.1 2007/05/22 20:26:49 customdesigned # Fix win32 nameserver discovery. # # Revision 1.3 2002/05/06 06:15:31 anthonybaxter # apparently some versions of windows return servers as unicode # string with space sep, rather than strings with comma sep. # *sigh* # # Revision 1.2 2002/03/19 12:41:33 anthonybaxter # tabnannied and reindented everything. 4 space indent, no tabs. # yay. # # Revision 1.1 2001/08/09 09:22:28 anthonybaxter # added what I hope is win32 resolver lookup support. I'll need to try # and figure out how to get the CVS checkout onto my windows machine to # make sure it works (wow, doing something other than games on the # windows machine :) # # Code from Wolfgang.Strobl@gmd.de # win32dns.py from # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66260 # # Really, ParseResolvConf() should be renamed "FindNameServers" or # some such. # # pydns-2.3.6/tests/0000755000160600001450000000000011713073473012711 5ustar stuartbmspydns-2.3.6/tests/test.py0000755000160600001450000000126711540277620014251 0ustar stuartbms#!/usr/bin/python import sys ; sys.path.insert(0, '..') import DNS # automatically load nameserver(s) from /etc/resolv.conf # (works on unix - on others, YMMV) DNS.ParseResolvConf() # lets do an all-in-one request # set up the request object r = DNS.DnsRequest(name='munnari.oz.au',qtype='A') # do the request a=r.req() # and do a pretty-printed output a.show() # now lets setup a reusable request object r = DNS.DnsRequest(qtype='ANY') res = r.req("a.root-servers.nex",qtype='ANY') res.show() res = r.req("proxy.connect.com.au") res.show() # do a TCP reply r = DNS.DnsRequest("imsavscan.netvigator.com", qtype="A", server=['8.8.8.8'], protocol='tcp', timeout=300) res = r.req() res.show() pydns-2.3.6/tests/test2.py0000755000160600001450000000054610624650522014330 0ustar stuartbms#!/usr/bin/python import sys ; sys.path.insert(0, '..') import DNS # automatically load nameserver(s) from /etc/resolv.conf # (works on unix - on others, YMMV) DNS.ParseResolvConf() r=DNS.Request(qtype='mx') res = r.req('connect.com.au') res.show() r=DNS.Request(qtype='soa') res = r.req('connect.com.au') res.show() print DNS.revlookup('192.189.54.17') pydns-2.3.6/tests/test4.py0000755000160600001450000000020210624650522014317 0ustar stuartbms#!/usr/bin/python import sys ; sys.path.insert(0, '..') import DNS DNS.ParseResolvConf() print DNS.mxlookup("connect.com.au") pydns-2.3.6/tests/test5.py0000755000160600001450000000347411663223514014337 0ustar stuartbms#!/usr/bin/python import sys ; sys.path.insert(0, '..') import DNS DNS.ParseResolvConf() def Error(mesg): import sys print sys.argv[0],"ERROR:" print mesg sys.exit(1) def main(): import sys if len(sys.argv) != 2: Error("usage: %s somedomain.com"%sys.argv[0]) domain = sys.argv[1] nslist = GetNS(domain) print "According to the primary, the following are nameservers for this domain" for ns in nslist: print " ",ns CheckNS(ns,domain) def GetNS(domain): import DNS # hm. this might fail if a server is off the air. r = DNS.Request(domain,qtype='SOA').req() if r.header['status'] != 'NOERROR': Error("received status of %s when attempting to look up SOA for domain"% (r.header['status'])) primary,email,serial,refresh,retry,expire,minimum = r.answers[0]['data'] print "Primary nameserver for domain %s is: %s"%(domain,primary) r = DNS.Request(domain,qtype='NS',server=primary,aa=1).req() if r.header['status'] != 'NOERROR': Error("received status of %s when attempting to query %s for NSs"% (r.header['status'])) if r.header['aa'] != 1: Error("primary NS %s doesn't believe that it's authoritative!"% primary) nslist = map(lambda x:x['data'], r.answers) return nslist def CheckNS(nameserver,domain): r = DNS.Request(domain,qtype='SOA',server=nameserver,aa=1).req() if r.header['status'] != 'NOERROR': Error("received status of %s when attempting to query %s for NS"% (r.header['status'])) if r.header['aa'] != 1: Error("NS %s doesn't believe that it's authoritative!"% nameserver) primary,email,serial,refresh,retry,expire,minimum = r.answers[0]['data'] print " NS has serial",serial[1] if __name__ == "__main__": main() pydns-2.3.6/tests/testPackers.py0000644000160600001450000003715507445630732015572 0ustar stuartbms#!/usr/bin/env python # # Tests of the packet assembler/disassembler routines. # # only tests the simple packers for now. next is to test the # classes: Hpacker/Hunpacker, # Qpacker/Unpacker, then Mpacker/Munpacker # # Start doing unpleasant tests with broken data, truncations, that # sort of thing. import DNS import unittest TestCompleted = "TestCompleted" # exc. class Int16Packing(unittest.TestCase): knownValues = ( ( 10, '\x00\n'), ( 500, '\x01\xf4' ), ( 5340, '\x14\xdc' ), ( 51298, '\xc8b'), ( 65535, '\xff\xff'), ) def test16bitPacking(self): """ pack16bit should give known output for known input """ for i,s in self.knownValues: result = DNS.Lib.pack16bit(i) self.assertEqual(s,result) def test16bitUnpacking(self): """ unpack16bit should give known output for known input """ for i,s in self.knownValues: result = DNS.Lib.unpack16bit(s) self.assertEqual(i,result) class Int32Packing(unittest.TestCase): knownValues = ( ( 10, '\x00\x00\x00\n'), ( 500, '\x00\x00\x01\xf4' ), ( 5340, '\x00\x00\x14\xdc' ), ( 51298, '\x00\x00\xc8b'), ( 65535, '\x00\x00\xff\xff'), ( 33265535, '\x01\xfb\x97\x7f' ), ( 147483647, '\x08\xcak\xff' ), ( 2147483647, '\x7f\xff\xff\xff' ), ) def test32bitPacking(self): """ pack32bit should give known output for known input """ for i,s in self.knownValues: result = DNS.Lib.pack32bit(i) self.assertEqual(s,result) def test32bitUnpacking(self): """ unpack32bit should give known output for known input """ for i,s in self.knownValues: result = DNS.Lib.unpack32bit(s) self.assertEqual(i,result) class IPaddrPacking(unittest.TestCase): knownValues = ( ('127.0.0.1', 2130706433 ), ('10.99.23.13', 174266125 ), ('192.35.59.45', -1071432915 ), # signed, sigh ('255.255.255.255', -1), ) def testIPaddrPacking(self): """ addr2bin should give known output for known input """ for i,s in self.knownValues: result = DNS.Lib.addr2bin(i) self.assertEqual(s,result) def testIPaddrUnpacking(self): """ bin2addr should give known output for known input """ for i,s in self.knownValues: result = DNS.Lib.bin2addr(s) self.assertEqual(i,result) class PackerClassPacking(unittest.TestCase): knownPackValues = [ ( ['www.ekit.com'], '\x03www\x04ekit\x03com\x00' ), ( ['ns1.ekorp.com', 'ns2.ekorp.com', 'ns3.ekorp.com'], '\x03ns1\x05ekorp\x03com\x00\x03ns2\xc0\x04\x03ns3\xc0\x04'), ( ['a.root-servers.net.', 'b.root-servers.net.', 'c.root-servers.net.', 'd.root-servers.net.', 'e.root-servers.net.', 'f.root-servers.net.'], '\x01a\x0croot-servers\x03net\x00\x01b\xc0\x02\x01c\xc0'+ '\x02\x01d\xc0\x02\x01e\xc0\x02\x01f\xc0\x02' ), ] knownUnpackValues = [ ( ['www.ekit.com'], '\x03www\x04ekit\x03com\x00' ), ( ['ns1.ekorp.com', 'ns2.ekorp.com', 'ns3.ekorp.com'], '\x03ns1\x05ekorp\x03com\x00\x03ns2\xc0\x04\x03ns3\xc0\x04'), ( ['a.root-servers.net', 'b.root-servers.net', 'c.root-servers.net', 'd.root-servers.net', 'e.root-servers.net', 'f.root-servers.net'], '\x01a\x0croot-servers\x03net\x00\x01b\xc0\x02\x01c\xc0'+ '\x02\x01d\xc0\x02\x01e\xc0\x02\x01f\xc0\x02' ), ] def testPackNames(self): from DNS.Lib import Packer for namelist,result in self.knownPackValues: p = Packer() for n in namelist: p.addname(n) self.assertEqual(p.getbuf(),result) def testUnpackNames(self): from DNS.Lib import Unpacker for namelist,result in self.knownUnpackValues: u = Unpacker(result) names = [] for i in range(len(namelist)): n = u.getname() names.append(n) self.assertEqual(names, namelist) def testUnpackerLimitCheck(self): from DNS.Lib import Unpacker u=Unpacker('\x03ns1\x05ekorp\x03com\x00\x03ns2\xc0\x04\x03ns3\xc0\x04') u.getname() ; u.getname() ; u.getname() # 4th call should fail self.assertRaises(IndexError, u.getname) class testUnpackingMangled(unittest.TestCase): "addA(self, name, klass, ttl, address)" packerCorrect = '\x05www02\x04ekit\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x02' def testWithoutRR(self): u = DNS.Lib.RRunpacker(self.packerCorrect) u.getAdata() def testWithTwoRRs(self): u = DNS.Lib.RRunpacker(self.packerCorrect) u.getRRheader() self.assertRaises(DNS.Lib.UnpackError, u.getRRheader) def testWithNoGetData(self): u = DNS.Lib.RRunpacker(self.packerCorrect) u.getRRheader() self.assertRaises(DNS.Lib.UnpackError, u.endRR) class PackerTestCase(unittest.TestCase): " base class for tests of Packing code. Laziness on my part, I know. " def testPacker(self): from DNS.Lib import RRpacker p = RRpacker() check = self.doPack(p) if (p is not None) and (check is not TestCompleted): return self.checkPackResult(p) def checkPackResult(self, buf): if not hasattr(self, 'packerExpectedResult'): if self.__class__.__name__ != 'PackerTestCase': print "P***", self, repr(buf.getbuf()) #cheat testcase else: return self.assertEqual(buf.getbuf(), self.packerExpectedResult) def checkUnpackResult(self, rrbits, specbits): if not hasattr(self, 'unpackerExpectedResult'): if self.__class__.__name__ != 'PackerTestCase': print "U***", self, repr((rrbits,specbits)) #cheat testcase else: return self.assertEqual((rrbits, specbits), self.unpackerExpectedResult) def testUnpacker(self): from DNS.Lib import RRunpacker if self.doUnpack is not None: if hasattr(self.__class__, 'doUnpack') \ and hasattr(self, 'packerExpectedResult'): u = RRunpacker(self.packerExpectedResult) rrbits = u.getRRheader()[:4] specbits = self.doUnpack(u) try: u.endRR() except DNS.Lib.UnpackError: self.assertEqual(0, 'Not at end of RR!') return self.checkUnpackResult(rrbits, specbits) else: me = self.__class__.__name__ if me != 'PackerTestCase': self.assertEquals(self.__class__.__name__, 'Unpack NotImplemented') def doPack(self, p): " stub. don't test the base class " return None def doUnpack(self, p): " stub. don't test the base class " return None class testPackingOfCNAME(PackerTestCase): "addCNAME(self, name, klass, ttl, cname)" def doPack(self,p): p.addCNAME('www.sub.domain', DNS.Class.IN, 3600, 'realhost.sub.domain') def doUnpack(self, u): return u.getCNAMEdata() unpackerExpectedResult = (('www.sub.domain', DNS.Type.CNAME, DNS.Class.IN, 3600), 'realhost.sub.domain') packerExpectedResult = \ '\x03www\x03sub\x06domain\x00\x00\x05\x00\x01\x00'+ \ '\x00\x0e\x10\x00\x0b\x08realhost\xc0\x04' class testPackingOfCNAME2(PackerTestCase): "addCNAME(self, name, klass, ttl, cname)" def doPack(self,p): p.addCNAME('www.cust.com', DNS.Class.IN, 200, 'www023.big.isp.com') def doUnpack(self, u): return u.getCNAMEdata() unpackerExpectedResult = (('www.cust.com', DNS.Type.CNAME, DNS.Class.IN, 200), 'www023.big.isp.com') packerExpectedResult = \ '\x03www\x04cust\x03com\x00\x00\x05\x00\x01\x00'+ \ '\x00\x00\xc8\x00\x11\x06www023\x03big\x03isp\xc0\t' class testPackingOfCNAME3(PackerTestCase): "addCNAME(self, name, klass, ttl, cname)" def doPack(self,p): p.addCNAME('www.fred.com', DNS.Class.IN, 86400, 'webhost.loa.com') def doUnpack(self, u): return u.getCNAMEdata() unpackerExpectedResult = (('www.fred.com', DNS.Type.CNAME, DNS.Class.IN, 86400), 'webhost.loa.com') packerExpectedResult = \ '\x03www\x04fred\x03com\x00\x00\x05\x00\x01\x00\x01Q'+ \ '\x80\x00\x0e\x07webhost\x03loa\xc0\t' class testPackingOfHINFO(PackerTestCase): "addHINFO(self, name, klass, ttl, cpu, os)" def doPack(self,p): p.addHINFO('www.sub.domain.com', DNS.Class.IN, 3600, 'i686', 'linux') def doUnpack(self, u): return u.getHINFOdata() unpackerExpectedResult = (('www.sub.domain.com', 13, 1, 3600), ('i686', 'linux')) packerExpectedResult = \ '\x03www\x03sub\x06domain\x03com\x00\x00\r\x00\x01'+ \ '\x00\x00\x0e\x10\x00\x0b\x04i686\x05linux' class testPackingOfHINFO2(PackerTestCase): "addHINFO(self, name, klass, ttl, cpu, os)" def doPack(self,p): p.addHINFO('core1.lax.foo.com', DNS.Class.IN, 3600, 'cisco', 'ios') def doUnpack(self, u): return u.getHINFOdata() unpackerExpectedResult = (('core1.lax.foo.com', 13, 1, 3600), ('cisco', 'ios')) packerExpectedResult = \ '\x05core1\x03lax\x03foo\x03com\x00\x00\r\x00\x01'+ \ '\x00\x00\x0e\x10\x00\n\x05cisco\x03ios' class testPackingOfMX(PackerTestCase): "addMX(self, name, klass, ttl, preference, exchange)" def doPack(self, p): p.addMX('sub.domain.com', DNS.Class.IN, 86400, 10, 'mailhost1.isp.com') def doUnpack(self, u): return u.getMXdata() packerExpectedResult = \ '\x03sub\x06domain\x03com\x00\x00\x0f\x00\x01'+ \ '\x00\x01Q\x80\x00\x12\x00\n\tmailhost1\x03isp\xc0\x0b' unpackerExpectedResult = (('sub.domain.com', 15, 1, 86400), (10, 'mailhost1.isp.com')) class testPackingOfMX2(PackerTestCase): "addMX(self, name, klass, ttl, preference, exchange)" def doPack(self, p): p.addMX('ekit-inc.com.', DNS.Class.IN, 86400, 10, 'mx1.ekorp.com') p.addMX('ekit-inc.com.', DNS.Class.IN, 86400, 20, 'mx2.ekorp.com') p.addMX('ekit-inc.com.', DNS.Class.IN, 86400, 30, 'mx3.ekorp.com') def doUnpack(self, u): res = [u.getMXdata(),] dummy = u.getRRheader()[:4] res += u.getMXdata() dummy = u.getRRheader()[:4] res += u.getMXdata() return res unpackerExpectedResult = (('ekit-inc.com', 15, 1, 86400), [(10, 'mx1.ekorp.com'), 20, 'mx2.ekorp.com', 30, 'mx3.ekorp.com']) packerExpectedResult = \ '\x08ekit-inc\x03com\x00\x00\x0f\x00\x01\x00\x01Q\x80\x00'+\ '\x0e\x00\n\x03mx1\x05ekorp\xc0\t\x00\x00\x0f\x00\x01\x00'+\ '\x01Q\x80\x00\x08\x00\x14\x03mx2\xc0\x1e\x00\x00\x0f\x00'+\ '\x01\x00\x01Q\x80\x00\x08\x00\x1e\x03mx3\xc0\x1e' class testPackingOfNS(PackerTestCase): "addNS(self, name, klass, ttl, nsdname)" def doPack(self, p): p.addNS('ekit-inc.com', DNS.Class.IN, 86400, 'ns1.ekorp.com') def doUnpack(self, u): return u.getNSdata() unpackerExpectedResult = (('ekit-inc.com', 2, 1, 86400), 'ns1.ekorp.com') packerExpectedResult = '\x08ekit-inc\x03com\x00\x00\x02\x00\x01\x00\x01Q\x80\x00\x0c\x03ns1\x05ekorp\xc0\t' class testPackingOfPTR(PackerTestCase): "addPTR(self, name, klass, ttl, ptrdname)" def doPack(self, p): p.addPTR('www.ekit-inc.com', DNS.Class.IN, 3600, 'www-real01.ekorp.com') def doUnpack(self, u): return u.getPTRdata() unpackerExpectedResult = (('www.ekit-inc.com', 12, 1, 3600), 'www-real01.ekorp.com') packerExpectedResult = '\x03www\x08ekit-inc\x03com\x00\x00\x0c\x00\x01\x00\x00\x0e\x10\x00\x13\nwww-real01\x05ekorp\xc0\r' class testPackingOfSOA(PackerTestCase): """addSOA(self, name, klass, ttl, mname, rname, serial, refresh, retry, expire, minimum)""" def doPack(self, p): p.addSOA('ekit-inc.com', DNS.Class.IN, 3600, 'ns1.ekorp.com', 'hostmaster.ekit-inc.com', 2002020301, 100, 200, 300, 400) def doUnpack(self, u): return u.getSOAdata() unpackerExpectedResult = (('ekit-inc.com', 6, 1, 3600), ('ns1.ekorp.com', 'hostmaster', ('serial', 2002020301), ('refresh ', 100, '1 minutes'), ('retry', 200, '3 minutes'), ('expire', 300, '5 minutes'), ('minimum', 400, '6 minutes'))) packerExpectedResult = '\x08ekit-inc\x03com\x00\x00\x06\x00\x01\x00\x00\x0e\x10\x00,\x03ns1\x05ekorp\xc0\t\nhostmaster\x00wTg\xcd\x00\x00\x00d\x00\x00\x00\xc8\x00\x00\x01,\x00\x00\x01\x90' class testPackingOfA(PackerTestCase): "addA(self, name, klass, ttl, address)" def doPack(self, p): p.addA('www02.ekit.com', DNS.Class.IN, 86400, '192.168.10.2') def doUnpack(self, u): return u.getAdata() unpackerExpectedResult = (('www02.ekit.com', 1, 1, 86400), '192.168.10.2') packerExpectedResult = '\x05www02\x04ekit\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x02' class testPackingOfA2(PackerTestCase): "addA(self, name, ttl, address)" def doPack(self, p): p.addA('www.ekit.com', DNS.Class.IN, 86400, '10.98.1.0') def doUnpack(self, u): return u.getAdata() unpackerExpectedResult = (('www.ekit.com', 1, 1, 86400), '10.98.1.0') packerExpectedResult = '\x03www\x04ekit\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\nb\x01\x00' class testPackingOfA3(PackerTestCase): "addA(self, name, ttl, address)" def doPack(self, p): p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.4') p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.3') p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.2') p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.1') def doUnpack(self, u): u1,d1,u2,d2,u3,d3,u4=u.getAdata(),u.getRRheader(),u.getAdata(),u.getRRheader(),u.getAdata(),u.getRRheader(),u.getAdata() return u1,u2,u3,u4 unpackerExpectedResult = (('www.zol.com', 1, 1, 86400), ('192.168.10.4', '192.168.10.3', '192.168.10.2', '192.168.10.1')) packerExpectedResult = '\x03www\x03zol\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x04\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x03\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x02\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x01' class testPackingOfTXT(PackerTestCase): "addTXT(self, name, klass, ttl, list)" def doPack(self, p): p.addTXT('ekit-inc.com', DNS.Class.IN, 3600, 'this is a text record') def doUnpack(self, u): return u.getTXTdata() packerExpectedResult = '\x08ekit-inc\x03com\x00\x00\x10\x00\x01\x00\x00\x0e\x10\x00\x16\x15this is a text record' unpackerExpectedResult = (('ekit-inc.com', 16, 1, 3600), ['this is a text record']) # check what the maximum/minimum &c of TXT records are. class testPackingOfTXT2(PackerTestCase): "addTXT(self, name, klass, ttl, list)" def doPack(self, p): f = lambda p=p:p.addTXT('ekit-inc.com', DNS.Class.IN, 3600, 'the quick brown fox jumped over the lazy brown dog\n'*20) self.assertRaises(ValueError, f) return TestCompleted doUnpack = None #class testPackingOfQuestion(PackerTestCase): # "addQuestion(self, qname, qtype, qclass)" # def doPack(self, p): # self.assertEquals(0,"NotImplemented") if __name__ == "__main__": unittest.main() pydns-2.3.6/tests/testsrv.py0000755000160600001450000000043011663223465014777 0ustar stuartbms#!/usr/bin/python import sys ; sys.path.insert(0, '..') import DNS # automatically load nameserver(s) from /etc/resolv.conf # (works on unix - on others, YMMV) DNS.ParseResolvConf() r=DNS.Request(qtype='srv') res = r.req('_ldap._tcp.openldap.org') res.show() print res.answers pydns-2.3.6/tools/0000755000160600001450000000000011713073473012707 5ustar stuartbmspydns-2.3.6/tools/caching.py0000644000160600001450000000307707325506222014661 0ustar stuartbms# # From: KevinL # A simple dns answer cache - it's author notes: # "It's probably really bodgy code, tho - it was my early python..." # So don't send him abusive messages if you hate it. # class DNSCache: """ Covers the DNS object, keeps a cache of answers. Clumsy as hell. """ forCache = {} revCache = {} # cache failures for this long, in seconds negCache = 3600 def __init__(self): import DNS DNS.ParseResolvConf() def lookup(self,IP = None,name = None): import DNS now = time.time() if (not IP) and (not name): return None if IP: if type(IP) != type(''): return None a = string.split(IP, '.') a.reverse() name = string.join(a, '.')+'.in-addr.arpa' cache = self.revCache qt = 'ptr' else: if type(name) != type(''): return None cache = self.forCache qt = 'a' if cache.has_key(name): # Check if it's timed out or not if cache[name][1] < now: del(cache[name]) else: return(cache[name][0]) x = DNS.DnsRequest(name,qtype=qt) try: x.req() except: return 'Timeout' if len(x.response.answers) > 0: cache[name] = ( x.response.answers[0]['data'], x.time_finish + x.response.answers[0]['ttl']) else: cache[name] = (None,now+self.negCache) return cache[name][0] pydns-2.3.6/tools/named-perf.py0000755000160600001450000000302007325500603015267 0ustar stuartbms#!/opt/python/bin/python servers = [ "192.92.129.1", "192.189.54.17", # yarrina "192.189.54.33", # warrane "203.8.183.1", # yalumba "192.189.54.65", # gnamma "128.250.1.21", # munnari ] lookups = [ ( 'munnari.oz.au', 'A' ), ( 'connect.com.au', 'SOA' ), ( 'parc.xerox.com', 'MX' ), ( 'bogus.example.net', 'A'), ] rpts = 5 def main(): import DNS, timing, socket, time res = {} for server in servers: res[server] = [100000,0,0,0] # min,max,tot,failed for what,querytype in lookups: for count in range(rpts): for server in servers: d = DNS.DnsRequest(server=server,timeout=1) fail = 0 timing.start() try: r=d.req(name=what,qtype=querytype) except DNS.Error: fail = 1 timing.finish() if fail: res[server][3] = res[server][3] + 1 print "(failed)",res[server][3] if 0: if r.header['ancount'] == 0: print "WARNING: Server",server,"got no answers for", \ what, querytype t = timing.milli() print server,"took",t,"ms for",what,querytype res[server][0] = min(t,res[server][0]) res[server][1] = max(t,res[server][1]) res[server][2] = res[server][2] + t for server in servers: queries = rpts * len(lookups) r = res[server] print "%-30s %2d/%2d(%3.2f%%) %dms/%dms/%dms min/avg/max" % ( socket.gethostbyaddr(server)[0], queries - r[3], queries, ((queries-r[3])*100.0)/queries, r[0], r[2] / queries, r[1]) if __name__ == "__main__": main() pydns-2.3.6/CREDITS.txt0000644000160600001450000000074611541226233013405 0ustar stuartbmsThis code was originally based on the DNS library created by Guido van Rossum somewhere near the dawn of time. Since then, as well as myself (Anthony), I have had contributions by: Michael Ströder Bastian Kleineidam Timothy J. Miller Wolfgang Strobl Arnaud Fontaine Scott Kitterman Stuart Gathman It's possible there's other people - the old RCS logs for my code were lost some time ago. The list above is almost certainly incomplete - let me know if I've forgotten you... pydns-2.3.6/LICENSE0000644000160600001450000000773611621305330012555 0ustar stuartbmsPYDNS is Copyright 2000-2011 by Guido van Rossum, Michael Ströder , Anthony Baxter , Stuart Gathman , and Scott Kitterman This code is released under the following Python-style license: CNRI LICENSE AGREEMENT FOR PYDNS-2.3.5 1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 (“CNRI”), and the Individual or Organization (“Licensee”) accessing and otherwise using pydns-2.3.5 software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use pydns-2.3.5 alone or in any derivative version, provided, however, that CNRI’s License Agreement and CNRI’s notice of copyright, i.e., “Copyright © 1995-2001 Corporation for National Research Initiatives; All Rights Reserved” are retained in pydns-2.3.5 alone or in any derivative version prepared by Licensee. Alternately, in lieu of CNRI’s License Agreement, Licensee may substitute the following text (omitting the quotes): “pydns-2.3.5 is made available subject to the terms and conditions in CNRI’s License Agreement. This Agreement together with pydns-2.3.5 may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This Agreement may also be obtained from a proxy server on the Internet using the following URL: http://hdl.handle.net/1895.22/1013.” 3. In the event Licensee prepares a derivative work that is based on or incorporates pydns-2.3.5 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to pydns-2.3.5. 4. CNRI is making pydns-2.3.5 available to Licensee on an “AS IS” basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. This License Agreement shall be governed by the federal intellectual property law of the United States, including without limitation the federal copyright law, and, to the extent such U.S. federal law does not apply, by the law of the Commonwealth of Virginia, excluding Virginia’s conflict of law provisions. Notwithstanding the foregoing, with regard to derivative works based on pydns-2.3.5 that incorporate non-separable material that was previously distributed under the GNU General Public License (GPL), the law of the Commonwealth of Virginia shall govern this License Agreement only as to issues arising under or with respect to Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By clicking on the “ACCEPT” button where indicated, or by copying, installing or otherwise using pydns-2.3.5, Licensee agrees to be bound by the terms and conditions of this License Agreement. pydns-2.3.6/README-guido.txt0000644000160600001450000000101407465427677014370 0ustar stuartbmsThis directory contains a module (dnslib) that implements a DNS (Domain Name Server) client, plus additional modules that define some symbolic constants used by DNS (dnstype, dnsclass, dnsopcode). Type "python dnslib.py -/" for a usage message. You can also import dnslib and write your own, more sophisticated client code; use the test program as an example (there is currently no documentation :-). --Guido van Rossum, CWI, Amsterdam URL: pydns-2.3.6/README.txt0000644000160600001450000000725707465427677013302 0ustar stuartbmsRelease 2.3 Mon May 6 16:18:02 EST 2002 This is a another release of the pydns code, as originally written by Guido van Rossum, and with a hopefully nicer API bolted over the top of it by Anthony Baxter . This code is released under a Python-style license. I'm making this release because there hasn't been a release in a heck of a long time, and it probably deserves one. I'd also like to do a substantial refactor of some of the guts of the code, and this is likely to break any code that uses the existing interface. So this will be a release for people who are using the existing API... There are several known bugs/unfinished bits - processing of AXFR results is not done yet. - doesn't do IPv6 DNS requests (type AAAA) - docs, aside from this file - all sorts of other stuff that I've probably forgotten. - MacOS support for discovering nameservers - the API that I evolved some time ago is pretty ugly. I'm going to re-do it, designed this time. Stuff it _does_ do: - processes /etc/resolv.conf - at least as far as nameserver directives go. - tries multiple nameservers. - nicer API - see below. - returns results in more useful format. - optional timing of requests. - default 'show' behaviour emulates 'dig' pretty closely. To use: import DNS reqobj=DNS.Request(args) reqobj.req(args) args can be a name, in which case it takes that as the query, and/or a series of keyword/value args. (see below for a list of args) when calling the 'req()' method, it reuses the options specified in the DNS.Request() call as defaults. options are applied in the following order: those specified in the req() call or, if not specified there, those specified in the creation of the Request() object or, if not specified there, those specified in the DNS.defaults dictionary name servers can be specified in the following ways: - by calling DNS.DiscoverNameServers(), which will load the DNS servers from the system's /etc/resolv.conf file on Unix, or from the Registry on windows. - by specifying it as an option to the request - by manually setting DNS.defaults['server'] to a list of server IP addresses to try - XXXX It should be possible to load the DNS servers on a mac os machine, from where-ever they've squirrelled them away name="host.do.main" # the object being looked up qtype="SOA" # the query type, eg SOA, A, MX, CNAME, ANY protocol="udp" # "udp" or "tcp" - usually you want "udp" server="nameserver" # the name of the nameserver. Note that you might # want to use an IP address here rd=1 # "recursion desired" - defaults to 1. other: opcode, port, ... There's also some convenience functions, for the lazy: to do a reverse lookup: >>> print DNS.revlookup("192.189.54.17") yarrina.connect.com.au to look up all MX records for an entry: >>> print DNS.mxlookup("connect.com.au") [(10, 'yarrina.connect.com.au'), (100, 'warrane.connect.com.au')] Documentation of the rest of the interface will have to wait for a later date. Note that the DnsAsyncRequest stuff is currently not working - I haven't looked too closely at why, yet. There's some examples in the tests/ directory - including test5.py, which is even vaguely useful. It looks for the SOA for a domain, checks that the primary NS is authoritative, then checks the nameservers that it believes are NSs for the domain and checks that they're authoritative, and that the zone serial numbers match. see also README.guido for the original docs. comments to me, anthony@interlink.com.au, or to the mailing list, pydns-developer@lists.sourceforge.net. bugs/patches to the tracker on SF - http://sourceforge.net/tracker/?group_id=31674 pydns-2.3.6/pydns.spec0000644000160600001450000000442611713064431013560 0ustar stuartbmsSummary: Python DNS library Name: python26-pydns Version: 2.3.6 Release: 1 Source0: pydns-%{version}.tar.gz License: Python license Group: Development/Libraries BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Prefix: %{_prefix} BuildArch: noarch Vendor: Anthony Baxter and others Packager: Stuart D. Gathman Url: http://pydns.sourceforge.net/ BuildRequires: python26-devel %description Python DNS library %prep %setup -n pydns-%{version} %build python2.6 setup.py build %install python2.6 setup.py install --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES %clean rm -rf $RPM_BUILD_ROOT %files -f INSTALLED_FILES %defattr(-,root,root) %changelog * Fri Feb 03 2012 Stuart Gathman 2.3.6-1 - Create subclasses of DNSError for different types of errors - Remove obsolete string module - close socket on TCP error - rename variables that conflict with builtin "list" type * Sat Mar 19 2011 Stuart Gathman 2.3.5-1 - Python 2.6 - Handle large TCP replies (change to blocking IO with timeout) - server rotation - additional convenience functions in lazy.py * Tue Jun 09 2009 Stuart Gathman 2.3.4-1 - Support IDNA label encoding (from 2.3.3-3) - Optionally support M$ compatible UTF-8 label encoding (DNS.LABEL_UTF8) - Built-in SPF RR support * Fri Aug 01 2008 Stuart Gathman 2.3.3-1 - Support IPv6 nameservers * Thu Jul 24 2008 Stuart Gathman 2.3.2-2 - Fix tcp timeout * Thu Jul 24 2008 Stuart Gathman 2.3.2-1 - Randomize TID and source port * Tue May 22 2007 Stuart Gathman 2.3.1-1 - Bug fix release - BTS Patches: - 01resolv-conf-parse patch, thanks to Arnaud Fontaine (closes: #378991) - Changes from Ubuntu (SF = Sourceforge project bug #) (closes: #411138): - 02utf-8 patch for files with UTF-8 content - 03socket-error-trap patch, Added DNSError trap for socket.error. - 04lazy-init SF 1563723 lazy should initilize defaults['server'] - 05addr2bin2addr SF 863364 Mac OS X, Win2000 DHCP, addr2bin and bin2addr. - 06win32-fix SF 1180344 win32dns.py fails on windows server 2003 - 07unpacker SF 954095 Bug in DNS.Lib.Unpacker.getbyte() - 08import-lib SF 658601 Missing "import Lib"; for TCP protocol pydns-2.3.6/python-pydns.spec0000644000160600001450000000652711713063563015110 0ustar stuartbms%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: python-pydns Version: 2.3.6 Release: 1%{?dist} Summary: Python module for DNS (Domain Name Service). Group: Development/Languages License: Python Software Foundation License URL: http://pydns.sourceforge.net/ Source0: pydns-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch #BuildRequires: python-setuptools %description This is a another release of the pydns code, as originally written by Guido van Rossum, and with a hopefully nicer API bolted over the top of it by Anthony Baxter . This package contains a module (dnslib) that implements a DNS (Domain Name Server) client, plus additional modules that define some symbolic constants used by DNS (dnstype, dnsclass, dnsopcode). %define namewithoutpythonprefix %(echo %{name} | sed 's/^python-//') %prep %setup -q -n %{namewithoutpythonprefix}-%{version} #patch -p1 -b .sdg %build %{__python} setup.py build %install rm -rf $RPM_BUILD_ROOT %{__python} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %doc CREDITS.txt PKG-INFO README-guido.txt README.txt %{python_sitelib}/DNS/*.py* #{python_sitelib}/%{name}-%{version}-py2.5.egg-info %changelog * Fri Feb 03 2012 Stuart Gathman 2.3.6-1 - Create subclasses of DNSError for different types of errors - Remove obsolete string module - close socket on TCP error - rename variables that conflict with builtin "list" type * Sat Mar 19 2011 Stuart Gathman 2.3.5-1 - Python 2.6 - Handle large TCP replies (change to blocking IO with timeout) - server rotation - additional convenience functions in lazy.py * Tue Jun 09 2009 Stuart Gathman 2.3.4-1 - Support IDNA label encoding (from 2.3.3-3) - Optionally support M$ compatible UTF-8 label encoding (DNS.LABEL_UTF8) - Built-in SPF RR support * Thu Sep 25 2008 Stuart Gathman 2.3.3-3 - Accept unicode names, encode to ascii with exception if non-ascii * Thu Sep 25 2008 Stuart Gathman 2.3.3-2 - Support IPv6 queries * Fri Aug 01 2008 Stuart Gathman 2.3.3-1 - Support IPv6 nameservers * Thu Jul 24 2008 Stuart Gathman 2.3.2-2 - Fix tcp timeout * Thu Jul 24 2008 Stuart Gathman 2.3.2-1 - Randomize TID and source port, CVE-2008-4099 CVE-2008-4126 * Tue May 22 2007 Stuart Gathman 2.3.1-1 - Bug fix release - BTS Patches: - 01resolv-conf-parse patch, thanks to Arnaud Fontaine (closes: #378991) - Changes from Ubuntu (SF = Sourceforge project bug #) (closes: #411138): - 02utf-8 patch for files with UTF-8 content - 03socket-error-trap patch, Added DNSError trap for socket.error. - 04lazy-init SF 1563723 lazy should initilize defaults['server'] - 05addr2bin2addr SF 863364 Mac OS X, Win2000 DHCP, addr2bin and bin2addr. - 06win32-fix SF 1180344 win32dns.py fails on windows server 2003 - 07unpacker SF 954095 Bug in DNS.Lib.Unpacker.getbyte() - 08import-lib SF 658601 Missing "import Lib"; for TCP protocol * Tue Aug 29 2006 Sean Reifschneider 2.3.0-1 - Initial RPM spec file. pydns-2.3.6/setup.cfg0000644000160600001450000000013711534006717013367 0ustar stuartbms[install] compile = 1 optimize = 1 [bdist_rpm] python=python2.6 group = Development/Libraries pydns-2.3.6/setup.py0000644000160600001450000000354111044505225013254 0ustar stuartbms#! /usr/bin/env python # # $Id: setup.py,v 1.4.2.3 2008/08/01 04:01:25 customdesigned Exp $ # import sys,os sys.path.insert(0,os.getcwd()) from distutils.core import setup import DNS setup( #-- Package description name = 'pydns', license = 'Python License', version = DNS.__version__, description = 'Python DNS library', long_description = """Python DNS library: """, author = 'Anthony Baxter and others', author_email = 'pydns-developer@lists.sourceforge.net', maintainer="Stuart D. Gathman", maintainer_email="stuart@bmsi.com", url = 'http://pydns.sourceforge.net/', packages = ['DNS'], keywords = ['DNS'], classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: No Input/Output (Daemon)', 'Intended Audience :: Developers', 'License :: OSI Approved :: Python License (CNRI Python License)', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Internet :: Name Service (DNS)', 'Topic :: Software Development :: Libraries :: Python Modules' ] ) # # $Log: setup.py,v $ # Revision 1.4.2.3 2008/08/01 04:01:25 customdesigned # Release 2.3.3 # # Revision 1.4.2.2 2008/08/01 03:58:03 customdesigned # Don't try to close socket when never opened. # # Revision 1.4.2.1 2008/07/28 19:54:13 customdesigned # Add pypi metadata to setup.py # # Revision 1.4 2002/05/06 06:32:07 anthonybaxter # filled in a blank # # Revision 1.3 2001/11/23 19:43:57 stroeder # Prepend current directory to sys.path to enable import of DNS. # # Revision 1.2 2001/11/23 19:36:35 stroeder # Use DNS.__version__ as package version and corrected name # # Revision 1.1 2001/08/09 13:42:38 anthonybaxter # initial setup.py. That was easy. :) # # pydns-2.3.6/PKG-INFO0000644000160600001450000000136311713073473012647 0ustar stuartbmsMetadata-Version: 1.0 Name: pydns Version: 2.3.6 Summary: Python DNS library Home-page: http://pydns.sourceforge.net/ Author: Stuart D. Gathman Author-email: stuart@bmsi.com License: Python License Description: Python DNS library: Keywords: DNS Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: No Input/Output (Daemon) Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Python License (CNRI Python License) Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Internet :: Name Service (DNS) Classifier: Topic :: Software Development :: Libraries :: Python Modules