diff --git a/internal/httputil/client.go b/internal/httputil/client.go index 8a1df16503..ad218a0cfa 100644 --- a/internal/httputil/client.go +++ b/internal/httputil/client.go @@ -50,14 +50,23 @@ func ctlLocalOnly(network, address string, _ syscall.RawConn) error { Err: "disallowed by policy", } } - addr := net.ParseIP(address) + host, _, err := net.SplitHostPort(address) + if err != nil { + return &net.AddrError{ + Addr: network + "!" + address, + Err: "martian address", + } + } + addr := net.ParseIP(host) if addr == nil { return &net.AddrError{ Addr: network + "!" + address, Err: "martian address", } } - if !addr.IsPrivate() { + if !addr.IsPrivate() && + !addr.IsLoopback() && + !addr.IsLinkLocalUnicast() { return &net.AddrError{ Addr: network + "!" + address, Err: "disallowed by policy", diff --git a/internal/httputil/client_test.go b/internal/httputil/client_test.go new file mode 100644 index 0000000000..287c31b2ad --- /dev/null +++ b/internal/httputil/client_test.go @@ -0,0 +1,57 @@ +package httputil + +import ( + "errors" + "net" + "testing" +) + +func TestLocalOnly(t *testing.T) { + tt := []struct { + Network string + Addr string + Err *net.AddrError + }{ + { + Network: "tcp4", + Addr: "192.168.0.1:443", + Err: nil, + }, + { + Network: "tcp4", + Addr: "127.0.0.1:443", + Err: nil, + }, + { + Network: "tcp6", + Addr: "[fe80::]:443", + Err: nil, + }, + { + Network: "tcp4", + Addr: "8.8.8.8:443", + Err: &net.AddrError{ + Addr: "tcp4!8.8.8.8:443", + Err: "disallowed by policy", + }, + }, + { + Network: "tcp6", + Addr: "[2000::]:443", + Err: &net.AddrError{ + Addr: "tcp6![2000::]:443", + Err: "disallowed by policy", + }, + }, + } + for _, tc := range tt { + t.Logf("%s!%s", tc.Network, tc.Addr) + var nErr *net.AddrError + got := ctlLocalOnly(tc.Network, tc.Addr, nil) + if errors.As(got, &nErr) { + if tc.Err.Err != nErr.Err || tc.Err.Addr != nErr.Addr { + t.Errorf("got: %v, want: %v", got, tc.Err) + } + } + } +}