Files
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

115 lines
3.4 KiB
Python

"""端到端冒烟: 本机起 CloudServer + ChannelClient, 验证 AUTH/帧/HTTP/DNS 链路。
用法: .venv/bin/python scripts/smoke_channel.py
- 生成临时自签证书(/tmp/smoke-tls/)
- 本机起 HTTP 回显目标(18080) + CloudServer(18443, forward_scheme=http)
- ChannelClient 连 127.0.0.1:18443, 验证: AUTH、HTTP 套壳转发、DNS 帧往返
DNS 目标走公网 223.5.5.5, 学校网络不通时只验证帧链路(ERROR/超时也算通路)。
"""
from __future__ import annotations
import http.server
import subprocess
import tempfile
import threading
from pathlib import Path
from channel.client import ChannelClient
from channel.httpmsg import parse_request
from channel.server import CloudConfig, CloudServer
WORK = Path(tempfile.mkdtemp(prefix="smoke-channel-"))
CERT = WORK / "cert.pem"
KEY = WORK / "key.pem"
TARGET_PORT = 18080
CLOUD_PORT = 18443
TOKEN = "smoke-token"
def _make_cert() -> None:
subprocess.run(
[
"openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes",
"-keyout", str(KEY), "-out", str(CERT), "-days", "1",
"-subj", "/CN=127.0.0.1",
],
check=True,
capture_output=True,
)
class _Echo(http.server.BaseHTTPRequestHandler):
def do_GET(self) -> None:
body = ("echo:" + self.path).encode()
self.send_response(200)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, format: str, *args: object) -> None:
pass
def _run_target() -> None:
srv = http.server.ThreadingHTTPServer(("127.0.0.1", TARGET_PORT), _Echo)
srv.serve_forever()
def main() -> int:
_make_cert()
target = threading.Thread(target=_run_target, daemon=True)
target.start()
cfg: dict[str, object] = {
"cloud_side": {
"listen_port": CLOUD_PORT,
"token": TOKEN,
"dns_upstream": "223.5.5.5",
"forward_scheme": "http",
"ua": "SmokeUA/1.0",
"deny_suffixes": [],
"tls": {"cert_file": str(CERT), "key_file": str(KEY)},
}
}
conf = CloudConfig(cfg)
srv = CloudServer(("127.0.0.1", CLOUD_PORT), conf)
threading.Thread(target=srv.serve_forever, daemon=True).start()
client = ChannelClient(
host="127.0.0.1", port=CLOUD_PORT, token=TOKEN, retries=1
)
client.connect()
print("[smoke] AUTH ok")
raw_req = (
f"GET /ping HTTP/1.1\r\nHost: 127.0.0.1:{TARGET_PORT}\r\n"
"User-Agent: curl/8\r\n\r\n"
).encode()
resp = client.send_http_request(raw_req)
assert resp is not None, "http resp is None"
assert resp.startswith(b"HTTP/1.1 200"), resp[:60]
parsed = parse_request(raw_req)
assert parsed is not None
assert b"echo:/ping" in resp
print(f"[smoke] HTTP 套壳转发 ok ({len(resp)}B 响应)")
qname = b"www.baidu.com"
query = (
b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00"
+ b"\x03www\x05baidu\x03com\x00\x00\x01\x00\x01"
)
dns_resp = client.send_dns_query(query)
if dns_resp is not None and len(dns_resp) >= 12:
print(f"[smoke] DNS 帧往返 ok ({len(dns_resp)}B, qname={qname.decode()})")
else:
print("[smoke] DNS 帧往返: ERROR/超时(学校网络公网 DNS 不通属正常), 链路本身已通")
client.close()
srv.shutdown()
print("[smoke] PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())