Twisted DNS 无法工作
我想知道为什么下面的代码不管用。
from twisted internet import defer, reactor
from twisted.python.failure import Failure
import twisted.names.client
def do_lookup(do_lookup):
d = twisted.names.client.getHostByName(domain)
d.addBoth(lookup_done)
def lookup_done(result):
print 'result:', result
reactor.stop()
domain = 'twistedmatrix.com'
reactor.callLater(0, do_lookup, domain)
reactor.run()
结果是:
result: [Failure instance: Traceback
(failure with no frames): <class
'twisted.names.error.ResolverError'>:
Stuck at response without answers or
delegation ]
3 个回答
我研究了一下为什么在我们的公司Windows机器上,client.createResolver(servers)
这个命令不管用。在Twisted的createResolver
的内部,有一段代码是跟操作系统有关的:
def createResolver(servers=None, resolvconf=None, hosts=None):
...
from twisted.names import resolve, cache, root, hosts as hostsModule
if platform.getType() == 'posix':
if resolvconf is None:
resolvconf = '/etc/resolv.conf'
if hosts is None:
hosts = '/etc/hosts'
theResolver = Resolver(resolvconf, servers)
hostResolver = hostsModule.Resolver(hosts)
else:
if hosts is None:
hosts = r'c:\windows\hosts'
from twisted.internet import reactor
bootstrap = _ThreadedResolverImpl(reactor)
hostResolver = hostsModule.Resolver(hosts)
theResolver = root.bootstrap(bootstrap)
L = [hostResolver, cache.CacheResolver(), theResolver]
return resolve.ResolverChain(L)
对于Windows来说,第一个警告信号是参数servers
没有被使用,所以自定义的DNS服务器被忽略了。接下来是_ThreadedResolverImpl()
,它使用了特定于平台的代码,在被添加到解析器链之前,它被包裹在root.bootstrap()
中。
root.bootstrap的目的是使用平台解析器去查找一些根服务器,比如a.root-servers.net、b.root-servers.net等,使用的是Windows平台的同步解析器(这个是有效的,并且能返回IP地址),然后直接对这些根服务器进行DNS查询。发往根服务器的UDP数据包被我们的公司防火墙阻挡了——我在Wireshark中看到了这些数据包。
默认的getResolver()
调用是通过names.client.getHostByName()
直接调用createResolver()
,这可能又会导致这个问题,因为它是通过一个正常工作的DNS设置来启动的。在大多数环境中,我认为将解析器(带有根服务器或自定义服务器)添加到解析器链中,并将_ThreadedResolverImpl
作为备用,应该是可行的——只是平台解析器是一个不同的接口。
这里有一个示例,展示了如何异步检测本地DNS服务器(通过解析ipconfig
的输出),然后安装一个自定义解析器来使用它们。
import sys
from twisted.python import log
from twisted.names import root, hosts, resolve, cache, client
from twisted.python.runtime import platform
from twisted.internet import reactor
from twisted.internet import utils
import re
def parseIpconfigDNSServers(output):
servers = []
found = False
for line in output.split('\n'):
if 'DNS Servers . . . . . . . . . . . :' in line or (found and not '. . . .' in line):
servers.append(line[38:].strip())
found = True
else:
found = False
log.msg( 'Windows: Detected DNS servers %s' % (str(servers)))
return servers
if platform.getType() != 'posix':
d = utils.getProcessOutput(os.path.join(os.environ['WINDIR'], 'system32', 'ipconfig.exe'), ["/all"])
d.addCallback(parseIpconfigDNSServers)
d.addCallback(lambda r: client.Resolver(servers=[(h, 53) for h in r]))
d.addErrback(log.msg)
theResolver = root.DeferredResolver(d)
client.theResolver = resolve.ResolverChain([cache.CacheResolver(), theResolver])
if __name__ == '__main__':
log.startLogging(sys.stdout)
def do_lookup(domain):
d = client.getHostByName(domain)
d.addBoth(log.msg)
from twisted.internet import reactor
reactor.callLater(0, do_lookup, 'example.com')
reactor.run()
我整理了一下这个内容,并把它发布在这里 https://gist.github.com/shuckc/af7490e1c4a2652ca740
到目前为止,这在Windows上是失败的,因为它使用了一个无效的路径来访问Windows的主机文件(在twisted.names.client.createResolver中)。它使用的是 'c:\windows\hosts'。这个路径在Windows 98和Me版本中是可以的(参考这里),但在像XP这样的“现代”版本中就会出错。
现在,它可能应该使用类似这样的路径:
hosts = os.path.join(
os.environ.get('systemroot','C:\\Windows'),
r'system32\drivers\etc\hosts'
)
我觉得这只是部分解决了问题(或者这可能是个误导)。
这现在只对在这个主机文件中实际指定的名称有效。它可能需要做的是查询一下DNS服务器的注册信息,然后再查询实际的DNS名称。
这个方法看起来很有希望,可以用来获取实际的DNS服务器。
把你的例子改成下面这样,这样语法上才是正确的:
from twisted.internet import reactor
import twisted.names.client
def do_lookup(domain):
d = twisted.names.client.getHostByName(domain)
d.addBoth(lookup_done)
def lookup_done(result):
print 'result:', result
reactor.stop()
domain = 'twistedmatrix.com'
reactor.callLater(0, do_lookup, domain)
reactor.run()
我得到的是:
$ python so-example.py
result: 66.35.39.65
所以,回答你的问题:是你的本地DNS环境出了问题,而不是twisted.names。或者可能是有个bug。你需要进一步查找原因。