Python3简易DNS服务器实现方式

 更新时间:2025年07月05日 14:32:56   作者:言之。  
本文介绍使用Python3和dnslib库开发简单DNS服务器的实现方案,支持配置资源记录及dig查询,涵盖JSON配置、日志记录、端口权限与安全注意事项,并建议扩展记录类型以提升功能

使用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等)或添加更复杂的逻辑。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 一步步教你用Python画五彩气球

    一步步教你用Python画五彩气球

    这篇文章主要给大家介绍了关于如何用Python画五彩气球的相关资料,主要是用turtle库自带的画笔turtle.Turtle()来绘制气球,文中给出了详细的实例代码,需要的朋友可以参考下
    2023-06-06
  • python open读取文件内容时的mode模式解析

    python open读取文件内容时的mode模式解析

    这篇文章主要介绍了python open读取文件内容时的mode模式解析,Python可以使用open函数来实现文件的打开,关闭,读写操作,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • matplotlib中legend位置调整解析

    matplotlib中legend位置调整解析

    这篇文章主要介绍了matplotlib中legend位置调整解析,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • 使用python制作游戏下载进度条的代码(程序说明见注释)

    使用python制作游戏下载进度条的代码(程序说明见注释)

    这篇文章主要介绍了用python制作游戏下载进度条的代码(程序说明见注释),代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-10-10
  • python斯皮尔曼spearman相关性分析实例

    python斯皮尔曼spearman相关性分析实例

    这篇文章主要为大家介绍了python斯皮尔曼spearman相关性分析实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Python字典实现简单的三级菜单(实例讲解)

    Python字典实现简单的三级菜单(实例讲解)

    下面小编就为大家带来一篇Python字典实现简单的三级菜单(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Python之标点符号string.punctuation的使用

    Python之标点符号string.punctuation的使用

    Python的string模块提供了一个方便的属性string.punctuation,其中包含所有ASCII标点符号字符,这使得在处理和识别字符串中的标点符号时非常有用,可以通过简单的in关键字来检测字符是否为标点
    2024-09-09
  • Python利用PIL进行图片压缩

    Python利用PIL进行图片压缩

    有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所以本文为大家介绍了Python中图片压缩的方法,需要的可以参考下
    2025-02-02
  • python如何修改图像的分辨率

    python如何修改图像的分辨率

    这篇文章主要介绍了python如何修改图像的分辨率问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 详解Python之数据序列化(json、pickle、shelve)

    详解Python之数据序列化(json、pickle、shelve)

    本篇文章主要介绍了Python之数据序列化,本节要介绍的就是Python内置的几个用于进行数据序列化的模块,有兴趣的可以了解一下。
    2017-03-03

最新评论