dns: Add neighbor-based hostname resolution to local server

This commit is contained in:
世界
2026-08-30 17:41:40 +08:00
parent 35540b4ff3
commit f8a623e0c6
16 changed files with 290 additions and 71 deletions
+6
View File
@@ -110,6 +110,12 @@ func (r *neighborResolver) LookupMAC(address netip.Addr) (net.HardwareAddr, bool
return nil, false
}
func (r *neighborResolver) LookupAddresses(hostname string) []netip.Addr {
r.access.RLock()
defer r.access.RUnlock()
return lookupAddressesByHostname(hostname, r.ipToHostname, r.macToHostname, r.neighborIPToMAC, r.leaseIPToMAC)
}
func (r *neighborResolver) LookupHostname(address netip.Addr) (string, bool) {
r.access.RLock()
defer r.access.RUnlock()
+56
View File
@@ -0,0 +1,56 @@
package route
import (
"net"
"net/netip"
"strings"
"github.com/sagernet/sing-box/dns"
)
func lookupAddressesByHostname(
hostname string,
ipToHostname map[netip.Addr]string,
macToHostname map[string]string,
ipToMACTables ...map[netip.Addr]net.HardwareAddr,
) []netip.Addr {
hostname = dns.FqdnToDomain(hostname)
if hostname == "" {
return nil
}
resultSet := make(map[netip.Addr]struct{})
var result []netip.Addr
addAddress := func(address netip.Addr) {
if isScopedIPv6Address(address) {
return
}
if _, exists := resultSet[address]; exists {
return
}
resultSet[address] = struct{}{}
result = append(result, address)
}
for address, entryHostname := range ipToHostname {
if strings.EqualFold(entryHostname, hostname) {
addAddress(address)
}
}
for mac, entryHostname := range macToHostname {
if !strings.EqualFold(entryHostname, hostname) {
continue
}
for _, table := range ipToMACTables {
for address, entryMAC := range table {
if entryMAC.String() == mac {
addAddress(address)
}
}
}
}
return result
}
func isScopedIPv6Address(address netip.Addr) bool {
// DNS AAAA records cannot carry an interface zone.
return address.Is6() && (address.IsLinkLocalUnicast() || address.Zone() != "")
}
+6
View File
@@ -114,6 +114,12 @@ func (r *neighborResolver) LookupMAC(address netip.Addr) (net.HardwareAddr, bool
return nil, false
}
func (r *neighborResolver) LookupAddresses(hostname string) []netip.Addr {
r.access.RLock()
defer r.access.RUnlock()
return lookupAddressesByHostname(hostname, r.ipToHostname, r.macToHostname, r.neighborIPToMAC, r.leaseIPToMAC)
}
func (r *neighborResolver) LookupHostname(address netip.Addr) (string, bool) {
r.access.RLock()
defer r.access.RUnlock()
+6
View File
@@ -46,6 +46,12 @@ func (r *platformNeighborResolver) LookupMAC(address netip.Addr) (net.HardwareAd
return extractMACFromEUI64(address)
}
func (r *platformNeighborResolver) LookupAddresses(hostname string) []netip.Addr {
r.access.RLock()
defer r.access.RUnlock()
return lookupAddressesByHostname(hostname, r.ipToHostname, r.macToHostname, r.ipToMAC)
}
func (r *platformNeighborResolver) LookupHostname(address netip.Addr) (string, bool) {
r.access.RLock()
defer r.access.RUnlock()
+31 -32
View File
@@ -63,7 +63,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
rules: make([]adapter.Rule, 0, len(options.Rules)),
ruleSetMap: make(map[string]adapter.RuleSet),
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
needFindNeighbor: hasRule(options.Rules, isNeighborRule) || hasDNSRule(dnsOptions.Rules, isNeighborDNSRule) || options.FindNeighbor,
needFindNeighbor: hasRule(options.Rules, isNeighborRule) || hasDNSRule(dnsOptions.Rules, isNeighborDNSRule) || hasLocalNeighborDNSServer(dnsOptions.Servers) || options.FindNeighbor,
leaseFiles: options.DHCPLeaseFiles,
pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
@@ -99,6 +99,36 @@ func (r *Router) Initialize(rules []option.Rule, ruleSets []option.RuleSet) erro
func (r *Router) Start(stage adapter.StartStage) error {
monitor := taskmonitor.New(r.logger, C.StartTimeout)
switch stage {
case adapter.StartStateInitialize:
if r.needFindNeighbor {
if r.platformInterface != nil && r.platformInterface.UsePlatformNeighborResolver() {
monitor.Start("initialize neighbor resolver")
resolver := newPlatformNeighborResolver(r.logger, r.platformInterface)
err := resolver.Start()
monitor.Finish()
if err != nil {
r.logger.Error(E.Cause(err, "start neighbor resolver"))
} else {
r.neighborResolver = resolver
}
} else {
monitor.Start("initialize neighbor resolver")
resolver, err := newNeighborResolver(r.logger, r.leaseFiles)
monitor.Finish()
if err != nil {
if err != os.ErrInvalid {
r.logger.Error(E.Cause(err, "create neighbor resolver"))
}
} else {
err = resolver.Start()
if err != nil {
r.logger.Error(E.Cause(err, "start neighbor resolver"))
} else {
r.neighborResolver = resolver
}
}
}
}
case adapter.StartStateStart:
var startContext *adapter.HTTPStartContext
if len(r.ruleSets) > 0 {
@@ -128,7 +158,6 @@ func (r *Router) Start(stage adapter.StartStage) error {
}
r.network.Initialize(r.ruleSets)
needFindProcess := r.needFindProcess
needFindNeighbor := r.needFindNeighbor
for _, ruleSet := range r.ruleSets {
metadata := ruleSet.Metadata()
if metadata.ContainsProcessRule {
@@ -163,36 +192,6 @@ func (r *Router) Start(stage adapter.StartStage) error {
processCache.SetLifetime(200 * time.Millisecond)
r.processCache = processCache
}
r.needFindNeighbor = needFindNeighbor
if needFindNeighbor {
if r.platformInterface != nil && r.platformInterface.UsePlatformNeighborResolver() {
monitor.Start("initialize neighbor resolver")
resolver := newPlatformNeighborResolver(r.logger, r.platformInterface)
err := resolver.Start()
monitor.Finish()
if err != nil {
r.logger.Error(E.Cause(err, "start neighbor resolver"))
} else {
r.neighborResolver = resolver
}
} else {
monitor.Start("initialize neighbor resolver")
resolver, err := newNeighborResolver(r.logger, r.leaseFiles)
monitor.Finish()
if err != nil {
if err != os.ErrInvalid {
r.logger.Error(E.Cause(err, "create neighbor resolver"))
}
} else {
err = resolver.Start()
if err != nil {
r.logger.Error(E.Cause(err, "start neighbor resolver"))
} else {
r.neighborResolver = resolver
}
}
}
}
case adapter.StartStatePostStart:
for i, rule := range r.rules {
monitor.Start("initialize rule[", i, "]")
+16
View File
@@ -60,3 +60,19 @@ func isWIFIRule(rule option.DefaultRule) bool {
func isWIFIDNSRule(rule option.DefaultDNSRule) bool {
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
}
func hasLocalNeighborDNSServer(servers []option.DNSServerOptions) bool {
for _, server := range servers {
if server.Type != C.DNSTypeLocal {
continue
}
localOptions, isLocal := server.Options.(*option.LocalDNSServerOptions)
if !isLocal {
continue
}
if len(localOptions.NeighborDomain) > 0 {
return true
}
}
return false
}