From 16916cee3ac1c0a8a9cd3b4e5870073bb251d8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 15 Apr 2026 17:58:54 +0800 Subject: [PATCH] Standardize hosts path --- dns/transport/hosts/hosts.go | 6 +++++- dns/transport/hosts/hosts_file.go | 10 ++++++++++ dns/transport/hosts/hosts_test.go | 21 +++++++++++++++++---- dns/transport/hosts/hosts_unix.go | 4 +++- dns/transport/hosts/hosts_windows.go | 11 +++++------ dns/transport/local/local.go | 10 ++++++++-- dns/transport/local/local_darwin.go | 7 ++++++- dns/transport/local/local_darwin_cgo.go | 2 +- 8 files changed, 55 insertions(+), 16 deletions(-) diff --git a/dns/transport/hosts/hosts.go b/dns/transport/hosts/hosts.go index f0e70a9a3..aeb878179 100644 --- a/dns/transport/hosts/hosts.go +++ b/dns/transport/hosts/hosts.go @@ -33,7 +33,11 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt predefined = make(map[string][]netip.Addr) ) if len(options.Path) == 0 { - files = append(files, NewFile(DefaultPath)) + defaultFile, err := NewDefault() + if err != nil { + return nil, err + } + files = append(files, defaultFile) } else { for _, path := range options.Path { files = append(files, NewFile(filemanager.BasePath(ctx, os.ExpandEnv(path)))) diff --git a/dns/transport/hosts/hosts_file.go b/dns/transport/hosts/hosts_file.go index ec384882a..af507f012 100644 --- a/dns/transport/hosts/hosts_file.go +++ b/dns/transport/hosts/hosts_file.go @@ -10,6 +10,8 @@ import ( "sync" "time" + E "github.com/sagernet/sing/common/exceptions" + "github.com/miekg/dns" ) @@ -30,6 +32,14 @@ func NewFile(path string) *File { } } +func NewDefault() (*File, error) { + defaultPathResolved, err := defaultPath() + if err != nil { + return nil, E.Cause(err, "resolve default hosts path") + } + return NewFile(defaultPathResolved), nil +} + func (f *File) Lookup(name string) []netip.Addr { f.access.Lock() defer f.access.Unlock() diff --git a/dns/transport/hosts/hosts_test.go b/dns/transport/hosts/hosts_test.go index 3ae160b78..61d20e0c6 100644 --- a/dns/transport/hosts/hosts_test.go +++ b/dns/transport/hosts/hosts_test.go @@ -1,16 +1,29 @@ -package hosts_test +package hosts import ( "net/netip" + "os" + "runtime" "testing" - "github.com/sagernet/sing-box/dns/transport/hosts" + E "github.com/sagernet/sing/common/exceptions" "github.com/stretchr/testify/require" ) func TestHosts(t *testing.T) { t.Parallel() - require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost")) - require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost")) + require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, NewFile("testdata/hosts").Lookup("localhost")) + if runtime.GOOS != "windows" { + defaultPathResolved, err := defaultPath() + if err != nil { + t.Fatal(E.Cause(err, "resolve default hosts path")) + } + content, readErr := os.ReadFile(defaultPathResolved) + require.NoError(t, readErr) + hFile := NewFile(defaultPathResolved) + if len(hFile.Lookup("localhost")) == 0 { + t.Fatal("failed to resolve localhost: ", defaultPathResolved, ": \n", string(content)) + } + } } diff --git a/dns/transport/hosts/hosts_unix.go b/dns/transport/hosts/hosts_unix.go index 4caed8b40..9c4485319 100644 --- a/dns/transport/hosts/hosts_unix.go +++ b/dns/transport/hosts/hosts_unix.go @@ -2,4 +2,6 @@ package hosts -var DefaultPath = "/etc/hosts" +func defaultPath() (string, error) { + return "/etc/hosts", nil +} diff --git a/dns/transport/hosts/hosts_windows.go b/dns/transport/hosts/hosts_windows.go index 3144e50d5..a17e6d2b8 100644 --- a/dns/transport/hosts/hosts_windows.go +++ b/dns/transport/hosts/hosts_windows.go @@ -2,16 +2,15 @@ package hosts import ( "path/filepath" + "sync" "golang.org/x/sys/windows" ) -var DefaultPath string - -func init() { +var defaultPath = sync.OnceValues(func() (string, error) { systemDirectory, err := windows.GetSystemDirectory() if err != nil { - systemDirectory = "C:\\Windows\\System32" + return "", err } - DefaultPath = filepath.Join(systemDirectory, "Drivers/etc/hosts") -} + return filepath.Join(systemDirectory, "Drivers", "etc", "hosts"), nil +}) diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index a3909acc8..55933510a 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -39,11 +39,11 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt if err != nil { return nil, err } + return &Transport{ TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options), ctx: ctx, logger: logger, - hosts: hosts.NewFile(hosts.DefaultPath), dialer: transportDialer, preferGo: options.PreferGo, }, nil @@ -52,6 +52,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt func (t *Transport) Start(stage adapter.StartStage) error { switch stage { case adapter.StartStateInitialize: + defaultHosts, err := hosts.NewDefault() + if err != nil { + t.logger.Warn(err) + } else { + t.hosts = defaultHosts + } if !t.preferGo { if isSystemdResolvedManaged() { resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger) @@ -84,7 +90,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, return t.resolved.Exchange(ctx, message) } question := message.Question[0] - if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { + if t.hosts != nil && (question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA) { addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name)) if len(addresses) > 0 { return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil diff --git a/dns/transport/local/local_darwin.go b/dns/transport/local/local_darwin.go index eb33d64fa..75fdfd9ab 100644 --- a/dns/transport/local/local_darwin.go +++ b/dns/transport/local/local_darwin.go @@ -51,7 +51,6 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options), ctx: ctx, logger: logger, - hosts: hosts.NewFile(hosts.DefaultPath), dialer: transportDialer, }, nil } @@ -60,6 +59,12 @@ func (t *Transport) Start(stage adapter.StartStage) error { if stage != adapter.StartStateStart { return nil } + defaultHosts, err := hosts.NewDefault() + if err != nil { + t.logger.Warn(err) + } else { + t.hosts = defaultHosts + } inboundManager := service.FromContext[adapter.InboundManager](t.ctx) for _, inbound := range inboundManager.Inbounds() { if inbound.Type() == C.TypeTun { diff --git a/dns/transport/local/local_darwin_cgo.go b/dns/transport/local/local_darwin_cgo.go index 318c38f38..11adf76fb 100644 --- a/dns/transport/local/local_darwin_cgo.go +++ b/dns/transport/local/local_darwin_cgo.go @@ -197,7 +197,7 @@ func darwinResolverHErrno(name string, hErrno int) error { func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { question := message.Question[0] - if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { + if t.hosts != nil && (question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA) { addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name)) if len(addresses) > 0 { return dns.FixedResponse(message.Id, question, addresses, boxC.DefaultDNSTTL), nil