Twisted DNS 无法工作

5 投票
3 回答
3210 浏览
提问于 2025-04-16 06:21

我想知道为什么下面的代码不管用。

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 个回答

2

我研究了一下为什么在我们的公司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

5

到目前为止,这在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服务器。

3

把你的例子改成下面这样,这样语法上才是正确的:

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。你需要进一步查找原因。

撰写回答