dns: Add neighbor-based hostname resolution to local server
This commit is contained in:
@@ -14,6 +14,7 @@ type NeighborEntry struct {
|
||||
type NeighborResolver interface {
|
||||
LookupMAC(address netip.Addr) (net.HardwareAddr, bool)
|
||||
LookupHostname(address netip.Addr) (string, bool)
|
||||
LookupAddresses(hostname string) []netip.Addr
|
||||
Start() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
@@ -26,12 +27,14 @@ var _ adapter.DNSTransport = (*Transport)(nil)
|
||||
|
||||
type Transport struct {
|
||||
dns.TransportAdapter
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
hosts *hosts.File
|
||||
dialer N.Dialer
|
||||
preferGo bool
|
||||
resolved ResolvedResolver
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
hosts *hosts.File
|
||||
dialer N.Dialer
|
||||
preferGo bool
|
||||
resolved ResolvedResolver
|
||||
neighborResolver adapter.NeighborResolver
|
||||
neighborSuffixes []string
|
||||
}
|
||||
|
||||
func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@@ -39,13 +42,17 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
suffixes, err := buildNeighborMatchers(options.NeighborDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
dialer: transportDialer,
|
||||
preferGo: options.PreferGo,
|
||||
neighborSuffixes: suffixes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -71,6 +78,11 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
case adapter.StartStateStart:
|
||||
router := service.FromContext[adapter.Router](t.ctx)
|
||||
if router != nil {
|
||||
t.neighborResolver = router.NeighborResolver()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -87,6 +99,10 @@ func (t *Transport) Reset() {
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if t.resolved != nil {
|
||||
response := t.lookupNeighbor(message)
|
||||
if response != nil {
|
||||
return response, nil
|
||||
}
|
||||
return t.resolved.Exchange(ctx, message)
|
||||
}
|
||||
question := message.Question[0]
|
||||
@@ -96,5 +112,9 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||
}
|
||||
}
|
||||
response := t.lookupNeighbor(message)
|
||||
if response != nil {
|
||||
return response, nil
|
||||
}
|
||||
return t.exchange(ctx, message, question.Name)
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@ var _ adapter.DNSTransport = (*Transport)(nil)
|
||||
|
||||
type Transport struct {
|
||||
dns.TransportAdapter
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
hosts *hosts.File
|
||||
dialer N.Dialer
|
||||
fallback bool
|
||||
dhcpTransport dhcpTransport
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
hosts *hosts.File
|
||||
dialer N.Dialer
|
||||
fallback bool
|
||||
dhcpTransport dhcpTransport
|
||||
neighborResolver adapter.NeighborResolver
|
||||
neighborSuffixes []string
|
||||
}
|
||||
|
||||
type dhcpTransport interface {
|
||||
@@ -47,39 +49,48 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
suffixes, err := buildNeighborMatchers(options.NeighborDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
dialer: transportDialer,
|
||||
neighborSuffixes: suffixes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
t.fallback = true
|
||||
break
|
||||
switch stage {
|
||||
case adapter.StartStateStart:
|
||||
defaultHosts, err := hosts.NewDefault()
|
||||
if err != nil {
|
||||
t.logger.Warn(err)
|
||||
} else {
|
||||
t.hosts = defaultHosts
|
||||
}
|
||||
}
|
||||
if t.fallback {
|
||||
t.dhcpTransport = newDHCPTransport(t.TransportAdapter, log.ContextWithOverrideLevel(t.ctx, log.LevelDebug), t.dialer, t.logger)
|
||||
if t.dhcpTransport != nil {
|
||||
err := t.dhcpTransport.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
inboundManager := service.FromContext[adapter.InboundManager](t.ctx)
|
||||
for _, inbound := range inboundManager.Inbounds() {
|
||||
if inbound.Type() == C.TypeTun {
|
||||
t.fallback = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if t.fallback {
|
||||
t.dhcpTransport = newDHCPTransport(t.TransportAdapter, log.ContextWithOverrideLevel(t.ctx, log.LevelDebug), t.dialer, t.logger)
|
||||
if t.dhcpTransport != nil {
|
||||
err = t.dhcpTransport.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
router := service.FromContext[adapter.Router](t.ctx)
|
||||
if router != nil {
|
||||
t.neighborResolver = router.NeighborResolver()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -86,7 +86,11 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
return dns.FixedResponse(message.Id, question, addresses, boxC.DefaultDNSTTL), nil
|
||||
}
|
||||
}
|
||||
if t.fallback && t.dhcpTransport != nil {
|
||||
response := t.lookupNeighbor(message)
|
||||
if response != nil {
|
||||
return response, nil
|
||||
}
|
||||
if t.dhcpTransport != nil {
|
||||
dhcpServers := t.dhcpTransport.Fetch()
|
||||
if len(dhcpServers) > 0 {
|
||||
return t.dhcpTransport.Exchange0(ctx, message, dhcpServers)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func buildNeighborMatchers(domains []string) ([]string, error) {
|
||||
if len(domains) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var suffixes []string
|
||||
for _, domain := range domains {
|
||||
if !strings.HasPrefix(domain, ".") {
|
||||
return nil, E.New("neighbor_domain entry must start with '.': ", domain)
|
||||
}
|
||||
suffixes = append(suffixes, mDNS.CanonicalName(domain))
|
||||
}
|
||||
return suffixes, nil
|
||||
}
|
||||
|
||||
func (t *Transport) lookupNeighbor(message *mDNS.Msg) *mDNS.Msg {
|
||||
if t.neighborResolver == nil {
|
||||
return nil
|
||||
}
|
||||
question := message.Question[0]
|
||||
if question.Qtype != mDNS.TypeA && question.Qtype != mDNS.TypeAAAA {
|
||||
return nil
|
||||
}
|
||||
host := extractNeighborHost(mDNS.CanonicalName(question.Name), t.neighborSuffixes)
|
||||
if host == "" {
|
||||
return nil
|
||||
}
|
||||
addresses := t.neighborResolver.LookupAddresses(host)
|
||||
if len(addresses) == 0 {
|
||||
return nil
|
||||
}
|
||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL)
|
||||
}
|
||||
|
||||
func extractNeighborHost(canonical string, suffixes []string) string {
|
||||
for _, suffix := range suffixes {
|
||||
if !strings.HasSuffix(canonical, suffix) || len(canonical) <= len(suffix) {
|
||||
continue
|
||||
}
|
||||
host := canonical[:len(canonical)-len(suffix)]
|
||||
if !strings.ContainsRune(host, '.') {
|
||||
return host
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.14.0"
|
||||
|
||||
:material-plus: [neighbor_domain](#neighbor_domain)
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [prefer_go](#prefer_go)
|
||||
@@ -19,7 +23,8 @@ icon: material/new-box
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "",
|
||||
"prefer_go": false
|
||||
"prefer_go": false,
|
||||
"neighbor_domain": []
|
||||
|
||||
// Dial Fields
|
||||
}
|
||||
@@ -56,6 +61,19 @@ On devices running Android versions lower than 10, this interface can only resol
|
||||
2. On macOS, `local` will try DHCP first in Network Extension, since DHCP respects DIal Fields,
|
||||
it will not be disabled by `prefer_go`.
|
||||
|
||||
#### neighbor_domain
|
||||
|
||||
!!! question "Since sing-box 1.14.0"
|
||||
|
||||
A list of domain suffixes for which A/AAAA queries are answered from the
|
||||
[neighbor resolver](/configuration/shared/neighbor/) instead of the upstream.
|
||||
|
||||
Each entry must start with `.`. Only queries whose host part (the portion
|
||||
before the suffix) contains no dots are matched; `.` matches any
|
||||
single-label name such as `nas`.
|
||||
|
||||
Example: `[".", ".lan"]`.
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/) for details.
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.14.0 中的更改"
|
||||
|
||||
:material-plus: [neighbor_domain](#neighbor_domain)
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [prefer_go](#prefer_go)
|
||||
@@ -20,6 +24,7 @@ icon: material/new-box
|
||||
"type": "local",
|
||||
"tag": "",
|
||||
"prefer_go": false,
|
||||
"neighbor_domain": [],
|
||||
|
||||
// 拨号字段
|
||||
}
|
||||
@@ -56,6 +61,17 @@ icon: material/new-box
|
||||
2. 在 macOS 上,`local` 会在 Network Extension 中首先尝试 DHCP,由于 DHCP 遵循拨号字段,
|
||||
它不会被 `prefer_go` 禁用。
|
||||
|
||||
#### neighbor_domain
|
||||
|
||||
!!! question "自 sing-box 1.14.0 起"
|
||||
|
||||
用于从[邻居解析器](/zh/configuration/shared/neighbor/)而非上游回答 A/AAAA 查询的域后缀列表。
|
||||
|
||||
每一项必须以 `.` 开头。仅匹配后缀之前的主机名部分不包含点的查询;
|
||||
`.` 匹配任意单标签名称,例如 `nas`。
|
||||
|
||||
示例:`[".", ".lan"]`。
|
||||
|
||||
### 拨号字段
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||
@@ -8,7 +8,9 @@ Match LAN devices by MAC address and hostname using
|
||||
[`source_mac_address`](/configuration/route/rule/#source_mac_address) and
|
||||
[`source_hostname`](/configuration/route/rule/#source_hostname) rule items.
|
||||
|
||||
Neighbor resolution is automatically enabled when these rule items exist.
|
||||
Neighbor resolution is automatically enabled when these rule items exist
|
||||
or when a [local DNS server](/configuration/dns/server/local/) sets
|
||||
[neighbor_domain](/configuration/dns/server/local/#neighbor_domain).
|
||||
Use [`route.find_neighbor`](/configuration/route/#find_neighbor) to force enable it for logging without rules.
|
||||
|
||||
## Linux
|
||||
|
||||
@@ -8,7 +8,7 @@ icon: material/lan
|
||||
[`source_mac_address`](/configuration/route/rule/#source_mac_address) 和
|
||||
[`source_hostname`](/configuration/route/rule/#source_hostname) 规则项匹配局域网设备的 MAC 地址和主机名。
|
||||
|
||||
当这些规则项存在时,邻居解析自动启用。
|
||||
当这些规则项存在,或 [local DNS 服务器](/zh/configuration/dns/server/local/) 设置了 [neighbor_domain](/zh/configuration/dns/server/local/#neighbor_domain) 时,邻居解析自动启用。
|
||||
使用 [`route.find_neighbor`](/configuration/route/#find_neighbor) 可在没有规则时强制启用以输出日志。
|
||||
|
||||
## Linux
|
||||
|
||||
+2
-1
@@ -154,7 +154,8 @@ type RawLocalDNSServerOptions struct {
|
||||
|
||||
type LocalDNSServerOptions struct {
|
||||
RawLocalDNSServerOptions
|
||||
PreferGo bool `json:"prefer_go,omitempty"`
|
||||
PreferGo bool `json:"prefer_go,omitempty"`
|
||||
NeighborDomain badoption.Listable[string] `json:"neighbor_domain,omitempty"`
|
||||
}
|
||||
|
||||
type RemoteDNSServerOptions struct {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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() != "")
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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, "]")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user