Files
seaweedfs/weed/storage/blockvol/net_util.go
T
Ping QiuandClaude Opus 4.6 4f3edffb0a fix: canonical replica address resolution (CP13-2)
ReplicaReceiver.DataAddr()/CtrlAddr() now return canonical ip:port
instead of raw listener addresses that may be wildcard (:port,
0.0.0.0:port, [::]:port).

New canonicalizeListenerAddr() resolves wildcard IPs using the
provided advertised host (from VS listen address). Falls back to
outbound-IP detection when no advertised host is available.

NewReplicaReceiver accepts optional advertisedHost parameter for
multi-NIC correctness. In production, the assignment path already
provides canonical addresses; this fix ensures test patterns with
:0 bind also produce routable addresses.

7 new tests. TestBug3_ReplicaAddr_MustBeIPPort_WildcardBind flips
from FAIL to PASS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:38:55 -07:00

54 lines
1.7 KiB
Go

package blockvol
import (
"net"
"strconv"
)
// canonicalizeListenerAddr resolves wildcard listener addresses to a routable
// ip:port string using the provided advertised host as the preferred IP.
//
// If the listener bound to a wildcard (":0", "0.0.0.0:port", "[::]:port"),
// the returned address uses advertisedHost instead of the wildcard.
//
// If advertisedHost is empty, falls back to preferredOutboundIP() as a
// best-effort guess. On multi-NIC hosts, the advertised host should always
// be provided to avoid selecting the wrong network interface.
func canonicalizeListenerAddr(addr net.Addr, advertisedHost string) string {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return addr.String()
}
ip := tcpAddr.IP
if ip != nil && !ip.IsUnspecified() {
// Already bound to a specific IP — return as-is.
return net.JoinHostPort(ip.String(), strconv.Itoa(tcpAddr.Port))
}
// Wildcard bind — use advertised host or fallback.
host := advertisedHost
if host == "" {
host = preferredOutboundIP()
}
if host == "" {
// Last resort: return raw address (will be ":port").
return addr.String()
}
return net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
}
// preferredOutboundIP returns the machine's preferred outbound IP as a string.
// Uses the standard Go pattern: dial a UDP socket (no data sent), read the
// local address. Returns "" if discovery fails.
//
// This is a fallback — callers should prefer an explicitly configured
// advertised host when available.
func preferredOutboundIP() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return ""
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}