linkchecker/DNS/Base.py
2002-02-24 12:29:35 +00:00

286 lines
8.2 KiB
Python

# $Id$
import os, sys, re, getopt, socket
import DNS,DNS.Lib,DNS.Type,DNS.Class,DNS.Opcode
#import asyncore
defaults= {
'protocol': 'udp',
'port': 53,
'opcode': DNS.Opcode.QUERY,
'qtype':DNS.Type.A,
'rd':1,
'timing':1,
'server': [],
'search_domains': [],
}
defaults['server']=[]
def init_dns_resolver():
"""init the DNS resolver. This function can be called more than once"""
global defaults
# reset to old values
defaults['server'] = []
defaults['search_domains'] = []
# platform specific config
if os.name=="posix":
init_dns_resolver_posix()
elif os.name=="nt":
init_dns_resolver_nt()
else:
# other platforms not supported (what about Mac?)
pass
# default values
if not defaults['search_domains']:
defaults['search_domains'].append('')
if not defaults['server']:
defaults['server'].append('127.0.0.1')
def init_dns_resolver_posix():
"parses the /etc/resolv.conf file and sets defaults for name servers"
if not os.path.exists('/etc/resolv.conf'):
return
# XXX this needs to be dynamic?
global defaults
for line in open('/etc/resolv.conf', 'r').readlines():
line = line.strip()
if (not line) or line[0]==';' or line[0]=='#':
continue
m = re.match(r'^search\s+\.?(.*)$', line)
if m:
for domain in m.group(1).split():
defaults['search_domains'].append('.'+domain.lower())
m = re.match(r'^nameserver\s+(\S+)\s*$', line)
if m: defaults['server'].append(m.group(1))
m = re.match(r'^domain\s+(\S+)\s*$', line)
if m: defaults['domain']= m.group(1)
def init_dns_resolver_nt():
"""Windows network config read from registry"""
import winreg
global defaults
key = None
try:
key = winreg.key_handle(winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters")
except EnvironmentError:
try: # for Windows ME
key = winreg.key_handle(winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet\Services\VxD\MSTCP")
except EnvironmentError:
pass
if key:
for server in winreg.stringdisplay(key["NameServer"]):
if server:
defaults['server'].append(server)
for item in winreg.stringdisplay(key["SearchList"]):
if item:
defaults['search_domains'].append(item)
# XXX search for "EnableDhcp", "DhcpNameServer"
try: # search adapters
key = winreg.key_handle(winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DNSRegisteredAdapters")
for subkey in key.subkeys():
count, counttype = subkey['DNSServerAddressCount']
values, valuestype = subkey['DNSServerAddresses']
for server in winreg.binipdisplay(values):
if server:
defaults['server'].append(server)
except EnvironmentError:
pass
try: # search interfaces
key = winreg.key_handle(winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces")
for subkey in key.subkeys():
for server in winreg.stringdisplay(subkey.get('NameServer', '')):
if server:
defaults['server'].append(server)
except EnvironmentError:
pass
class DnsRequest:
def __init__(self,*name,**args):
self.donefunc=None
self.async=None
self.defaults = {}
self.argparse(name,args)
self.defaults = self.args
def argparse(self,name,args):
if not name and self.defaults.has_key('name'):
args['name'] = self.defaults['name']
if type(name) is type(""):
args['name']=name
else:
if len(name) == 1:
if name[0]:
args['name']=name[0]
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']) == type(''):
args['server'] = [args['server']]
self.args=args
def socketInit(self,a,b):
self.s = socket.socket(a,b)
def processUDPReply(self):
import time
self.reply = self.s.recv(1024)
self.time_finish=time.time()
self.args['server']=self.ns
return self.processReply()
def processTCPReply(self):
import time
self.f = self.s.makefile('r')
header = self.f.read(2)
if len(header) < 2:
raise DNS.Error,'EOF'
count = DNS.Lib.unpack16bit(header)
self.reply = self.f.read(count)
if len(self.reply) != count:
raise DNS.Error,'incomplete reply'
self.time_finish=time.time()
self.args['server']=self.ns
return self.processReply()
def processReply(self):
#import time
self.args['elapsed']=(self.time_finish-self.time_start)*1000
u = DNS.Lib.Munpacker(self.reply)
r = DNS.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 == DNS.Type.AXFR:
# while 1:
# header = f.read(2)
# if len(header) < 2:
# print '========== EOF =========='
# break
# count = DNS.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 = DNS.Lib.Munpacker(reply)
# DNS.Lib.dumpM(u)
def conn(self):
self.s.connect((self.ns,self.port))
def req(self,*name,**args):
import time
self.argparse(name,args)
#if not self.args:
# raise DNS.Error,'reinitialize request before reuse'
protocol = self.args['protocol']
self.port = self.args['port']
opcode = self.args['opcode']
rd = self.args['rd']
server = self.args['server']
if type(self.args['qtype']) == type('foo'):
try:
qtype = eval(self.args['qtype'].upper(), DNS.Type.__dict__)
except (NameError,SyntaxError):
raise DNS.Error,'unknown query type'
else:
qtype=self.args['qtype']
if not self.args.has_key('name'):
print self.args
raise DNS.Error,'nothing to lookup'
qname = self.args['name']
if qtype == DNS.Type.AXFR:
print 'Query type AXFR, protocol forced to TCP'
protocol = 'tcp'
#print 'QTYPE %d(%s)' % (qtype, DNS.Type.typestr(qtype))
m = DNS.Lib.Mpacker()
m.addHeader(0,
0, opcode, 0, 0, rd, 0, 0, 0,
1, 0, 0, 0)
m.addQuestion(qname, qtype, DNS.Class.IN)
self.request = m.getbuf()
if protocol == 'udp':
self.response=None
self.socketInit(socket.AF_INET, socket.SOCK_DGRAM)
for self.ns in server:
try:
#self.s.connect((self.ns, self.port))
self.conn()
self.time_start=time.time()
if not self.async:
self.s.send(self.request)
self.response=self.processUDPReply()
#except socket.error:
except None:
continue
break
if not self.response:
if not self.async:
raise DNS.Error,'no working nameservers found'
else:
self.response=None
for self.ns in server:
try:
self.socketInit(socket.AF_INET, socket.SOCK_STREAM)
self.time_start=time.time()
self.conn()
self.s.send(DNS.Lib.pack16bit(len(self.request)) + self.request)
self.s.shutdown(1)
self.response=self.processTCPReply()
except socket.error:
continue
break
if not self.response:
raise DNS.Error,'no working nameservers found'
if not self.async:
return self.response
import asyncore
#class DnsAsyncRequest(DnsRequest,asyncore.dispatcher_with_send):
class DnsAsyncRequest(DnsRequest):
def __init__(self,*name,**args):
if args.has_key('done') and args['done']:
self.donefunc=args['done']
else:
self.donefunc=self.showResult
self.realinit(name,args)
self.async=1
def conn(self):
import time
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()