当前位置: 代码网 > it编程>前端脚本>Python > Python3简易DNS服务器实现方式

Python3简易DNS服务器实现方式

2025年07月06日 Python 我要评论
使用python3开发一个简单的dns服务器,支持配置资源记录(rr),并能通过dig命令进行查询。让自己理解dns原理实现方案我们将使用socketserver和dnslib库来构建这个dns服务器

使用python3开发一个简单的dns服务器,支持配置资源记录(rr),并能通过dig命令进行查询。

让自己理解dns原理

实现方案

我们将使用socketserverdnslib库来构建这个dns服务器。dnslib库能帮助我们处理dns协议的复杂细节。

1. 安装依赖

首先确保安装了dnslib库:

pip install dnslib

2. dns服务器实现代码

#!/usr/bin/env python3
import socketserver
from dnslib import dnsrecord, dnsheader, qtype, rr, a, aaaa, cname, txt
from dnslib.server import dnsserver, baseresolver

class simpleresolver(baseresolver):
    """
    自定义dns解析器,包含我们配置的资源记录
    """
    def __init__(self):
        # 初始化资源记录字典
        self.records = {
            # a记录 (域名 -> ipv4)
            'example.com.': [
                rr('example.com.', qtype.a, rdata=a('93.184.216.34'), ttl=60),
                rr('example.com.', qtype.a, rdata=a('93.184.216.35'), ttl=60)
            ],
            # aaaa记录 (域名 -> ipv6)
            'ipv6.example.com.': [
                rr('ipv6.example.com.', qtype.aaaa, rdata=aaaa('2606:2800:220:1:248:1893:25c8:1946'), ttl=60)
            ],
            # cname记录 (别名)
            'www.example.com.': [
                rr('www.example.com.', qtype.cname, rdata=cname('example.com.'), ttl=60)
            ],
            # txt记录
            'txt.example.com.': [
                rr('txt.example.com.', qtype.txt, rdata=txt('this is a test txt record'), ttl=60)
            ]
        }
  
    def resolve(self, request, handler):
        """
        处理dns查询请求
        """
        reply = request.reply()
        qname = request.q.qname
        qtype = request.q.qtype
      
        # 记录查询日志
        print(f"received query: {qname} (type: {qtype[qtype]})")
      
        # 检查是否有匹配的记录
        if str(qname) in self.records:
            for record in self.records[str(qname)]:
                if record.rtype == qtype or qtype == qtype.any:
                    reply.add_answer(record)
      
        return reply

def main():
    """
    启动dns服务器
    """
    resolver = simpleresolver()
  
    # 创建dns服务器,监听udp 53端口
    server = dnsserver(
        resolver,
        port=53,
        address="0.0.0.0",  # 监听所有接口
        tcp=false           # 仅使用udp
    )
  
    print("starting dns server on port 53...")
    try:
        server.start()
    except keyboardinterrupt:
        server.stop()
        print("\ndns server stopped")

if __name__ == '__main__':
    main()

3. 运行服务器

由于dns服务需要使用53端口,在linux/macos上需要以root权限运行:

sudo python3 dns_server.py

4. 使用dig测试

打开另一个终端窗口,使用dig命令测试:

# 查询a记录
dig @127.0.0.1 example.com

# 输出中有
# example.com.            60      in      a       93.184.216.34
# example.com.            60      in      a       93.184.216.35


# 查询aaaa记录
dig @127.0.0.1 -t aaaa ipv6.example.com

# 查询cname记录
dig @127.0.0.1 -t cname www.example.com

# 查询txt记录
dig @127.0.0.1 -t txt txt.example.com

进阶功能

1. 从配置文件加载资源记录

我们可以改进代码,从json文件加载资源记录:

import json

class configurableresolver(baseresolver):
    def __init__(self, config_file='dns_config.json'):
        self.records = {}
        self.load_config(config_file)
  
    def load_config(self, config_file):
        with open(config_file) as f:
            config = json.load(f)
      
        for domain, records in config.items():
            self.records[domain] = []
            for record in records:
                rtype = record['type'].upper()
                if rtype == 'a':
                    self.records[domain].append(
                        rr(domain, qtype.a, rdata=a(record['value']), ttl=record.get('ttl', 60))
                    )
                elif rtype == 'aaaa':
                    self.records[domain].append(
                        rr(domain, qtype.aaaa, rdata=aaaa(record['value']), ttl=record.get('ttl', 60))
                    )
                elif rtype == 'cname':
                    self.records[domain].append(
                        rr(domain, qtype.cname, rdata=cname(record['value']), ttl=record.get('ttl', 60))
                    )
                elif rtype == 'txt':
                    self.records[domain].append(
                        rr(domain, qtype.txt, rdata=txt(record['value']), ttl=record.get('ttl', 60))
                    )

示例配置文件dns_config.json:

{
    "example.com.": [
        {"type": "a", "value": "93.184.216.34", "ttl": 300},
        {"type": "a", "value": "93.184.216.35", "ttl": 300}
    ],
    "ipv6.example.com.": [
        {"type": "aaaa", "value": "2606:2800:220:1:248:1893:25c8:1946"}
    ],
    "www.example.com.": [
        {"type": "cname", "value": "example.com."}
    ],
    "txt.example.com.": [
        {"type": "txt", "value": "this is a test txt record"}
    ]
}

2. 添加日志记录

我们可以添加更详细的日志记录:

import logging

# 配置日志
logging.basicconfig(
    level=logging.info,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.filehandler('dns_server.log'),
        logging.streamhandler()
    ]
)

class loggingresolver(baseresolver):
    def resolve(self, request, handler):
        client_ip = handler.client_address[0]
        qname = request.q.qname
        qtype = qtype[request.q.qtype]
      
        logging.info(f"query from {client_ip}: {qname} (type: {qtype})")
      
        reply = super().resolve(request, handler)
      
        if reply.rr:
            for answer in reply.rr:
                logging.info(f"responded with: {answer}")
        else:
            logging.warning(f"no records found for {qname} (type: {qtype})")
      
        return reply

注意事项

端口权限:dns服务器需要使用53端口,在unix-like系统上需要root权限。

防火墙设置:确保防火墙允许udp 53端口的传入连接。

系统dns缓存:测试时可能需要清除本地dns缓存:

  • macos: sudo killall -hup mdnsresponder
  • linux: 取决于发行版,可能是systemd-resolve --flush-caches

性能考虑:这个实现是单线程的,对于高负载环境,可以考虑使用多线程或异步io。

安全性:生产环境应考虑添加dns查询限制、防止dns放大攻击等安全措施。

这个实现提供了基本的dns服务器功能,你可以根据需要扩展更多记录类型(mx, ns, soa等)或添加更复杂的逻辑。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com