Files
3x-ui/internal/amneziawgnet/resolving_bind.go
T
mrchatam 7a41c59494 fix(amneziawg): honor inbound listen when binding UDP socket (#6461)
* fix(amneziawg): honor inbound listen when binding UDP socket

AmneziaWG inbounds ignored the listen field and always opened
awgconn.NewDefaultBind(), so multi-IP hosts replied from the primary
address and handshakes to a secondary IP never completed (#6367).

Carry Inbound.Listen on amneziawg.Instance, open a Bind pinned to that
address (wildcard when empty/0.0.0.0/::), and include listen in
addressFingerprint so edits rebuild the Device.

Fixes #6367

* fix(amneziawg): fall back to wildcard when listen is unusable

Invalid or non-local listen values no longer hard-fail inbound startup;
treat ::0/[::0] as wildcards and normalize listen in the bind fingerprint.

* fix(amneziawg): use ListenConfig.ListenPacket for noctx

---------

Co-authored-by: mrchatam <mrchatam@users.noreply.github.com>
Co-authored-by: mrchatam <287639636+mrchatam@users.noreply.github.com>
2026-09-12 11:11:02 +02:00

68 lines
1.9 KiB
Go

package amneziawgnet
import (
"context"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
"time"
awgconn "github.com/amnezia-vpn/amneziawg-go/v3/conn"
)
// endpointResolveTimeout bounds the one-time DNS lookup in ParseEndpoint.
const endpointResolveTimeout = 5 * time.Second
// resolvingBind wraps a Bind so peer endpoints may be hostnames (#6367).
// Concrete listen values use pinnedBind; wildcards keep StdNetBind.
type resolvingBind struct {
awgconn.Bind
}
var lookupEndpointHost = defaultLookupEndpointHost
func defaultLookupEndpointHost(ctx context.Context, host string) ([]netip.Addr, error) {
addrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host)
if err != nil {
return nil, err
}
out := make([]netip.Addr, 0, len(addrs))
for _, a := range addrs {
out = append(out, a.Unmap())
}
return out, nil
}
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).
func (b *resolvingBind) ParseEndpoint(s string) (awgconn.Endpoint, error) {
host, portStr, err := net.SplitHostPort(strings.TrimSpace(s))
if err != nil {
return nil, fmt.Errorf("endpoint %q: %w", s, err)
}
port64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil || port64 == 0 {
return nil, fmt.Errorf("endpoint %q: bad port", s)
}
addr, err := netip.ParseAddr(host)
if err != nil {
ctx, cancel := context.WithTimeout(context.Background(), endpointResolveTimeout)
defer cancel()
addrs, rerr := lookupEndpointHost(ctx, host)
if rerr != nil {
return nil, fmt.Errorf("endpoint %q: resolve host: %w", s, rerr)
}
if len(addrs) == 0 {
return nil, fmt.Errorf("endpoint %q: host resolved to no addresses", s)
}
addr = addrs[0]
}
return &awgconn.StdNetEndpoint{AddrPort: netip.AddrPortFrom(addr.Unmap(), uint16(port64))}, nil
}