Files
vpn-loopback-firewall/channel/dnsmsg.py
T
lou 96d44569fd L2/L3 通道: HTTP/DNS 套壳转发(本地分流代理 + 云端转发器)
- proto.py: 帧协议 [type][req_id][len][payload], req_id 多路复用
- client.py: TLS 长连 + AUTH + 断线自动重连(全标准库, OpenWrt 可跑)
- proxy_http.py: 本地 HTTP 透明代理(明文 80 走通道, 失败回 502)
- proxy_dns.py: DNS 分流(内网域 -> 学校 DNS, 公网域 -> 云通道递归)
- server.py: 云端 AUTH + HTTPS 套壳(统一 UA, 剔 hop-by-hop)+ DNS 递归
- deploy/hook.sh: REDIRECT 规则(内网段放行, 认证 eportal 绝不走通道)
- 30 个新单测(40 总)+ smoke_channel 端到端冒烟 PASS
2026-09-09 14:55:02 +08:00

47 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]
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)