// SPDX-FileCopyrightText: 2026 Echolot contributors // SPDX-License-Identifier: GPL-3.0-or-later package selftest import ( "fmt" "net" ) // ReservedWebPortsFree verifies that nothing on this host — this process or any other — is // listening on 80/443 of the reserved measurement addresses. // // config.CheckReserved keeps *our own* listeners off those ports, but our configuration is not // the host: the adb-beacon receiver, a separate Python process wildcard-bound to 0.0.0.0:443, // silently voided the IPv4 interception proof for as long as it ran, and nothing in this server's // config could have seen it. Asking the OS is the only check that covers processes we did not // start. // // The mechanism is a throwaway bind: if the bind succeeds the port was provably free (closed // again immediately — nothing is served), and if it fails with EADDRINUSE something is listening // there. Any other failure (address not assigned to this host, missing privilege) means the // question could not be answered, which is reported separately rather than pretending either way. func ReservedWebPortsFree(ips []net.IP) (occupied, unverifiable []string) { return portsFree(ips, []string{"80", "443"}) } func portsFree(ips []net.IP, ports []string) (occupied, unverifiable []string) { for _, ip := range ips { for _, port := range ports { addr := net.JoinHostPort(ip.String(), port) ln, err := net.Listen("tcp", addr) if err == nil { ln.Close() continue } if isAddrInUse(err) { occupied = append(occupied, addr) } else { unverifiable = append(unverifiable, fmt.Sprintf("%s (%v)", addr, err)) } } } return occupied, unverifiable }