我将为你介绍三种主流的 Python DNS 服务器搭建方法,从简单到专业,并附上详细的代码示例和说明。

使用 dnslib - 最简单、最轻量
dnslib 是一个纯 Python 实现的 DNS 库,非常适合快速创建一个功能完整的 DNS 服务器,无需依赖外部库(如 dnspython 的网络部分)。
特点:
- 纯 Python:安装和部署非常简单。
- 轻量级:代码库小,资源占用低。
- 功能齐全:支持解析和应答 DNS 查询,可以自定义记录。
- 内置服务器:提供了一个简单的
UDPServer类,可以直接启动。
适用场景:
- 快速原型开发。
- 学习 DNS 协议。
- 构建简单的、自定义逻辑的 DNS 解析服务。
安装
pip install dnslib
代码示例
下面是一个使用 dnslib 搭建的、可以响应 A 记录查询的简单 DNS 服务器。

# dns_server_dnslib.py
from dnslib import DNSRecord, DNSQuestion, RR, QTYPE, A
from dnslib.server import DNSServer, BaseResolver
import socket
import time
# 1. 定义一个自定义的解析器
class SimpleResolver(BaseResolver):
"""
一个简单的 DNS 解析器。
它会根据域名返回一个预设的 IP 地址。
"""
def resolve(self, request, handler):
"""
处理 DNS 查询请求
:param request: DNS 请求对象
:param handler: 请求处理器对象
:return: DNS 响应对象
"""
reply = request.reply()
qname = request.q.qname
qtype = request.q.qtype
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Received query: {qname} (Type: {QTYPE.get(qtype)})")
# 我们只处理 A 记录查询 (qtype == 1)
if qtype == QTYPE.A:
# 定义我们的域名到 IP 的映射
domain_mapping = {
b'www.mytestserver.com.': '192.168.1.100',
b'api.mytestserver.com.': '192.168.1.101',
}
# 如果查询的域名在我们的映射表中
if qname in domain_mapping:
# 在响应中添加一个 Answer Record (RR)
# RR(rname, rtype, rclass, rttl, rdata)
reply.add_answer(
RR(
rname=qname,
rtype=QTYPE.A,
rclass=1, # IN (Internet)
rttl=60, # 60秒的 TTL (Time To Live)
rdata=A(domain_mapping[qname])
)
)
print(f" -> Resolved to {domain_mapping[qname]}")
# 也可以添加其他记录类型,NS 记录
# reply.add_auth(RR(...))
# reply.add_ar(RR(...))
return reply
# 2. 启动 DNS 服务器
if __name__ == '__main__':
# 创建解析器实例
resolver = SimpleResolver()
# 创建 UDP 服务器实例
# interface='0.0.0.0' 表示监听所有网络接口
# port=53 是 DNS 的标准端口
server = DNSServer(resolver, port=53, interface='0.0.0.0')
print("Starting DNS server on 0.0.0.0:53...")
print("It will resolve 'www.mytestserver.com' to 192.168.1.100")
print("Press Ctrl+C to stop the server.")
try:
server.start()
except KeyboardInterrupt:
server.stop()
print("\nDNS server stopped.")
如何运行和测试:
-
运行服务器:
python dns_server_dnslib.py
服务器将在
0.0.0:53上启动。 -
测试: 打开另一个终端,使用
dig或nslookup命令进行测试。
(图片来源网络,侵删)# 使用 dig 测试 dig @127.0.0.1 www.mytestserver.com # 输出应该类似这样: # ... # ;; QUESTION SECTION: # ;www.mytestserver.com. IN A # # ;; ANSWER SECTION: # www.mytestserver.com. 60 IN A 192.168.1.100 # ...
使用 dnspython - 最强大、最灵活
dnspython 是 Python 中功能最强大的 DNS 工具包,它不仅可以作为客户端,也可以作为服务器,它的服务器功能更底层,提供了更大的灵活性,但代码量也稍多。
特点:
- 功能强大:支持所有 DNS 记录类型、DNSSEC、TSIG 等。
- 灵活度高:可以更精细地控制请求处理流程。
- 工业级:被广泛用于生产环境的各种 DNS 工具中。
适用场景:
- 构建复杂的、生产级别的 DNS 服务器。
- 需要实现高级 DNS 功能(如动态更新、DNSSEC)。
- 对性能和可扩展性有较高要求。
安装
pip install dnspython
代码示例
下面是一个使用 dnspython 的异步 DNS 服务器示例,这种方式更适合处理高并发。
# dns_server_dnspython.py
import dns.asyncio
import dns.message
import dns.name
import dns.rdatatype
import dns.rdataclass
import dns.rdata
import asyncio
import socket
# 定义域名到 IP 的映射
DOMAIN_IP_MAP = {
'www.mytestserver.com.': '192.168.1.200',
'api.mytestserver.com.': '192.168.1.201',
}
class DnsHandler:
"""
dnspython 的请求处理器
"""
async def handle(self, query, transport):
"""
处理传入的 DNS 查询
"""
# 将二进制查询数据解析为 dns.message.Message 对象
request = dns.message.from_wire(query)
print(f"[{asyncio.get_event_loop().time():.2f}] Received query for: {request.question[0].name}")
# 创建一个空的响应消息
response = dns.message.make_response(request)
# 遍历查询问题部分
for question in request.question:
qname = question.name.to_text()
qtype = question.rdtype
# 只处理 A 记录查询
if qtype == dns.rdatatype.A:
# 如果域名在我们的映射中
if qname in DOMAIN_IP_MAP:
# 创建一个 A 记录
rr = dns.rdata.from_text(
dns.rdataclass.IN,
dns.rdatatype.A,
DOMAIN_IP_MAP[qname]
)
# 将记录添加到响应的 answer 部分
response.answer.append(rr)
print(f" -> Resolved {qname} to {DOMAIN_IP_MAP[qname]}")
# 将响应对象序列化为二进制 wire 格式
response_wire = response.to_wire()
# 将响应数据发送回客户端
transport.sendto(response_wire, transport.get_extra_info('addr'))
async def main():
"""
主函数,启动 DNS 服务器
"""
# 创建一个异步的 UDP 服务器
loop = asyncio.get_running_loop()
# transport, protocol = await loop.create_datagram_endpoint(...)
# 上面的方式更底层,这里我们用 dns.asyncio 模块提供的便利方法
# 创建一个 DNS 服务器实例
# 它会自动处理套接字创建和事件循环集成
server = dns.asyncio.DNSUDPServer(
handler=DnsHandler(),
# 可以指定监听地址和端口
# address='0.0.0.0',
# port=53
)
# 启动服务器
await server.start()
print(f"DNS server started on {server.address[0]}:{server.address[1]}")
print("It will resolve 'www.mytestserver.com' to 192.168.1.200")
try:
# 保持服务器运行
await asyncio.Future()
except KeyboardInterrupt:
print("\nShutting down server...")
await server.stop()
if __name__ == '__main__':
try:
asyncio.run(main())
except KeyboardInterrupt:
pass
如何运行和测试:
-
运行服务器:
python dns_server_dnspython.py
服务器将启动。
-
测试: 同样使用
dig或nslookup。dig @127.0.0.1 www.mytestserver.com # 应该会返回 192.168.1.200
使用 rbxdns - 高性能、C 扩展
rbxdns 是一个基于 rbtree 的、高性能的 DNS 库,它使用了 C 扩展来处理 DNS 协议的核心部分,因此在处理大量查询时性能远超纯 Python 实现。
特点:
- 高性能:专为高吞吐量设计。
- 底层优化:C 语言核心,速度快。
- 功能专注:专注于核心的 DNS 解析功能。
适用场景:
- 需要处理极高 DNS 查询量的场景。
- 对性能有极致要求的应用。
安装
pip install rbxdns
代码示例
# dns_server_rbxdns.py
import rbxdns
import time
# 定义域名到 IP 的映射
DOMAIN_IP_MAP = {
'www.mytestserver.com.': '192.168.1.300',
'api.mytestserver.com.': '192.168.1.301',
}
# 定义一个回调函数来处理查询
def handle_dns_request(request, data, from_addr):
"""
rbxdns 的请求处理函数
:param request: DNS 请求对象
:param data: 原始请求数据
:param from_addr: 客户端地址
"""
qname = request.q.name
qtype = request.q.type
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Query from {from_addr[0]} for {qname} (Type: {qtype})")
response = rbxdns.DNSResponse(request)
if qtype == rbxdns.TYPE_A:
if qname in DOMAIN_IP_MAP:
# 添加一个 A 记录到响应中
response.add_answer(qname, rbxdns.TYPE_A, rbxdns.CLASS_IN, 60, DOMAIN_IP_MAP[qname])
print(f" -> Resolved {qname} to {DOMAIN_IP_MAP[qname]}")
# 返回响应数据
return response.pack()
if __name__ == '__main__':
# 创建一个 DNS 服务器实例
# port=53, interface='0.0.0.0'
server = rbxdns.DNSServer(handle_dns_request, port=53, interface='0.0.0.0')
print("Starting high-performance rbxdns server on 0.0.0.0:53...")
print("It will resolve 'www.mytestserver.com' to 192.168.1.300")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nDNS server stopped.")
server.server_close()
如何运行和测试:
-
运行服务器:
python dns_server_rbxdns.py
-
测试: 使用
dig或nslookup。dig @127.0.0.1 www.mytestserver.com # 应该会返回 192.168.1.300
总结与对比
| 特性 | dnslib |
dnspython |
rbxdns |
|---|---|---|---|
| 易用性 | 最高,几行代码即可启动 | 高,但更底层,代码稍多 | 中等,API 直观 |
| 性能 | 低 (纯 Python) | 中 (纯 Python,但优化好) | 高 (C 扩展) |
| 功能 | 基本功能齐全 | 最全,支持所有标准及扩展 | 核心功能强大 |
| 灵活性 | 中等 | 最高,可深度定制 | 中等 |
| 适用场景 | 快速原型、学习、简单服务 | 复杂应用、生产环境、高级功能 | 高性能、高并发场景 |
| 依赖 | 无外部 C 依赖 | 无外部 C 依赖 | 需要 C 编译器 |
如何选择?
- 如果你是初学者,或者只是想快速搭建一个用于本地测试的 DNS 服务器:
dnslib是你的不二之选,它简单、快速、足够用。 - 如果你正在构建一个复杂的、需要长期维护的 DNS 服务,或者需要实现一些高级功能(如动态更新、DNSSEC):
dnspython是最强大的选择,虽然代码量稍多,但它的灵活性和可靠性是经过考验的。 - 如果你对性能有极致要求,需要处理每秒数万甚至数十万的查询:
rbxdns是最佳选择,它的 C 扩展核心能提供无与伦比的性能。
希望这份详细的指南能帮助你成功搭建自己的 Python DNS 服务器!
