自制Python DNS服务器与客户端Wireshark畸形数据包问题求助
DNS服务器返回畸形数据包问题排查
我在运行自制的DNS服务器与客户端时,发现Wireshark中显示畸形数据包,无法定位问题所在。该DNS服务器的设计逻辑是嗅探DNS协议传输的数据包、提取信息并向客户端返回响应。
服务器代码
import socket # Server parameters server_ip = '127.0.0.1' server_port = 53 # Create a UDP socket for DNS communication dns_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) dns_socket.bind((server_ip, server_port)) print(f"DNS server listening on {server_ip}:{server_port}") # Loop to handle incoming DNS requests while True: # Receive a DNS query from a client dns_query, client_address = dns_socket.recvfrom(1024) try: query_str = dns_query.decode('utf-8') print(f"Received DNS query '{query_str}' from {client_address[0]}:{client_address[1]}") except UnicodeDecodeError: print(f"Received DNS query from {client_address[0]}:{client_address[1]}") # Resolve the DNS query and get the corresponding IP address # This is where you would implement your own DNS resolution logic ip_address = '10.9.1.205' # Send the IP address back to the client as the DNS response dns_socket.sendto(ip_address.encode(), client_address) print(f"Sent DNS response '{ip_address}' to {client_address[0]}:{client_address[1]}")
客户端代码
import socket # Client parameters server_ip = '127.0.0.1' server_port = 53 query_domain = 'www.google.com' # Create a UDP socket for DNS communication dns_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) dns_socket.bind((server_ip, 0)) # Use any available port for client # Construct the DNS query message # See https://www.cloudflare.com/learning/dns/dns-records/dns-message-format/ # for the format of a DNS query message dns_query = b'\xab\xcd' # 2-byte transaction ID dns_query += b'\x01\x00' # Standard query with recursion dns_query += b'\x00\x01' # One question in the query dns_query += b'\x00\x00' # No answers, authority, or additional sections dns_query += b'\x00\x00' # No answers, authority, or additional sections dns_query += bytes.fromhex('03 77 77 77') # www dns_query += bytes.fromhex('06 67 6f 6f 67 6c 65') # google dns_query += bytes.fromhex('03 63 6f 6d') # com dns_query += b'\x00' # End of domain name dns_query += b'\x00\x01' # Type A (IPv4 address) query dns_query += b'\x00\x01' # Class IN (Internet) # Send the DNS query to the DNS server dns_socket.sendto(dns_query, (server_ip, server_port)) print(f"Sent DNS query '{query_domain}' to {server_ip}:{server_port}") # Wait for the DNS response from the server dns_response, server_address = dns_socket.recvfrom(1024) print(f"Received DNS response '{dns_response.decode()}' from {server_address[0]}:{server_address[1]}") # Close the socket dns_socket.close()
补充说明
- 更换多个客户端后仍存在畸形数据包问题
- PyCharm中可正常获取信息,但Wireshark显示异常
- DHCP服务器也出现类似问题
问题原因与解决方法
核心原因
DNS是二进制协议,不是文本协议。你的服务器直接把字符串形式的IP地址编码后发送,完全不符合DNS响应的二进制格式规范,Wireshark解析不了这种非标准数据包,就会标记为畸形。另外,服务器尝试用utf-8解码DNS查询也是错误的——DNS查询是二进制结构,不存在可直接解码的UTF-8文本内容。
解决步骤
1. 构造符合规范的DNS响应数据包
DNS响应必须遵循固定二进制格式,至少包含:事务ID(与请求一致)、标志位、计数字段、问题部分、回答部分。修改服务器的响应构造逻辑:
# 从请求中提取事务ID transaction_id = dns_query[:2] # 标志位:响应、无错误、递归可用 flags = b'\x81\x80' # 问题数、回答数、权威数、附加数 counts = b'\x00\x01\x00\x01\x00\x00\x00\x00' # 复用请求中的问题部分 question_section = dns_query[12:] # 回答部分:域名指针、类型A、类别IN、TTL、数据长度、IP二进制 answer_section = b'\xc0\x0c' # 指针指向问题中的域名 answer_section += b'\x00\x01' # 类型A answer_section += b'\x00\x01' # 类别IN answer_section += b'\x00\x00\x0e\x10' # TTL(3600秒) answer_section += b'\x00\x04' # IPv4地址长度(4字节) # 将IP转为二进制 ip_bytes = socket.inet_aton('10.9.1.205') answer_section += ip_bytes # 组装完整响应 dns_response = transaction_id + flags + counts + question_section + answer_section # 发送响应 dns_socket.sendto(dns_response, client_address)
2. 移除错误的UTF-8解码逻辑
服务器不需要把DNS查询解码为字符串,直接处理二进制数据包即可,替换原解码代码为:
print(f"Received DNS query from {client_address[0]}:{client_address[1]}, hex: {dns_query.hex()}")
3. DHCP问题的通用解决思路
DHCP同样是二进制协议,出现类似问题的原因也是直接发送文本而非符合规范的二进制数据包。解决时:
- 严格遵循DHCP协议的二进制格式构造数据包
- 不要用文本编码/解码处理DHCP数据包
- 参考DHCP协议文档构建正确报文结构
内容的提问来源于stack exchange,提问作者Ronin Saad
相关产品推荐
相关产品推荐

