我正在尝试使用 Python 2.7 在 Twisted 中创建一个数据库驱动的 DNS 服务器(专门用于仅处理 MX 记录,并将其他所有内容传递给上游)。下面的代码有效(就获得结果而言),但不是异步操作。相反,任何传入的 DNS 请求都会阻止整个程序接受任何其他请求,直到第一个请求得到答复。我们需要扩大规模,目前我们无法弄清楚我们哪里出了问题。如果有人有一个可行的例子可以分享,或者看到这个问题,我们将永远感激不尽。
import settings
import db
from twisted.names import dns, server, client, cache
from twisted.application import service, internet
from twisted.internet import defer
class DNSResolver(client.Resolver):
def __init__(self, servers):
client.Resolver.__init__(self, servers=servers)
@defer.inlineCallbacks
def _lookup_mx_records(self, hostname, timeout):
# Check the DB to see if we handle this domain.
mx_results = yield db.get_domain_mx_record_list(hostname)
if mx_results:
defer.returnValue(
[([dns.RRHeader(hostname, dns.MX, dns.IN, settings.DNS_TTL,
dns.Record_MX(priority, forward, settings.DNS_TTL))
for forward, priority in mx_results]),
(), ()])
# If the hostname isn't in the DB, we forward
# to our upstream DNS provider (8.8.8.8).
else:
i = yield self._lookup(hostname, dns.IN, dns.MX, timeout)
defer.returnValue(i)
def lookupMailExchange(self, name, timeout=None):
"""
The twisted function which is called when an MX record lookup is requested.
:param name: The domain name being queried for (e.g. example.org).
:param timeout: Time in seconds to wait for the query response. (optional, default: None)
:return: A DNS response for the record query.
"""
return self._lookup_mx_records(name, timeout)
# App name, UID, GID to run as. (root/root for port 53 bind)
application = service.Application('db_driven_dns', 1, 1)
# Set the secondary resolver
db_dns_resolver = DNSResolver(settings.DNS_NAMESERVERS)
# Create the protocol handlers
f = server.DNSServerFactory(caches=[cache.CacheResolver()], clients=[db_dns_resolver])
p = dns.DNSDatagramProtocol(f)
f.noisy = p.noisy = False
# Register as a tcp and udp service
ret = service.MultiService()
PORT=53
for (klass, arg) in [(internet.TCPServer, f), (internet.UDPServer, p)]:
s = klass(PORT, arg)
s.setServiceParent(ret)
# Run all of the above as a twistd application
ret.setServiceParent(service.IServiceCollection(application))
编辑#1
blakev 建议我可能没有正确使用生成器(这当然是可能的)。但是,如果我稍微简化一下,甚至不使用数据库,我仍然不能一次处理多个 DNS 请求。为了测试这一点,我已经剥离了课程。接下来是我的整个、可运行的测试文件。即使在我的服务器的这个高度精简的版本中,Twisted 在第一个请求到来之前也不会再接受任何请求。
import sys
import logging
from twisted.names import dns, server, client, cache
from twisted.application import service, internet
from twisted.internet import defer
class DNSResolver(client.Resolver):
def __init__(self, servers):
client.Resolver.__init__(self, servers=servers)
def lookupMailExchange(self, name, timeout=None):
"""
The twisted function which is called when an MX record lookup is requested.
:param name: The domain name being queried for (e.g. example.org).
:param timeout: Time in seconds to wait for the query response. (optional, default: None)
:return: A DNS response for the record query.
"""
logging.critical("Query for " + name)
return defer.succeed([
(dns.RRHeader(name, dns.MX, dns.IN, 600,
dns.Record_MX(1, "10.0.0.9", 600)),), (), ()
])
# App name, UID, GID to run as. (root/root for port 53 bind)
application = service.Application('db_driven_dns', 1, 1)
# Set the secondary resolver
db_dns_resolver = DNSResolver( [("8.8.8.8", 53), ("8.8.4.4", 53)] )
# Create the protocol handlers
f = server.DNSServerFactory(caches=[cache.CacheResolver()], clients=[db_dns_resolver])
p = dns.DNSDatagramProtocol(f)
f.noisy = p.noisy = False
# Register as a tcp and udp service
ret = service.MultiService()
PORT=53
for (klass, arg) in [(internet.TCPServer, f), (internet.UDPServer, p)]:
s = klass(PORT, arg)
s.setServiceParent(ret)
# Run all of the above as a twistd application
ret.setServiceParent(service.IServiceCollection(application))
# If called directly, instruct the user to run it through twistd
if __name__ == '__main__':
print "Usage: sudo twistd -y %s (background) OR sudo twistd -noy %s (foreground)" % (sys.argv[0], sys.argv[0])