Files
x/handler/socks/v5/metadata.go
T
ginuerzh 61eb3e2a3b feat(socks5): add udp.resolveDomain option for clients incompatible with ATYP=Domain
Add a  wrapper that intercepts WriteTo on the
client-facing SOCKS5 UDP relay and resolves domain addresses to IPs
before the SOCKS5 header is encoded. Controlled via the
 (or ) handler metadata flag.

When enabled, SOCKS5 UDP response datagrams always carry ATYP=IPv4 or
ATYP=IPv6 — never ATYP=Domain (0x03) — making them compatible with
clients like tun2proxy and Surge that cannot parse domain-typed
addresses in UDP.

The wrapper resolves domains through the router's infrastructure:
host mapper → resolver → system DNS fallback. Unresolvable domains
cause the datagram to be silently dropped rather than forwarded in a
format the client rejects.

Fixes go-gost/gost#434
2026-06-21 15:12:53 +08:00

112 lines
3.7 KiB
Go

package v5
import (
"crypto"
"crypto/tls"
"crypto/x509"
"time"
"github.com/go-gost/core/bypass"
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/x/internal/util/mux"
mdutil "github.com/go-gost/x/metadata/util"
"github.com/go-gost/x/registry"
)
type metadata struct {
publicAddr string
// readTimeout is the deadline for reading the initial SOCKS5
// handshake (auth + connect/associate/udp request) from the client
// connection. The deadline is cleared after the handshake, so it
// does not affect subsequent data transfer. Also passed to
// SnifferBuilder for the upstream response header read timeout.
// 0 or negative defaults to 15s.
readTimeout time.Duration
noTLS bool
enableBind bool
enableUDP bool
udpBufferSize int
udpResolveDomain bool
compatibilityMode bool
hash string
muxCfg *mux.Config
observerPeriod time.Duration
observerResetTraffic bool
sniffing bool
sniffingTimeout time.Duration
sniffingWebsocket bool
sniffingWebsocketSampleRate float64
certificate *x509.Certificate
privateKey crypto.PrivateKey
alpn string
mitmBypass bypass.Bypass
limiterRefreshInterval time.Duration
limiterCleanupInterval time.Duration
}
func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) {
h.md.publicAddr = mdutil.GetString(md, "socks.publicAddr", "publicAddr")
h.md.readTimeout = mdutil.GetDuration(md, "readTimeout")
if h.md.readTimeout <= 0 {
h.md.readTimeout = 15 * time.Second
}
h.md.noTLS = mdutil.GetBool(md, "notls")
h.md.enableBind = mdutil.GetBool(md, "bind")
h.md.enableUDP = mdutil.GetBool(md, "udp")
h.md.udpBufferSize = mdutil.GetInt(md, "udp.bufferSize", "udpBufferSize")
h.md.udpResolveDomain = mdutil.GetBool(md, "udp.resolveDomain", "udpResolveDomain")
h.md.compatibilityMode = mdutil.GetBool(md, "comp")
h.md.hash = mdutil.GetString(md, "hash")
h.md.muxCfg = &mux.Config{
Version: mdutil.GetInt(md, "mux.version"),
KeepAliveInterval: mdutil.GetDuration(md, "mux.keepaliveInterval"),
KeepAliveDisabled: mdutil.GetBool(md, "mux.keepaliveDisabled"),
KeepAliveTimeout: mdutil.GetDuration(md, "mux.keepaliveTimeout"),
MaxFrameSize: mdutil.GetInt(md, "mux.maxFrameSize"),
MaxReceiveBuffer: mdutil.GetInt(md, "mux.maxReceiveBuffer"),
MaxStreamBuffer: mdutil.GetInt(md, "mux.maxStreamBuffer"),
}
h.md.observerPeriod = mdutil.GetDuration(md, "observePeriod", "observer.period", "observer.observePeriod")
if h.md.observerPeriod == 0 {
h.md.observerPeriod = 5 * time.Second
}
if h.md.observerPeriod < time.Second {
h.md.observerPeriod = time.Second
}
h.md.observerResetTraffic = mdutil.GetBool(md, "observer.resetTraffic")
h.md.sniffing = mdutil.GetBool(md, "sniffing")
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
if certFile != "" && keyFile != "" {
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
h.md.certificate, err = x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
return err
}
h.md.privateKey = tlsCert.PrivateKey
}
h.md.alpn = mdutil.GetString(md, "mitm.alpn")
h.md.mitmBypass = registry.BypassRegistry().Get(mdutil.GetString(md, "mitm.bypass"))
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
h.md.limiterCleanupInterval = mdutil.GetDuration(md, "limiter.cleanupInterval")
return nil
}