DNS(域名解析系统),用来把主机名转换成IP地址。DNS是一个分布式的数据库,有许多的服务器。13个根服务器以及许许多多的提供DNS域名解析服务的子服务器。。当程序或系统需要查询某一主机名的IP地址时,会先和其指定的名称服务器同喜,这个服务器是一个递归的名称服务器,若它有DNS缓存,则直接将IP地址返回给程序,若无则将请求向上传递。。。
操作系统上本身带有一些DNS查找功能,像UNIX系统上有一个/etc/hosts文件,其中定义了主机名和IP地址。操作系统通常会先查看这个文件,若没有答案则在去进行DNS查询。。。
在此,我对DNS不再做过多介绍。。本章讨论利用Python来实现这种DNS域名解析功能。。。
通常情况下, 进行正向查询,也就是根据主机名来查找IP地址时,Python的socket库会替我们实现这种查询。不过,也可以自己实现,通过socket.getaddrinfo()函数。
getaddrinfo(host,port[,family[,socktype[,proto[,flags]]]])
host就是主机名,其他参数只有当你想把结果直接传递给socket.socket()或socket.connect()时才用到。它们会在输出中限制显示什么协议,以及为了建立socket而填写根据默认值得到的结果。可以设置port为None,省略其他参数来进行简单查询。这个函数的返回值是一列元组,每一个元组格式如下:
(family,socktype,proto,canonname,sockaddr)
sockaddr是远程机器的地址(IP和端口),返回一个列表是因为对一个查询,可能有多个不同的IP地址。
反向查询
通过IP地址查询主机名。有一个重要的问题:对于一个IP地址,可能不存在反向的映射。需要确保每一个反向查找的行为捕获和处理socket.herror()
#!/usr/bin/env python from socket import * import sys try: result=gethostbyaddr(sys.argv[1]) #返回一个元组(hostname, aliaslist, ipaddrlist) print 'Primary hostname:' print ' '+result[0] print '\nAddress' for item in result[2]: print ' '+ item except herror,e: print "Couldn't look up name:",e
需要注意反向查找数据的真实性,也就是这个IP并不是属于查找到的主机名的,可在进行反向查询后,根据主机名在进行一此正向查询来阻止欺骗。
获得环境信息
socket.gethostname(),不带任何参数,返回一个表明操作系统配置中本地机器的主机名。通常是不完整的。socket.getfqdn(),带一 个参数——主机名,并试图获得完整的数据。
使用PyDNS进行高级查询
PyDNS通常不能提供操作系统自带文件的查询,入/etc/hosts,所以不能通过它取得一些本地名称
PyDNS可以从http://pydns.sourceforge.net下载。
DNS.DiscoverNameServers()可以找到系统中的名称服务器,Windows中是注册表,UNIX中是/etc/resolv.conf。若Windows上使用DHCP,则DHCP不会向注册表中发布名称服务器的细腻希,所以DNS.DiscoverNameServer()可能不起作用,这时可以直接设置:DNS.defaults['server']=['192.168.1.1]
初始化名称服务器后,需要建立一个请求对象。通过调用DNS.Request()来实现。请求对象的req()方法用来执行实际的查询。通常有两个参数:name给出了实际查询的名称,qtype指定了某条record类型,具体可查看help(DNS.Type)。请求对象发出查询后,PyDNS会返回一个包含结果的应答对象,它有个属性answers,包含所有返回的应答列表。例子:
#!/usr/bin/env python import sys,DNS query=sys.argv[1] DNS.DiscoverNameServers() reqobj=DNS.Request() answerobj=reqobj.req(name=query,qtype=DNS.Type.ANY) if not len(answerobj.answers): print 'Not found' for item in answerobj.answers: print '%-5s %s'(item['typename'],item['data'])
ANY类型的查询有一个问题,就是它只返回保存在本地名称服务器缓存中的细腻希,可能是不完整的。解决这个问题的办法是跳过本地名称服务器,直接向改域中权威的名称服务器发送查询。代码:
#!/usr/bin/env python #-*-coding:utf-8-*- import sys,DNS def hierquery(qstring,qtype): reqobj=DNS.Request() try: answerobj=reqobj.req(name=qstring,qtype=qtype) answers=[x['data'] for x in answerobj.answers if x['type']==qtype] except DNS.Base.DNSError: answers=[] if len(answers): return answers else: remainder=qstring.split('.',1) if len(remainder)==1: return None else: return hierquery(remainder[1],qtype) def findnameservers(hostname): return hierquery(hostname,DNS.Type.NS) def getrecordsfromnameserver(qstring,qtype,nslist): for ns in nslist: reqobj=DNS.Request(server=ns) try: answers=reqobj.req(name=qstring,qtype=qtype).answers if len(answers): return answers except DNS.Base.DNSError: pass return [] def nslookup(qstring,qtype,verbose=1): nslist=findnameservers(qstring) if nslist==None: raise RuntimeError,'Could not find nameserver to use.' if verbose: print 'Using nameservers:',','.join(nslist) return getrecordsfromnameserver(qstring,qtype,nslist) if __name__=='__main__': query=sys.argv[1] DNS.DiscoverNameServers() answers=nslookup(query,DNS.Type.ANY) if not len(answers): print 'Not found' for item in answers: print '%-5s %s'%(item['typename'],item['data'])