Files
x/internal/net/resovle_test.go
T
ginuerzh 0522ae8155 fix(net): use pure-Go DNS fallback to prevent thread exhaustion
When no custom resolver or host mapper is configured, xnet.Resolve now
resolves hostnames via Go's native pure-Go resolver (PreferGo: true)
instead of returning the hostname unchanged.

Previously, the unresolved hostname would reach net.Dialer.DialContext,
which on CGO_ENABLED=1 builds used cgo-based DNS — blocking a dedicated
OS thread per concurrent lookup. At scale (1000+ listeners), this
exhausted Go's 10000-thread limit.

Fixes go-gost/gost#550
2026-06-21 22:34:43 +08:00

146 lines
3.7 KiB
Go

package net
import (
"context"
"net"
"testing"
"github.com/go-gost/core/hosts"
"github.com/go-gost/core/resolver"
)
type mockResolver struct {
ips []net.IP
err error
}
func (m *mockResolver) Resolve(ctx context.Context, network, host string, opts ...resolver.Option) ([]net.IP, error) {
if m.err != nil {
return nil, m.err
}
return m.ips, nil
}
type mockHostMapper struct {
ips []net.IP
}
func (m *mockHostMapper) Lookup(ctx context.Context, network, host string, opts ...hosts.Option) ([]net.IP, bool) {
if len(m.ips) == 0 {
return nil, false
}
return m.ips, true
}
func TestResolve_EmptyAddr(t *testing.T) {
ctx := context.Background()
addr, err := Resolve(ctx, "tcp", "", nil, nil, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != "" {
t.Errorf("expected empty, got %s", addr)
}
}
func TestResolve_NoHost(t *testing.T) {
ctx := context.Background()
addr, err := Resolve(ctx, "tcp", ":8080", nil, nil, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != ":8080" {
t.Errorf("expected :8080, got %s", addr)
}
}
func TestResolve_HostMapperMatch(t *testing.T) {
ctx := context.Background()
mapper := &mockHostMapper{ips: []net.IP{net.ParseIP("10.0.0.1")}}
r := &mockResolver{ips: []net.IP{net.ParseIP("192.168.1.1")}}
addr, err := Resolve(ctx, "tcp", "example.com:8080", r, mapper, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != "10.0.0.1:8080" {
t.Errorf("expected 10.0.0.1:8080, got %s", addr)
}
}
func TestResolve_Resolver(t *testing.T) {
ctx := context.Background()
r := &mockResolver{ips: []net.IP{net.ParseIP("192.168.1.1")}}
addr, err := Resolve(ctx, "tcp", "example.com:8080", r, nil, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != "192.168.1.1:8080" {
t.Errorf("expected 192.168.1.1:8080, got %s", addr)
}
}
func TestResolve_ResolverInvalid(t *testing.T) {
ctx := context.Background()
r := &mockResolver{err: resolver.ErrInvalid}
addr, err := Resolve(ctx, "tcp", "example.com:8080", r, nil, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != "example.com:8080" {
t.Errorf("expected example.com:8080, got %s", addr)
}
}
func TestResolve_ResolverOtherError(t *testing.T) {
ctx := context.Background()
r := &mockResolver{err: net.UnknownNetworkError("test-error")}
_, err := Resolve(ctx, "tcp", "example.com:8080", r, nil, nil)
if err == nil {
t.Error("expected error, got nil")
}
}
func TestResolve_ResolverEmptyResult(t *testing.T) {
ctx := context.Background()
r := &mockResolver{ips: nil}
_, err := Resolve(ctx, "tcp", "example.com:8080", r, nil, nil)
if err == nil {
t.Error("expected error, got nil")
}
}
func TestResolve_NoResolverNoHosts(t *testing.T) {
// When no resolver and no host mapper are configured, the function now
// resolves via Go's native DNS resolver (PreferGo: true). This test
// uses an IP address which is short-circuited at the top of Resolve,
// avoiding the need for actual DNS resolution in unit tests.
// DNS fallback behavior is verified by the e2e tests.
ctx := context.Background()
addr, err := Resolve(ctx, "tcp", "1.2.3.4:8080", nil, nil, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != "1.2.3.4:8080" {
t.Errorf("expected 1.2.3.4:8080, got %s", addr)
}
}
func TestResolve_HostsNoMatchUsesResolver(t *testing.T) {
ctx := context.Background()
mapper := &mockHostMapper{}
r := &mockResolver{ips: []net.IP{net.ParseIP("10.0.0.1")}}
addr, err := Resolve(ctx, "tcp", "example.com:8080", r, mapper, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if addr != "10.0.0.1:8080" {
t.Errorf("expected 10.0.0.1:8080, got %s", addr)
}
}