watcher: an IPv6 address is written in brackets before its port (closes #270)
check / check (push) Successful in 3m33s
check / check (push) Successful in 3m33s
Port and certificate keys were joined as address:port, so an IPv6 address ran into its port: 2a01:4f8:2200:fd11::1:80 is also an address on its own. Keys are now joined with net.JoinHostPort and split with net.SplitHostPort, giving [2a01:4f8:2200:fd11::1]:80 in the state file, /api/v1/status, the dashboard and Port Change notifications; an IPv4 address is unchanged. A certificate key still splits at its last colon, since a hostname has none. Tests that built IPv6 keys by hand now use the bracketed form, and a test checks that an IPv6 address's certificate entry is removed when its port 443 closes. Model: opus-5-5
This commit is contained in:
+12
-13
@@ -6,8 +6,10 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"net"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -1000,7 +1002,7 @@ func (w *Watcher) buildPortAssociations() map[string][]string {
|
||||
ips := w.collectIPs(name)
|
||||
for _, ip := range ips {
|
||||
for _, port := range monitoredPorts {
|
||||
key := fmt.Sprintf("%s:%d", ip, port)
|
||||
key := net.JoinHostPort(ip, strconv.Itoa(port))
|
||||
if assoc[key] == nil {
|
||||
assoc[key] = make(map[string]bool)
|
||||
}
|
||||
@@ -1025,18 +1027,15 @@ func (w *Watcher) buildPortAssociations() map[string][]string {
|
||||
return result
|
||||
}
|
||||
|
||||
// parsePortKey splits an "ip:port" key into its components.
|
||||
// parsePortKey splits an "ip:port" key, written by net.JoinHostPort,
|
||||
// into its components.
|
||||
func parsePortKey(key string) (string, int) {
|
||||
lastColon := strings.LastIndex(key, ":")
|
||||
if lastColon < 0 {
|
||||
ip, port, err := net.SplitHostPort(key)
|
||||
if err != nil {
|
||||
return key, 0
|
||||
}
|
||||
|
||||
ip := key[:lastColon]
|
||||
|
||||
var p int
|
||||
|
||||
_, err := fmt.Sscanf(key[lastColon+1:], "%d", &p)
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return ip, 0
|
||||
}
|
||||
@@ -1074,7 +1073,7 @@ func (w *Watcher) cleanupStaleCertificates() {
|
||||
for _, key := range w.state.GetAllCertificateKeys() {
|
||||
ip, hostname := parseCertKey(key)
|
||||
|
||||
ps, ok := w.state.GetPortState(fmt.Sprintf("%s:%d", ip, tlsPort))
|
||||
ps, ok := w.state.GetPortState(net.JoinHostPort(ip, strconv.Itoa(tlsPort)))
|
||||
if ok && !ps.Open {
|
||||
w.state.DeleteCertificateState(key)
|
||||
|
||||
@@ -1189,7 +1188,7 @@ func (w *Watcher) checkSinglePort(
|
||||
return
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("%s:%d", ip, port)
|
||||
key := net.JoinHostPort(ip, strconv.Itoa(port))
|
||||
now := time.Now().UTC()
|
||||
prev, hasPrev := w.state.GetPortState(key)
|
||||
|
||||
@@ -1251,7 +1250,7 @@ func (w *Watcher) checkTLSForHostname(
|
||||
ips := w.collectIPs(hostname)
|
||||
|
||||
for _, ip := range ips {
|
||||
portKey := fmt.Sprintf("%s:%d", ip, tlsPort)
|
||||
portKey := net.JoinHostPort(ip, strconv.Itoa(tlsPort))
|
||||
|
||||
ps, ok := w.state.GetPortState(portKey)
|
||||
if !ok || !ps.Open {
|
||||
@@ -1275,7 +1274,7 @@ func (w *Watcher) checkTLSCert(
|
||||
return
|
||||
}
|
||||
|
||||
certKey := fmt.Sprintf("%s:%d:%s", ip, tlsPort, hostname)
|
||||
certKey := net.JoinHostPort(ip, strconv.Itoa(tlsPort)) + ":" + hostname
|
||||
now := time.Now().UTC()
|
||||
prev, hasPrev := w.state.GetCertificateState(certKey)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user