我们如何从dnspython获取TXT、CNAME和SOA记录?

19

我有一个需求,需要一个dns查询函数来查询服务器上的各种记录。我已经弄清楚了如何获取MX记录(大多数示例都显示此内容),A记录和NS记录。但我该如何获取TXT、CNAME和SOA记录呢?

示例代码片段:

   import dns.resolver
   answer=dns.resolver.query("google.com", "A")
       for data in answer:
           print data.address

我尝试将查询类型替换为TXT,并将data.address对象替换为data.text、data.data等,但最终出现了属性错误。我可以查看一下我之前提到的数据类型的参考资料吗?

3个回答

30

(回答如何确定返回的数据)

你可以以类似的方式获取TXT、CNAME和SOA记录,但是你必须根据DNS响应对象获取正确的属性。

使用Python内置的dir()方法可以帮助你找出DNS响应对象中存在的属性 - 当API文档不可用时非常方便。为了找出合适的属性,暂时将你的for循环更改为以下内容:

   for data in answer:
       print dir(data)

另一种更快的方法是查看dnspython的API文档,这些页面列出了每个返回对象的属性。

最后,如果库是Python编写的,或者C代码可用,您可以查看源代码。

(回答您的问题)

以下是TXT、CNAME和SOA查询的示例:

TXT

http://www.dnspython.org/docs/1.15.0/dns.rdtypes.txtbase.TXTBase-class.html#section-InstanceVariables

answers = dns.resolver.query('google.com', 'TXT')
print ' query qname:', answers.qname, ' num ans.', len(answers)
for rdata in answers:
    for txt_string in rdata.strings:
      print ' TXT:', txt_string

CNAME

http://www.dnspython.org/docs/1.15.0/dns.rdtypes.ANY.CNAME.CNAME-class.html

answers = dns.resolver.query('mail.google.com', 'CNAME')
print ' query qname:', answers.qname, ' num ans.', len(answers)
for rdata in answers:
    print ' cname target address:', rdata.target

SOA

http://www.dnspython.org/docs/1.15.0/dns.rdtypes.ANY.SOA.SOA-class.html#section-InstanceVariables

answers = dns.resolver.query('google.com', 'SOA')
print 'query qname:', answers.qname, ' num ans.', len(answers)
for rdata in answers:
    print ' serial: %s  tech: %s' % (rdata.serial, rdata.rname)
    print ' refresh: %s  retry: %s' % (rdata.refresh, rdata.retry)
    print ' expire: %s  minimum: %s' % (rdata.expire, rdata.minimum)
    print ' mname: %s' % (rdata.mname)

1
警告:为了进行防御性编程,无论您请求什么记录类型,请不要默认期望得到您所请求的内容,始终在尝试以某种方式使用它之前检查您获得的记录类型。 - Patrick Mevzek

12

你可以尝试一些有所不同的东西。

与每种记录类型分别查询不同,你可以对任何记录进行单个查询。 这样,如果该域名具有TXT、CNAME等多种类型,你将获得一个包含所有数据的对象。

from dns.resolver import dns
name_server = '8.8.8.8' #Google's DNS server
ADDITIONAL_RDCLASS = 65535
request = dns.message.make_query('google.com', dns.rdatatype.ANY)
request.flags |= dns.flags.AD
request.find_rrset(request.additional, dns.name.root, ADDITIONAL_RDCLASS,
                       dns.rdatatype.OPT, create=True, force_unique=True)       
response = dns.query.udp(request, name_server)

希望这能对你有所帮助。


为什么要指定 ADDITIONAL_RDCLASS = 65535?我查了一下,但没找到这个东西具体是干什么的。 - Nathaniel
1
你不必这样做,这只是为了涵盖所有可能的记录类型ID。通常使用2的幂来表示它,目前最大的ID是32769(DLV)。这需要2的16次方(2的15次方是32768)。 - Meny Issakov
6
不要使用ANY。与普遍理解相反,它不是 ALL 的同义词,它只会给出缓存中包含的内容,肯定不是所有记录(取决于其活动情况)。此外,这已经被过滤了,而且IETF正在进行工作以使ANY变得过时。所以无论你的问题是什么,ANY都不是你的解决方案。 - Patrick Mevzek
1
关于“如果该域名同时具有TXT和CNAME”:根据定义,任何单个域名不能与其他记录共存CNAME记录。 - Patrick Mevzek
实际上,当添加OPT伪RR时,“CLASS”字段指定请求者的UDP负载大小:https://datatracker.ietf.org/doc/html/rfc6891#section-6.1 这意味着您必须确保您的网络堆栈能够重新组装/传递此大小的UDP消息。RFC建议使用“4096”作为一个很好的折衷方案。 - Andreas Ley

3

以前的答案中提供的示例,使用以下内容创建dnsdig.py文件:

import sys
import socket
import dns.resolver

print 'Argument List:', str(sys.argv)
site = sys.argv[1]
dns_server = sys.argv[2]

# Basic CNAME query the host's DNS
for rdata in dns.resolver.query(site, 'CNAME') :
    print rdata.target

# Basic A query the host's DNS
for rdata in dns.resolver.query(site, 'A') :
    print rdata.address

# Setting an specific DNS Server
resolver = dns.resolver.Resolver()
resolver.nameservers = [socket.gethostbyname(dns_server)]

# Basic CNAME query with the specific DNS server
answer = resolver.query(site, 'CNAME');
for rdata in answer :
    print rdata.target

# Basic A query with the specific DNS server
answer = resolver.query(site, 'A');
for rdata in answer :
    print rdata.address

运行:

python dnsdig.py www.youtube.com 8.8.8.8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接