python网络编程学习笔记(四):域名系统
一、什么是域名系统
DNS计算机域名系统(DNS)是由解析器以及域名服务器组成的。当我们在上网的时候,通常输入的是网址,其实这就是一个域名,而我们计算机网络上的计算机彼此之间只能用IP地址才能相互识别。再如,我们去一WEB服务器中请求一WEB页面,我们可以在浏览器中输入网址或者是相应的IP地址,例如我们要上新浪网,我们可以在IE的地址栏中输入网址,也可输入IP地址,但是这样子的IP地址我们记不住或说是很难记住,所以有了域名的说法,这样的域名会让我们容易的记住。
二、访问DNS的方法一:使用socket模块
1、DNS查询
以查询www.external.example.com为例。首先,程序会和操作系统配置文件指定的本地名称服务器通信。这个服务器是一个递归的名称服务器,它收到请求并以适当的方式传递下去。递归服务器做的第一件事情是询问.com域,回答是以一种指向另外一外名称服务器的提名形式给出的。这个名称服务器可以提供名称中包含.com的信息。查询发送到该服务器后,该服务器将以另一个提名回答进行回应,指向另外一台服务器,而这个服务器可以提供example.com的名称信息。这个循环重复多次,直到查询到external.example.com服务的名称服务器。
2、正向查询
最基本的查询是正向查询,即根据一个主机名来查找ip地址。Socket库可以实现这种查询,主要用函数socket.getaddrinfo()。注意,该函数和ipv6不兼容。
Getaddrinfo(host,port[,family[,sockettype[,proto[,flags]]]])
参数host为域名,以字符串形式给出代表一个IPV4/IPV6地址或者None.
参数port如果字符串形式就代表一个服务名,比如“http”"ftp""email"等,或者为数字,或者为None
参数family为地主族,可以为AF_INET ,AF_INET6,AF_UNIX.
参数socketype可以为SOCK_STREAM(TCP)或者SOCK_DGRAM(UDP)
参数proto通常为0可以直接忽略
参数flags为AI_*的组合,比如AI_NUMERICHOST,它会影响函数的返回值
该函数返回值是一列tuple,每一个tuple如下:
(family,socktype,proto,canonname,sockaddr)
其中sockaddr实际上就是远程机器的地址和端口,也就是查询的数据。
例如:
>>>importsocket
>>>printsocket.getaddrinfo('www.baidu.com',None)
[(2,0,0,'',('61.135.169.125',0)),(2,0,0,'',('61.135.169.105',0))]
>>>printsocket.getaddrinfo('www.baidu.com',None)[0][4][0]
61.135.169.125
>>>printsocket.getaddrinfo('www.baidu.com',None)[0][4][1]
0
注意:因为一个网站可能有多个网址,所以两次查询时,结果不同也是很正常的。这里用一段代码将所有查询结果列出:
##@小五义 importsocket host=raw_input('host:') result=socket.getaddrinfo(host,None) counter=0 foriinresult: print"%-2d:%s"%(counter,i[4]) counter+=1
运行结果如下:
>>>
host:www.baidu.com
0:('61.135.169.105',0)
1:('61.135.169.125',0)
>>>
host:www.yahoo.com
0:('106.10.170.118',0)
>>>
host:www.163.com
0:('60.210.18.169',0)
1:('123.132.254.15',0)
3、反向查询
反向查询是指通过ip地址查询域名。这里用到gethostbyaddr
gethostbyaddr(addr,len,type)
参数addr可以为IPv4或IPv6的IP地址,参数len为参数addr的长度,参数type为AF_INET。
下面给出的例子,将反向查询ip地址的域名。
##@小五义 importsocket hostip=raw_input('ip:') try: result=socket.gethostbyaddr(hostip) print"hostname:"+result[0] print"\nAddresses:" foriinresult[2]: print""+i exceptsocket.herror,e: print"counldnotlookupname:",e
运行结果是:
>>>
ip:127.0.0.1
hostname:localhost
Addresses:
127.0.0.1
>>>
ip:216.109.118.73
hostname:gi-2-19.bas1-1-con.ac2.yahoo.com
Addresses:
216.109.118.73
>>>
ip:123.132.254.15
counldnotlookupname:[Errno11004]hostnotfound
>>>
ip:60.210.18.169
counldnotlookupname:[Errno11004]hostnotfound
从运行的结果看,第一次查询到的两个ip放进去,却反向查询不到域名,这里我也没搞明白是什么原因,有待高手解答。
三、访问DNS的方法二:使用PyDNS进行高级查询
pyDNS提供了一个功能更强的访问DNS系统的接口。其下载地址为http://pydns.sourceforge.net。其中py3dns是针对python3.x的,本人的学习环境是python2.6,所以就下载安装了pydns。下载后解压,将DNS文件夹拷贝到Python安装文件夹下的Lib\site-packages\文件夹下即可。
1、简单的pyDNS查询
首先调用DNS.DiscoverNameServers()查找系统中的名称服务器。然后建立一个请求对象DNS.Request()。DNS.Request()的req()方法用来执行实际的查询。通常有两个参数:name给出了实际查询的名称;qtype指定了record类型。当使用请求对象来发出查询请求时,pyDNS会返回一个包含结果的应答对象(answerobject),该对象有个属性叫answers,包含所有返回的应答列表。
在给出例子前,首先列出大多数的DNSrecords列表如下:
AAddress.网址记录(定在右边),定义於RFC1035.
AAAA IPv6Address.(第6代网址表式法).定义於RFC1886.
AFSDB AFSDataDaselocation.定义於RFC1183.
CNAME CanonicalName(正式名称),定义於RFC1035.这是定别名(alias)的指标用法.别名设定在LHS,正式名称设定在RHS.
GPOS GeographicPOSition(地理位置)?,定义於RFC1712.过时(obsolete)用法,不建议使用..
HINFO HostINFOrmation(机器基本资料;OS,硬体,...),定义於RFC1035.
ISDN ISDN(整合数位网路网址表示法),定义於RFC1183.
KEY publickkey(公开金匙;DNS资料,透过编码,密码通讯),定义於RFC2065.
LOC LOCation(网路所在的地理区域;表经纬度),定於RFC1876.
MB MailBox.(信箱;已经很少使用),定义於RFC1035.-->参考MX记录项目.
MD 定义於RFC1035.过时(obsolete)用法,不建议使用.-->参考MX记录项目.
MF 定义於RFC1035.过时(obsolete)用法,不建议使用.-->参考MX记录项目.
MG 定义於RFC1035.
MINFO 定义於RFC1035.
MR 定义於RFC1035.
MX MaileXchanger.(电子邮件,交寄设定).定义於RFC1035.基本用法是,当一个emailaddress包含某一笔MX记录的LHS时,那麽email传递系统会,将该电子邮件,转交给该笔MX的RHS所指示的系统,去进一步处理.
NULL 空记录(如空白行等;无实际用途),定义於RFC1035.
NS NameServer(表示RHS为一领域名称伺服机器),定义於RFC1035.
NSAP NetworkServicesAccessPointaddress.(ISO-OSI的网路服务,网址表示法)定义於RFC1348,另外又分别经过RFC1637,1706两次重新定义.
NSAP-PTR 定义於RFC1348.过时用法.
NXT 定义於RFC2065.
PTR PoinTeR.(指标),定义於RFC1035.通常用於将IPaddr.只回到某一个对应的domainname.
下面是一个简单的例子:
##@小五义 #-*-coding:cp936-*- importDNS query=raw_input('输入DNS:') DNS.DiscoverNameServers()
reqobj=DNS.Request() answerobj=reqobj.req(name=query,qtype=DNS.Type.ANY) ifnotlen(answerobj.answers): print"Notfound" foriinanswerobj.answers: print"%-5s%s"%(i['typename'],i['data'])