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'])