切换为默认的dns解析;增加resolver=system的支持

This commit is contained in:
wenyifan
2026-07-02 20:50:24 +08:00
parent a24d79fd45
commit bf85966600
6 changed files with 312 additions and 24 deletions
+3
View File
@@ -96,6 +96,8 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
resolverCfg.Nameservers,
&config.NameserverConfig{
Addr: rs,
Prefer: mdutil.GetString(md, "prefer"),
Only: mdutil.GetString(md, "only"),
},
)
}
@@ -272,6 +274,7 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
&config.NameserverConfig{
Addr: rs,
Prefer: mdutil.GetString(md, "prefer"),
Only: mdutil.GetString(md, "only"),
},
)
}
+53 -1
View File
@@ -1068,6 +1068,31 @@ func TestBuildConfigFromCmd_NodeWithResolver(t *testing.T) {
}
}
func TestBuildConfigFromCmd_NodeResolverFamilyOptions(t *testing.T) {
cfg, err := BuildConfigFromCmd(
[]string{":8080"},
[]string{"http://proxy:3128?resolver=auto&prefer=ipv6&only=ipv4"},
)
if err != nil {
t.Fatalf("BuildConfigFromCmd: %v", err)
}
rs := cfg.Resolvers[0]
if len(rs.Nameservers) != 1 {
t.Fatalf("len(Nameservers) = %d, want 1", len(rs.Nameservers))
}
ns := rs.Nameservers[0]
if ns.Addr != "auto" {
t.Errorf("Addr = %q, want auto", ns.Addr)
}
if ns.Prefer != "ipv6" {
t.Errorf("Prefer = %q, want ipv6", ns.Prefer)
}
if ns.Only != "ipv4" {
t.Errorf("Only = %q, want ipv4", ns.Only)
}
}
func TestBuildConfigFromCmd_NodeWithHosts(t *testing.T) {
cfg, err := BuildConfigFromCmd(
[]string{":8080"},
@@ -1171,6 +1196,34 @@ func TestBuildConfigFromCmd_ServiceWithResolver(t *testing.T) {
}
}
func TestBuildConfigFromCmd_ServiceSystemResolverOnlyIPv4(t *testing.T) {
cfg, err := BuildConfigFromCmd(
[]string{"http://:8080?resolver=system&only=ipv4"},
nil,
)
if err != nil {
t.Fatalf("BuildConfigFromCmd: %v", err)
}
if len(cfg.Resolvers) != 1 {
t.Fatalf("len(Resolvers) = %d, want 1", len(cfg.Resolvers))
}
rs := cfg.Resolvers[0]
if len(rs.Nameservers) != 1 {
t.Fatalf("len(Nameservers) = %d, want 1", len(rs.Nameservers))
}
ns := rs.Nameservers[0]
if ns.Addr != "system" {
t.Errorf("Addr = %q, want system", ns.Addr)
}
if ns.Only != "ipv4" {
t.Errorf("Only = %q, want ipv4", ns.Only)
}
if cfg.Services[0].Resolver != rs.Name {
t.Errorf("Service.Resolver = %q, want %q", cfg.Services[0].Resolver, rs.Name)
}
}
func TestBuildConfigFromCmd_ServiceWithHosts(t *testing.T) {
cfg, err := BuildConfigFromCmd(
[]string{":8080?hosts=example.com:1.2.3.4"},
@@ -1896,4 +1949,3 @@ func TestBuildNodeConfig_TLSOnlySecure(t *testing.T) {
t.Error("TLS.Secure should be true")
}
}
+3 -4
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net"
"runtime"
"strings"
"syscall"
"time"
@@ -147,9 +146,9 @@ func (d *Dialer) dialOnce(ctx context.Context, network, addr, ifceName string, i
return nil, fmt.Errorf("dial: unsupported network %s", network)
}
var resolver *net.Resolver
if runtime.GOOS != "android" {
resolver = &net.Resolver{PreferGo: true}
}
//if runtime.GOOS != "android" {
// resolver = &net.Resolver{PreferGo: true}
//}
netd := net.Dialer{
Resolver: resolver,
LocalAddr: ifAddr,
+14
View File
@@ -82,6 +82,11 @@ func NewExchanger(addr string, opts ...Option) (Exchanger, error) {
opt(&options)
}
addr = strings.TrimSpace(addr)
if isSystemResolverAddr(addr) {
return newSystemExchanger(addr, options), nil
}
if !strings.Contains(addr, "://") {
addr = "udp://" + addr
}
@@ -143,6 +148,15 @@ func NewExchanger(addr string, opts ...Option) (Exchanger, error) {
return ex, nil
}
func isSystemResolverAddr(addr string) bool {
switch strings.ToLower(strings.TrimSpace(addr)) {
case "system", "auto", "system://", "auto://":
return true
default:
return false
}
}
func (ex *exchanger) Exchange(ctx context.Context, msg []byte) ([]byte, error) {
if ex.network == "https" {
return ex.dohExchange(ctx, msg)
+112
View File
@@ -10,6 +10,7 @@ import (
"github.com/go-gost/core/chain"
xlogger "github.com/go-gost/x/logger"
"github.com/miekg/dns"
)
func TestNewExchanger_UDP(t *testing.T) {
@@ -175,6 +176,117 @@ func TestNewExchanger_String(t *testing.T) {
}
}
func TestNewExchanger_System(t *testing.T) {
ex, err := NewExchanger("system", LoggerOption(xlogger.Nop()))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, ok := ex.(*systemExchanger); !ok {
t.Fatalf("NewExchanger(system) = %T, want *systemExchanger", ex)
}
if ex.String() != "system" {
t.Errorf("String() = %q, want system", ex.String())
}
}
func TestNewExchanger_AutoSystem(t *testing.T) {
ex, err := NewExchanger("auto", LoggerOption(xlogger.Nop()))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, ok := ex.(*systemExchanger); !ok {
t.Fatalf("NewExchanger(auto) = %T, want *systemExchanger", ex)
}
}
func TestSystemExchanger_ExchangeA(t *testing.T) {
ex := &systemExchanger{
rawAddr: "system",
lookupIP: func(_ context.Context, network, host string) ([]net.IP, error) {
if network != "ip4" {
t.Errorf("network = %q, want ip4", network)
}
if host != "example.com" {
t.Errorf("host = %q, want example.com", host)
}
return []net.IP{
net.ParseIP("192.0.2.1"),
net.ParseIP("2001:db8::1"),
}, nil
},
}
mq := new(dns.Msg)
mq.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
packed, err := mq.Pack()
if err != nil {
t.Fatal(err)
}
reply, err := ex.Exchange(context.Background(), packed)
if err != nil {
t.Fatalf("Exchange: %v", err)
}
mr := new(dns.Msg)
if err := mr.Unpack(reply); err != nil {
t.Fatalf("Unpack: %v", err)
}
if len(mr.Answer) != 1 {
t.Fatalf("len(Answer) = %d, want 1", len(mr.Answer))
}
a, ok := mr.Answer[0].(*dns.A)
if !ok {
t.Fatalf("Answer[0] = %T, want *dns.A", mr.Answer[0])
}
if !a.A.Equal(net.ParseIP("192.0.2.1")) {
t.Errorf("A = %s, want 192.0.2.1", a.A)
}
}
func TestSystemExchanger_ExchangeAAAA(t *testing.T) {
ex := &systemExchanger{
rawAddr: "system",
lookupIP: func(_ context.Context, network, host string) ([]net.IP, error) {
if network != "ip6" {
t.Errorf("network = %q, want ip6", network)
}
if host != "example.com" {
t.Errorf("host = %q, want example.com", host)
}
return []net.IP{
net.ParseIP("192.0.2.1"),
net.ParseIP("2001:db8::1"),
}, nil
},
}
mq := new(dns.Msg)
mq.SetQuestion(dns.Fqdn("example.com"), dns.TypeAAAA)
packed, err := mq.Pack()
if err != nil {
t.Fatal(err)
}
reply, err := ex.Exchange(context.Background(), packed)
if err != nil {
t.Fatalf("Exchange: %v", err)
}
mr := new(dns.Msg)
if err := mr.Unpack(reply); err != nil {
t.Fatalf("Unpack: %v", err)
}
if len(mr.Answer) != 1 {
t.Fatalf("len(Answer) = %d, want 1", len(mr.Answer))
}
aaaa, ok := mr.Answer[0].(*dns.AAAA)
if !ok {
t.Fatalf("Answer[0] = %T, want *dns.AAAA", mr.Answer[0])
}
if !aaaa.AAAA.Equal(net.ParseIP("2001:db8::1")) {
t.Errorf("AAAA = %s, want 2001:db8::1", aaaa.AAAA)
}
}
func TestExchange_DialFailure(t *testing.T) {
ex, err := NewExchanger("udp://127.0.0.1:1", TimeoutOption(100*time.Millisecond), LoggerOption(xlogger.Nop()))
if err != nil {
+108
View File
@@ -0,0 +1,108 @@
package exchanger
import (
"context"
"errors"
"net"
"strings"
"time"
"github.com/miekg/dns"
)
type systemExchanger struct {
rawAddr string
timeout time.Duration
lookupIP func(ctx context.Context, network, host string) ([]net.IP, error)
}
func newSystemExchanger(rawAddr string, opts Options) Exchanger {
if opts.timeout <= 0 {
opts.timeout = 5 * time.Second
}
ex := &systemExchanger{
rawAddr: rawAddr,
timeout: opts.timeout,
}
ex.lookupIP = net.DefaultResolver.LookupIP
return ex
}
func (ex *systemExchanger) Exchange(ctx context.Context, msg []byte) ([]byte, error) {
mq := new(dns.Msg)
if err := mq.Unpack(msg); err != nil {
return nil, err
}
mr := new(dns.Msg)
mr.SetReply(mq)
mr.RecursionAvailable = true
if len(mq.Question) == 0 {
return mr.Pack()
}
q := mq.Question[0]
network := "ip"
switch q.Qtype {
case dns.TypeA:
network = "ip4"
case dns.TypeAAAA:
network = "ip6"
default:
return mr.Pack()
}
if ex.timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, ex.timeout)
defer cancel()
}
host := strings.TrimSuffix(q.Name, ".")
ips, err := ex.lookupIP(ctx, network, host)
if err != nil {
var dnsErr *net.DNSError
if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
return nil, err
}
}
for _, ip := range ips {
switch q.Qtype {
case dns.TypeA:
if ip4 := ip.To4(); ip4 != nil {
mr.Answer = append(mr.Answer, &dns.A{
Hdr: dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeA,
Class: q.Qclass,
},
A: ip4,
})
}
case dns.TypeAAAA:
if ip.To4() == nil {
if ip16 := ip.To16(); ip16 != nil {
mr.Answer = append(mr.Answer, &dns.AAAA{
Hdr: dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeAAAA,
Class: q.Qclass,
},
AAAA: ip16,
})
}
}
}
}
return mr.Pack()
}
func (ex *systemExchanger) String() string {
if ex.rawAddr == "" {
return "system"
}
return ex.rawAddr
}