Files
vpn-loopback-firewall/channel/dnsmsg.py
T
lou af43b07dc0 fix: 清 Pylance 类型检查提示(老板标注)
- client.py: 删未使用导入 TYPE_AUTH_OK/TYPE_DNS_RESP/TYPE_HTTP_RESP
- dnsmsg.py: flags 解包后未用 -> 补 QR=1 响应拒绝(符合'非 query 返回 None'语义)
- httpmsg.py: headers default_factory=list 推断 Unknown -> lambda: [] 显式类型
- smoke_channel.py: 删未使用导入 json/os/socket/FrameReader/pack_frame; cfg 显式 dict[str, object]
- analyze_capture.py: proto Counter() 无类型参数 -> Counter[int]
2026-09-09 16:32:31 +08:00

49 lines
1.4 KiB
Python

"""DNS 报文解析(纯函数, 只取分流需要的字段)。
客户端 query 原样透传(不重新生成 ID): 本地 -> 云端 -> 上游递归,
响应沿原路返回, ID 全程不变, 客户端按 (ID, question) 匹配天然成立。
"""
from __future__ import annotations
import struct
DNS_HEADER_LEN = 12
def parse_qname(pkt: bytes) -> str | None:
"""提取 query 的 question 域名(小写, 无尾点)。
只处理标准 query(12B 头 + 一个 question, qname 无压缩指针)。
畸形/非 query 返回 None(调用方按"不分流"处理)。
"""
if len(pkt) < DNS_HEADER_LEN + 5:
return None
flags = struct.unpack("!H", pkt[2:4])[0]
if flags & 0x8000: # QR=1 是响应报文, 非 query, 拒绝
return None
qdcount = struct.unpack("!H", pkt[4:6])[0]
if qdcount != 1:
return None
i = DNS_HEADER_LEN
labels: list[str] = []
end = len(pkt)
while i < end:
length = pkt[i]
if length == 0:
break
if length & 0xC0: # 压缩指针(应答里才出现, query 不应有)
return None
i += 1
if i + length > end:
return None
try:
labels.append(pkt[i : i + length].decode("ascii").lower())
except UnicodeDecodeError:
return None
i += length
else:
return None # 没遇到 0 结尾
if i + 4 > end: # qtype + qclass
return None
return ".".join(labels)