I understood something why the explicit client.createResolver(servers) did not work on our corporate Windows machines. Into the guts Twisted createResolver is an os-dependent branch:
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)
The first warning sign for Windows is that the servers argument is not used, so user DNS servers are ignored. It follows that _ThreadedResolverImpl (), which uses platform-specific code, is wrapped in root.bootstrap() before being added to the recognizer chain.
The purpose of root.bootstrap is to use the platform recognition tool to search for a.root-servers.net, b.root-servers.net, etc. using a synchronous Windows platform resolver (which works and returns IP addresses), and then make direct DNS queries against root servers. UDP packets running on root servers are then blocked by our corporate firewall - I see them in Wireshark.
By default, the call to getResolver () used by the names .client.getHostByName () calls directly createResolver() , which can again lead to this broken setting loaded by the working DNS setting. For most environments, I think that adding Resolver (with root or explicit servers) to ResolverChain along with _ThreadedResolverImpl as backup will work, except that the platform converter is a different interface.
Here's an example of asynchronously locating local DNS servers (by analyzing ipconfig output), and then installing a custom resolver to use them.
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()
I cleaned it and posted it here https://gist.github.com/shuckc/af7490e1c4a2652ca740