Complete Phase 12 dual-stack networking

Add IPv6-aware Wi-Fi state, HTTPS/SSH listeners, mDNS service
reconciliation, and browser Wi-Fi administration.

Include a guarded build-local fix for mDNS 1.12.0 membership handling,
focused regression suites, and Phase 12 acceptance documentation.
This commit is contained in:
2026-09-20 22:35:34 +02:00
parent ece4ba77e3
commit 8902b25d78
52 changed files with 3042 additions and 163 deletions
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Generate the narrowly guarded mDNS 1.12.0 membership overlay; never edit upstream."""
import argparse
import hashlib
from pathlib import Path
import re
SOURCE_SHA256 = "adc139fa504a925ab644f21f8dce3659927f534e390a176b72b0ae3206c6a3ea"
OLD_DEINIT = """ s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
if (s_interfaces[tcpip_if].proto == 0) {
s_interfaces[tcpip_if].ready = false;
join_group(tcpip_if, ip_protocol, false);
"""
NEW_DEINIT = """ int proto = (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
if (!(s_interfaces[tcpip_if].proto & proto)) {
return;
}
join_group(tcpip_if, ip_protocol, false);
s_interfaces[tcpip_if].proto &= ~proto;
if (s_interfaces[tcpip_if].proto == 0) {
s_interfaces[tcpip_if].ready = false;
"""
OLD_INIT = """ err = pcb_init();
if (err) {
return err;
}
"""
NEW_INIT = """ err = pcb_init();
if (err) {
join_group(tcpip_if, ip_protocol, false);
return err;
}
"""
def generate(component: Path, output: Path) -> None:
manifest = (component / "idf_component.yml").read_text()
if re.findall(r"^version:\s*(\S+)\s*$", manifest, re.MULTILINE) != ["1.12.0"]:
raise ValueError("mDNS membership overlay requires exactly version 1.12.0; review upstream")
original = (component / "mdns_networking_lwip.c").read_bytes()
if hashlib.sha256(original).hexdigest() != SOURCE_SHA256:
raise ValueError("mDNS networking source SHA-256 mismatch; review upstream, do not bypass guard")
patched = original.decode("utf-8")
for old, new in ((OLD_DEINIT, NEW_DEINIT), (OLD_INIT, NEW_INIT)):
if patched.count(old) != 1:
raise ValueError("mDNS membership replacement must match exactly once")
patched = patched.replace(old, new, 1)
result = patched.encode("utf-8")
output.parent.mkdir(parents=True, exist_ok=True)
if not output.exists() or output.read_bytes() != result:
output.write_bytes(result)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("component", type=Path)
parser.add_argument("output", type=Path)
args = parser.parse_args()
try:
generate(args.component, args.output)
except (OSError, ValueError) as error:
parser.exit(1, f"mDNS membership overlay: {error}\n")