From d2fa21d07b52fae2c0d7bbd9ac98909c6f210a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 16 Mar 2026 09:36:24 +0800 Subject: [PATCH] Deprecate Socksaddr.IsFqdn: do not reject potentially valid domain names --- common/dialer/default.go | 2 +- common/dialer/resolve.go | 8 ++++---- go.mod | 2 +- go.sum | 4 ++-- option/dns.go | 2 +- option/outbound.go | 2 +- option/tailscale.go | 2 +- protocol/socks/outbound.go | 4 ++-- protocol/tailscale/dns_transport.go | 4 ++-- protocol/tailscale/endpoint.go | 6 +++--- protocol/vless/outbound.go | 4 ++-- protocol/vmess/outbound.go | 2 +- protocol/wireguard/endpoint.go | 4 ++-- route/route.go | 4 ++-- transport/trojan/protocol.go | 2 +- transport/wireguard/endpoint.go | 6 +++--- 16 files changed, 29 insertions(+), 29 deletions(-) diff --git a/common/dialer/default.go b/common/dialer/default.go index 6b2379f4d..39b96dfe4 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -239,7 +239,7 @@ func setMarkWrapper(networkManager adapter.NetworkManager, mark uint32, isDefaul func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { if !address.IsValid() { return nil, E.New("invalid address") - } else if address.IsFqdn() { + } else if address.IsDomain() { return nil, E.New("domain not resolved") } if d.networkStrategy == nil { diff --git a/common/dialer/resolve.go b/common/dialer/resolve.go index 49ed07038..21fe38d5c 100644 --- a/common/dialer/resolve.go +++ b/common/dialer/resolve.go @@ -96,7 +96,7 @@ func (d *resolveDialer) DialContext(ctx context.Context, network string, destina if err != nil { return nil, err } - if !destination.IsFqdn() { + if !destination.IsDomain() { return d.dialer.DialContext(ctx, network, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) @@ -116,7 +116,7 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd if err != nil { return nil, err } - if !destination.IsFqdn() { + if !destination.IsDomain() { return d.dialer.ListenPacket(ctx, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) @@ -144,7 +144,7 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context if err != nil { return nil, err } - if !destination.IsFqdn() { + if !destination.IsDomain() { return d.dialer.DialContext(ctx, network, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) @@ -167,7 +167,7 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C if err != nil { return nil, err } - if !destination.IsFqdn() { + if !destination.IsDomain() { return d.dialer.ListenPacket(ctx, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) diff --git a/go.mod b/go.mod index a394028de..8b1c752b0 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/sagernet/gomobile v0.1.12 github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 - github.com/sagernet/sing v0.8.2 + github.com/sagernet/sing v0.8.3-0.20260315153529-ed51f65fbfde github.com/sagernet/sing-mux v0.3.4 github.com/sagernet/sing-quic v0.6.0 github.com/sagernet/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index 76a680f87..222aace80 100644 --- a/go.sum +++ b/go.sum @@ -236,8 +236,8 @@ github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNen github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o= github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4= -github.com/sagernet/sing v0.8.2 h1:kX1IH9SWJv4S0T9M8O+HNahWgbOuY1VauxbF7NU5lOg= -github.com/sagernet/sing v0.8.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.8.3-0.20260315153529-ed51f65fbfde h1:RNQzlpnsXIuu1HGts/fIzJ1PR7RhrzaNlU52MDyiX1c= +github.com/sagernet/sing v0.8.3-0.20260315153529-ed51f65fbfde/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s= github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk= github.com/sagernet/sing-quic v0.6.0 h1:dhrFnP45wgVKEOT1EvtsToxdzRnHIDIAgj6WHV9pLyM= diff --git a/option/dns.go b/option/dns.go index 4c1ac208b..b5ccf2080 100644 --- a/option/dns.go +++ b/option/dns.go @@ -339,7 +339,7 @@ func (o DNSServerAddressOptions) Build() M.Socksaddr { } func (o DNSServerAddressOptions) ServerIsDomain() bool { - return M.IsDomainName(o.Server) + return o.Build().IsDomain() } func (o *DNSServerAddressOptions) TakeServerOptions() ServerOptions { diff --git a/option/outbound.go b/option/outbound.go index cb388c443..6676a3e92 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -155,7 +155,7 @@ func (o ServerOptions) Build() M.Socksaddr { } func (o ServerOptions) ServerIsDomain() bool { - return M.IsDomainName(o.Server) + return o.Build().IsDomain() } func (o *ServerOptions) TakeServerOptions() ServerOptions { diff --git a/option/tailscale.go b/option/tailscale.go index dac8e866a..68a143693 100644 --- a/option/tailscale.go +++ b/option/tailscale.go @@ -61,7 +61,7 @@ func (d DERPVerifyClientURLOptions) ServerIsDomain() bool { if err != nil { return false } - return M.IsDomainName(verifyURL.Host) + return M.ParseSocksaddr(verifyURL.Hostname()).IsDomain() } func (d DERPVerifyClientURLOptions) MarshalJSON() ([]byte, error) { diff --git a/protocol/socks/outbound.go b/protocol/socks/outbound.go index 851412ff0..344c79888 100644 --- a/protocol/socks/outbound.go +++ b/protocol/socks/outbound.go @@ -83,7 +83,7 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination default: return nil, E.Extend(N.ErrUnknownNetwork, network) } - if h.resolve && destination.IsFqdn() { + if h.resolve && destination.IsDomain() { destinationAddresses, err := h.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err @@ -101,7 +101,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination) return h.uotClient.ListenPacket(ctx, destination) } - if h.resolve && destination.IsFqdn() { + if h.resolve && destination.IsDomain() { destinationAddresses, err := h.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go index 1c227db7a..3a92a66ba 100644 --- a/protocol/tailscale/dns_transport.go +++ b/protocol/tailscale/dns_transport.go @@ -287,7 +287,7 @@ type DNSDialer struct { } func (d *DNSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - if destination.IsFqdn() { + if destination.IsDomain() { panic("invalid request here") } for _, prefix := range d.transport.routePrefixes { @@ -299,7 +299,7 @@ func (d *DNSDialer) DialContext(ctx context.Context, network string, destination } func (d *DNSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - if destination.IsFqdn() { + if destination.IsDomain() { panic("invalid request here") } for _, prefix := range d.transport.routePrefixes { diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index b8f2003d2..d1c22aed8 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -190,7 +190,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, E.Cause(err, "parse control URL") } - remoteIsDomain = M.IsDomainName(controlURL.Hostname()) + remoteIsDomain = M.ParseSocksaddr(controlURL.Hostname()).IsDomain() } else { // controlplane.tailscale.com remoteIsDomain = true @@ -492,7 +492,7 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination case N.NetworkUDP: t.logger.InfoContext(ctx, "outbound packet connection to ", destination) } - if destination.IsFqdn() { + if destination.IsDomain() { destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err @@ -578,7 +578,7 @@ func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.So func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) { t.logger.InfoContext(ctx, "outbound packet connection to ", destination) - if destination.IsFqdn() { + if destination.IsDomain() { destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, netip.Addr{}, err diff --git a/protocol/vless/outbound.go b/protocol/vless/outbound.go index ab7747606..d8132cf9d 100644 --- a/protocol/vless/outbound.go +++ b/protocol/vless/outbound.go @@ -167,7 +167,7 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati if h.xudp { return h.client.DialEarlyXUDPPacketConn(conn, destination) } else if h.packetAddr { - if destination.IsFqdn() { + if destination.IsDomain() { return nil, E.New("packetaddr: domain destination is not supported") } packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}) @@ -204,7 +204,7 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) if h.xudp { return h.client.DialEarlyXUDPPacketConn(conn, destination) } else if h.packetAddr { - if destination.IsFqdn() { + if destination.IsDomain() { return nil, E.New("packetaddr: domain destination is not supported") } conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}) diff --git a/protocol/vmess/outbound.go b/protocol/vmess/outbound.go index f0b41ae0c..703f06b1b 100644 --- a/protocol/vmess/outbound.go +++ b/protocol/vmess/outbound.go @@ -194,7 +194,7 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) return nil, err } if h.packetAddr { - if destination.IsFqdn() { + if destination.IsDomain() { return nil, E.New("packetaddr: domain destination is not supported") } return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index bcf2078ee..9fdc4814a 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -210,7 +210,7 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination case N.NetworkUDP: w.logger.InfoContext(ctx, "outbound packet connection to ", destination) } - if destination.IsFqdn() { + if destination.IsDomain() { destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err @@ -224,7 +224,7 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination func (w *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) { w.logger.InfoContext(ctx, "outbound packet connection to ", destination) - if destination.IsFqdn() { + if destination.IsDomain() { destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, netip.Addr{}, err diff --git a/route/route.go b/route/route.go index cdd7ba250..40a90e7da 100644 --- a/route/route.go +++ b/route/route.go @@ -349,7 +349,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.Dire } directRouteOutbound = defaultOutbound.(adapter.DirectRouteOutbound) } - if metadata.Destination.IsFqdn() { + if metadata.Destination.IsDomain() { if len(metadata.DestinationAddresses) == 0 { var strategy C.DomainStrategy if metadata.Source.IsIPv4() { @@ -790,7 +790,7 @@ func (r *Router) actionSniff( } func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionResolve) error { - if metadata.Destination.IsFqdn() { + if metadata.Destination.IsDomain() { var transport adapter.DNSTransport if action.Server != "" { var loaded bool diff --git a/transport/trojan/protocol.go b/transport/trojan/protocol.go index 6369d86dc..0456b6b90 100644 --- a/transport/trojan/protocol.go +++ b/transport/trojan/protocol.go @@ -136,7 +136,7 @@ func (c *ClientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) return } n = buffer.Len() - if destination.IsFqdn() { + if destination.IsDomain() { addr = destination } else { addr = destination.UDPAddr() diff --git a/transport/wireguard/endpoint.go b/transport/wireguard/endpoint.go index dac07c859..f9f4628ab 100644 --- a/transport/wireguard/endpoint.go +++ b/transport/wireguard/endpoint.go @@ -63,7 +63,7 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) { } if rawPeer.Endpoint.Addr.IsValid() { peer.endpoint = rawPeer.Endpoint.AddrPort() - } else if rawPeer.Endpoint.IsFqdn() { + } else if rawPeer.Endpoint.IsDomain() { peer.destination = rawPeer.Endpoint } publicKeyBytes, err := base64.StdEncoding.DecodeString(rawPeer.PublicKey) @@ -135,13 +135,13 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) { func (e *Endpoint) Start(resolve bool) error { if common.Any(e.peers, func(peer peerConfig) bool { - return !peer.endpoint.IsValid() && peer.destination.IsFqdn() + return !peer.endpoint.IsValid() && peer.destination.IsDomain() }) { if !resolve { return nil } for peerIndex, peer := range e.peers { - if peer.endpoint.IsValid() || !peer.destination.IsFqdn() { + if peer.endpoint.IsValid() || !peer.destination.IsDomain() { continue } destinationAddress, err := e.options.ResolvePeer(peer.destination.Fqdn)