diff --git a/internal/amneziawgnet/resolving_bind.go b/internal/amneziawgnet/resolving_bind.go index 1f92c16da..d8bb57d4b 100644 --- a/internal/amneziawgnet/resolving_bind.go +++ b/internal/amneziawgnet/resolving_bind.go @@ -39,8 +39,8 @@ func newResolvingBind(listen string) *resolvingBind { return &resolvingBind{Bind: newListenBind(listen)} } -// ParseEndpoint resolves hostnames before handing the address to amneziawg-go -// (whose own implementation accepts literal IPs only). +// ParseEndpoint resolves hostnames, then lets the wrapped bind build the endpoint: +// its own parser takes literal IPs only, and WinRingBind sends to its own type only. func (b *resolvingBind) ParseEndpoint(s string) (awgconn.Endpoint, error) { host, portStr, err := net.SplitHostPort(strings.TrimSpace(s)) if err != nil { @@ -63,5 +63,5 @@ func (b *resolvingBind) ParseEndpoint(s string) (awgconn.Endpoint, error) { } addr = addrs[0] } - return &awgconn.StdNetEndpoint{AddrPort: netip.AddrPortFrom(addr.Unmap(), uint16(port64))}, nil + return b.Bind.ParseEndpoint(netip.AddrPortFrom(addr.Unmap(), uint16(port64)).String()) } diff --git a/internal/amneziawgnet/resolving_bind_test.go b/internal/amneziawgnet/resolving_bind_test.go index d17b596f6..d81d5e38a 100644 --- a/internal/amneziawgnet/resolving_bind_test.go +++ b/internal/amneziawgnet/resolving_bind_test.go @@ -14,12 +14,39 @@ func mustResolvingBind(t *testing.T) *resolvingBind { return newResolvingBind("") } -func endpointAddrPort(ep awgconn.Endpoint) netip.AddrPort { - std, ok := ep.(*awgconn.StdNetEndpoint) - if !ok { - panic("unexpected endpoint type") +// endpointAddrPort reads any bind's endpoint; Windows' default bind has its own type. +func endpointAddrPort(t *testing.T, ep awgconn.Endpoint) netip.AddrPort { + t.Helper() + ap, err := netip.ParseAddrPort(ep.DstToString()) + if err != nil { + t.Fatalf("endpoint %q: %v", ep.DstToString(), err) + } + return ap +} + +// ownEndpointBind accepts only endpoints it parsed itself, as WinRingBind does. +type ownEndpointBind struct{ awgconn.Bind } + +type ownEndpoint struct{ awgconn.StdNetEndpoint } + +func (ownEndpointBind) ParseEndpoint(s string) (awgconn.Endpoint, error) { + ap, err := netip.ParseAddrPort(s) + if err != nil { + return nil, err + } + return &ownEndpoint{awgconn.StdNetEndpoint{AddrPort: ap}}, nil +} + +// WinRingBind, the default bind on Windows, refuses to send to an endpoint of any +// other type, so a hand-built StdNetEndpoint killed every handshake there. +func TestResolvingBind_ParseEndpointComesFromTheWrappedBind(t *testing.T) { + ep, err := (&resolvingBind{Bind: ownEndpointBind{}}).ParseEndpoint("203.0.113.7:51820") + if err != nil { + t.Fatalf("ParseEndpoint: %v", err) + } + if _, ok := ep.(*ownEndpoint); !ok { + t.Fatalf("endpoint is %T, not the wrapped bind's own type", ep) } - return std.AddrPort } func TestResolvingBind_ParseEndpointIPLiteral(t *testing.T) { @@ -28,7 +55,7 @@ func TestResolvingBind_ParseEndpointIPLiteral(t *testing.T) { if err != nil { t.Fatalf("IP endpoint rejected: %v", err) } - got := endpointAddrPort(ep) + got := endpointAddrPort(t, ep) if got.Addr().String() != "203.0.113.7" || got.Port() != 51820 { t.Fatalf("endpoint = %v, want 203.0.113.7:51820", got) } @@ -49,7 +76,7 @@ func TestResolvingBind_ParseEndpointHostnameResolves(t *testing.T) { if err != nil { t.Fatalf("hostname endpoint rejected: %v", err) } - if got := endpointAddrPort(ep); got.Addr().String() != "198.51.100.9" || got.Port() != 443 { + if got := endpointAddrPort(t, ep); got.Addr().String() != "198.51.100.9" || got.Port() != 443 { t.Fatalf("endpoint = %v, want 198.51.100.9:443", got) } }