Python:如何区分不可用的DNS服务器和不存在的地址

标签 python dns

名称解析可能会失败,因为没有与主机名关联的 ip,或者因为无法访问 DNS 服务器。不幸的是,Python 的 socket.create_connectionsocket.gethostbyname 函数似乎在两种情况下都会引发相同的错误:

$ python3 -c 'import socket; socket.create_connection(("www.google.com_bar", 80))'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.4/socket.py", line 491, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
  File "/usr/lib/python3.4/socket.py", line 530, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known

$ python3 -c 'import socket; socket.gethostbyname("www.google_bar.com")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
socket.gaierror: [Errno -5] No address associated with hostname

$ sudo vim /etc/resolv.conf # point to non-existing nameserver

$ python3 -c 'import socket; socket.create_connection(("www.google.com", 80))'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.4/socket.py", line 491, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
  File "/usr/lib/python3.4/socket.py", line 530, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known

$ python3 -c 'import socket; socket.gethostbyname("www.google.com")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
socket.gaierror: [Errno -5] No address associated with hostname

有什么方法可以区分这两种情况而不需要我对“已知良好”的主机名执行第二次查找?

该解决方案应在 Linux 下运行。

最佳答案

您可以使用 dnslib库客户端自己发出 DNS 请求。客户端提供类似挖掘的功能,可以指示地址是否无法解析 (NXDOMAIN) 与解析失败相比(不幸的是只是阻塞 - 请参阅下面的补丁)。

你可以这样使用它:

from dnslib import DNSRecord, RCODE

# I have dnsmasq running locally, so I can make requests to localhost.
# You need to find the address of the DNS server.
# The /etc/resolv.conf file is quite easily parsed, so you can just do that.
DNS_SERVER = "127.0.0.1"

query = DNSRecord.question("google.com")
response = DNSRecord.parse(query.send(DNS_SERVER, 53, False))
print RCODE[response.header.rcode]  # prints 'NOERROR'

query = DNSRecord.question("google.com_bar")
response = DNSRecord.parse(query.send(DNS_SERVER, 53, False))
print RCODE[response.header.rcode]  # prints 'NXDOMAIN'

# To avoid making the DNS request again when using the socket
# you can get the resolved IP address from the response.

当连接到一个不存在的 DNS 服务器时,问题就来了。每次我尝试这个请求都会挂起。 (当我在命令行上发出相同的请求时,使用诸如 netcat 之类的东西,请求也只是挂起。我可能随机 IP 的选择不当,并且受到防火墙的影响,这些防火墙只会丢弃数据包)

无论如何,您都可以更改源代码以添加超时。可以查看源码中的相关方法here (也反射(reflect)在 github 上)。我改变的是:

--- a/dns.py
+++ b/dns.py
@@ -357,6 +357,7 @@
             response = response[2:]
         else:
             sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
+            sock.settimeout(10)
             sock.sendto(self.pack(),(dest,port))
             response,server = sock.recvfrom(8192)
             sock.close()

执行此操作后,DNS 请求超时。

关于Python:如何区分不可用的DNS服务器和不存在的地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24855168/

相关文章:

linux - 分析dnsmasq的日志文件

dns - 多个子域指向同一文件夹,但保留 url

python - 从 YouTube 下载 FLV 格式的视频

python - 使用 google-api-python-client 拉超时

python - 在 python 上使用 mysql 时出现错误,例如 fromflask.ext.mysql import MySQL

dns - 需要将一个域重定向到另一个域而不影响电子邮件

Python 的多个循环和线程问题

python - 在python中拆分wav文件

dns - 附加部分中的 CNAME 记录

Grails:从域类中的自定义验证器中消除 ConfigurationHolder