Standardize hosts path

This commit is contained in:
世界 2026-04-15 17:58:54 +08:00
parent b276b955ed
commit 16916cee3a
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
8 changed files with 55 additions and 16 deletions

View file

@ -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))))

View file

@ -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()

View file

@ -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))
}
}
}

View file

@ -2,4 +2,6 @@
package hosts
var DefaultPath = "/etc/hosts"
func defaultPath() (string, error) {
return "/etc/hosts", nil
}

View file

@ -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
})

View file

@ -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

View file

@ -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 {

View file

@ -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