@ 261101.7
current
package main

import (
	"encoding/binary"
	"encoding/hex"
	"net/netip"
	"os"
	"strconv"
	"strings"
)

// taken explains why ep cannot be bound when no www of this user serves
// there: it names the listeners that hold the port, marking the ones that
// are www servers on other addresses.
func taken(ep netip.AddrPort) string {
	var names []string
	for _, l := range holders(ep) {
		name := l.String()
		if len(peekTrees(l)) > 0 {
			name = "the " + progName + " on " + name
		}
		names = append(names, name)
	}
	msg := ep.String() + " is taken"
	if len(names) > 0 {
		msg += " by " + strings.Join(names, ", ")
	} else {
		msg += ", and no " + progName + " of yours serves there"
	}
	if ep.Addr().IsUnspecified() {
		msg += ". 0.0.0.0 claims the port on every address; share on one address, eg --share, or on another port, eg --share=0.0.0.0:8081"
	}
	return msg
}

// holders returns the TCP listeners whose address keeps ep from binding:
// one on the same address, or on either side a wildcard on the same port.
func holders(ep netip.AddrPort) []netip.AddrPort {
	var out []netip.AddrPort
	for _, f := range []string{"/proc/net/tcp", "/proc/net/tcp6"} {
		b, err := os.ReadFile(f)
		if err != nil {
			continue
		}
		for _, l := range listeners(string(b)) {
			if l.Port() == ep.Port() && (ep.Addr().IsUnspecified() || l.Addr().IsUnspecified() || l.Addr() == ep.Addr()) {
				out = append(out, l)
			}
		}
	}
	return out
}

// listeners reads the sockets in LISTEN state from a /proc/net/tcp or tcp6
// table. The kernel prints each 32-bit word of an address in host order.
func listeners(table string) []netip.AddrPort {
	var out []netip.AddrPort
	for _, line := range strings.Split(table, "\n") {
		f := strings.Fields(line)
		if len(f) < 4 || f[3] != "0A" {
			continue
		}
		addr, port, ok := strings.Cut(f[1], ":")
		raw, err := hex.DecodeString(addr)
		n, perr := strconv.ParseUint(port, 16, 16)
		if !ok || err != nil || perr != nil || (len(raw) != 4 && len(raw) != 16) {
			continue
		}
		for i := 0; i < len(raw); i += 4 {
			binary.NativeEndian.PutUint32(raw[i:], binary.BigEndian.Uint32(raw[i:]))
		}
		a, _ := netip.AddrFromSlice(raw)
		out = append(out, netip.AddrPortFrom(a.Unmap(), uint16(n)))
	}
	return out
}