af43b07dc0
- 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]
49 lines
1.4 KiB
Python
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)
|