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:
@@ -0,0 +1,44 @@
|
||||
# Only the lwIP backend needs this fix. Keep the managed component immutable.
|
||||
function(project_mdns_membership_overlay)
|
||||
if(CONFIG_MDNS_NETWORKING_SOCKET)
|
||||
return()
|
||||
endif()
|
||||
idf_component_get_property(mdns_dir espressif__mdns COMPONENT_DIR)
|
||||
idf_component_get_property(mdns_lib espressif__mdns COMPONENT_LIB)
|
||||
idf_component_get_property(mdns_version espressif__mdns COMPONENT_VERSION)
|
||||
if(NOT mdns_version STREQUAL "1.12.0")
|
||||
message(FATAL_ERROR "mDNS membership overlay requires component version 1.12.0; review upstream")
|
||||
endif()
|
||||
idf_build_get_property(python PYTHON)
|
||||
set(helper "${CMAKE_CURRENT_LIST_DIR}/mdns_membership.py")
|
||||
set(original "${mdns_dir}/mdns_networking_lwip.c")
|
||||
set(overlay "${CMAKE_BINARY_DIR}/mdns_membership/mdns_networking_lwip.c")
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
||||
"${helper}" "${original}" "${mdns_dir}/idf_component.yml" "${overlay}")
|
||||
execute_process(COMMAND "${python}" "${helper}" "${mdns_dir}" "${overlay}"
|
||||
RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE error)
|
||||
if(NOT result EQUAL 0)
|
||||
message(FATAL_ERROR "mDNS membership overlay failed: ${output}${error}")
|
||||
endif()
|
||||
|
||||
get_target_property(sources ${mdns_lib} SOURCES)
|
||||
get_target_property(source_dir ${mdns_lib} SOURCE_DIR)
|
||||
set(replaced 0)
|
||||
set(updated_sources)
|
||||
foreach(source IN LISTS sources)
|
||||
get_filename_component(absolute "${source}" ABSOLUTE BASE_DIR "${source_dir}")
|
||||
if(absolute STREQUAL original)
|
||||
list(APPEND updated_sources "${overlay}")
|
||||
math(EXPR replaced "${replaced} + 1")
|
||||
else()
|
||||
list(APPEND updated_sources "${source}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT replaced EQUAL 1)
|
||||
message(FATAL_ERROR "Expected exactly one mDNS lwIP target source, found ${replaced}; review upstream CMake")
|
||||
endif()
|
||||
set_property(TARGET ${mdns_lib} PROPERTY SOURCES "${updated_sources}")
|
||||
message(STATUS "mDNS 1.12.0: using guarded build-local multicast membership fix")
|
||||
endfunction()
|
||||
|
||||
project_mdns_membership_overlay()
|
||||
@@ -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")
|
||||
Reference in New Issue
Block a user