Files
x/internal/net/resolve.go
T
ginuerzh 8fd5200bf1 fix(net): defer DNS resolution to final dialer to preserve domain in proxy chain
Commit 0522ae8 added inline DNS resolution (PreferGo: true) in Resolve(),
resolving domain names to IPs before routing. This caused the SOCKS5
connector to send CONNECT with ATYP=IPv4 instead of ATYP=domain, breaking
chains where the backend (e.g., ssh -D) cannot reach the gost-resolved IP.

Revert the DNS fallback in Resolve() — return the address unchanged so
the original hostname flows through the proxy chain. Instead, add
net.Resolver{PreferGo: true} to the TCP dialer in internal/net/dialer,
which resolves DNS at the point of actual TCP connection — still pure
Go (no cgo thread exhaustion) but after the proxy chain routing is done.

Also rename resovle.go → resolve.go (fix typo).

Fixes go-gost/gost#877
2026-06-25 11:02:36 +08:00

64 lines
1.8 KiB
Go

package net
import (
"context"
"fmt"
"net"
"github.com/go-gost/core/hosts"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/resolver"
ctxvalue "github.com/go-gost/x/ctx"
)
// Resolve resolves addr to a concrete IP address. If hosts is non-nil, it is
// consulted first. If r is non-nil and no host mapping matched, the resolver
// is used. If neither is available, the address is returned unchanged so that
// the original hostname is preserved for downstream connectors (e.g., SOCKS5
// ATYP=domain). DNS resolution is deferred to the final TCP dialer, which uses
// a pure-Go resolver to avoid cgo thread exhaustion.
func Resolve(ctx context.Context, network, addr string, r resolver.Resolver, hosts hosts.HostMapper, log logger.Logger) (string, error) {
if addr == "" {
return addr, nil
}
host, port, _ := net.SplitHostPort(addr)
if host == "" {
return addr, nil
}
if log == nil {
log = logger.Default()
}
log = log.WithFields(map[string]any{
"sid": ctxvalue.SidFromContext(ctx),
})
if hosts != nil {
if ips, _ := hosts.Lookup(ctx, network, host); len(ips) > 0 {
log.Debugf("hit host mapper: %s -> %s", host, ips)
return net.JoinHostPort(ips[0].String(), port), nil
}
}
if r != nil {
ips, err := r.Resolve(ctx, network, host)
if err != nil {
if err == resolver.ErrInvalid {
return addr, nil
}
log.Error(err)
}
if len(ips) == 0 {
return "", fmt.Errorf("resolver: domain %s does not exist", host)
}
return net.JoinHostPort(ips[0].String(), port), nil
}
// Return the address unchanged so that the original hostname is
// preserved through the proxy chain. DNS resolution happens in the
// final TCP dialer (dialer.go) which uses net.Resolver{PreferGo: true}
// to avoid cgo-based lookups that block OS threads.
return addr, nil
}