diff --git a/cmd/cli/ad_others.go b/cmd/cli/ad_others.go index 6a7417f..b23476f 100644 --- a/cmd/cli/ad_others.go +++ b/cmd/cli/ad_others.go @@ -8,3 +8,8 @@ import ( // addExtraSplitDnsRule adds split DNS rule if present. func addExtraSplitDnsRule(_ *ctrld.Config) bool { return false } + +// getActiveDirectoryDomain returns AD domain name of this computer. +func getActiveDirectoryDomain() (string, error) { + return "", nil +} diff --git a/cmd/cli/dns_proxy.go b/cmd/cli/dns_proxy.go index 341a830..5396642 100644 --- a/cmd/cli/dns_proxy.go +++ b/cmd/cli/dns_proxy.go @@ -51,6 +51,12 @@ var privateUpstreamConfig = &ctrld.UpstreamConfig{ Timeout: 2000, } +var localUpstreamConfig = &ctrld.UpstreamConfig{ + Name: "Local resolver", + Type: ctrld.ResolverTypeLocal, + Timeout: 2000, +} + // proxyRequest contains data for proxying a DNS query to upstream. type proxyRequest struct { msg *dns.Msg @@ -443,6 +449,11 @@ func (p *prog) proxy(ctx context.Context, req *proxyRequest) *proxyResponse { upstreams = []string{upstreamOS} } + if p.isAdDomainQuery(req.msg) { + upstreamConfigs = []*ctrld.UpstreamConfig{localUpstreamConfig} + upstreams = []string{upstreamOS} + } + res := &proxyResponse{} // LAN/PTR lookup flow: @@ -651,6 +662,14 @@ func (p *prog) upstreamConfigsFromUpstreamNumbers(upstreams []string) []*ctrld.U return upstreamConfigs } +func (p *prog) isAdDomainQuery(msg *dns.Msg) bool { + if p.adDomain == "" { + return false + } + cDomainName := canonicalName(msg.Question[0].Name) + return dns.IsSubDomain(p.adDomain, cDomainName) +} + // canonicalName returns canonical name from FQDN with "." trimmed. func canonicalName(fqdn string) string { q := strings.TrimSpace(fqdn) diff --git a/cmd/cli/prog.go b/cmd/cli/prog.go index a68dad2..46d4d18 100644 --- a/cmd/cli/prog.go +++ b/cmd/cli/prog.go @@ -106,6 +106,7 @@ type prog struct { internalLogSent time.Time runningIface string requiredMultiNICsConfig bool + adDomain string selfUninstallMu sync.Mutex refusedQueryCount int @@ -441,6 +442,10 @@ func (p *prog) run(reload bool, reloadCh chan struct{}) { } } } + if domain, err := getActiveDirectoryDomain(); err == nil && domain != "" && hasLocalDnsServerRunning() { + mainLog.Load().Debug().Msgf("active directory domain: %s", domain) + p.adDomain = domain + } var wg sync.WaitGroup wg.Add(len(p.cfg.Listener)) diff --git a/config.go b/config.go index c88404c..099f75b 100644 --- a/config.go +++ b/config.go @@ -384,7 +384,7 @@ func (uc *UpstreamConfig) IsDiscoverable() bool { return *uc.Discoverable } switch uc.Type { - case ResolverTypeOS, ResolverTypeLegacy, ResolverTypePrivate: + case ResolverTypeOS, ResolverTypeLegacy, ResolverTypePrivate, ResolverTypeLocal: if ip, err := netip.ParseAddr(uc.Domain); err == nil { return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || tsaddr.CGNATRange().Contains(ip) } diff --git a/resolver.go b/resolver.go index 0097fe0..7dc76b0 100644 --- a/resolver.go +++ b/resolver.go @@ -30,8 +30,10 @@ const ( ResolverTypeOS = "os" // ResolverTypeLegacy specifies legacy resolver. ResolverTypeLegacy = "legacy" - // ResolverTypePrivate is like ResolverTypeOS, but use for local resolver only. + // ResolverTypePrivate is like ResolverTypeOS, but use for private resolver only. ResolverTypePrivate = "private" + // ResolverTypeLocal is like ResolverTypeOS, but use for local resolver only. + ResolverTypeLocal = "local" // ResolverTypeSDNS specifies resolver with information encoded using DNS Stamps. // See: https://dnscrypt.info/stamps-specifications/ ResolverTypeSDNS = "sdns" @@ -47,6 +49,16 @@ var controldPublicDnsWithPort = net.JoinHostPort(controldPublicDns, "53") // or is the Resolver used for ResolverTypeOS. var or = newResolverWithNameserver(defaultNameservers()) +var localResolver = newLocalResolver() + +func newLocalResolver() Resolver { + var nss []string + for _, addr := range Rfc1918Addresses() { + nss = append(nss, net.JoinHostPort(addr, "53")) + } + return NewResolverWithNameserver(nss) +} + // LanQueryCtxKey is the context.Context key to indicate that the request is for LAN network. type LanQueryCtxKey struct{} @@ -89,7 +101,8 @@ func availableNameservers() []string { // It's the caller's responsibility to ensure the system DNS is in a clean state before // calling this function. func InitializeOsResolver() []string { - return initializeOsResolver(availableNameservers()) + ns := initializeOsResolver(availableNameservers()) + return ns } // initializeOsResolver performs logic for choosing OS resolver nameserver. @@ -301,6 +314,8 @@ func NewResolver(uc *UpstreamConfig) (Resolver, error) { return &legacyResolver{uc: uc}, nil case ResolverTypePrivate: return NewPrivateResolver(), nil + case ResolverTypeLocal: + return localResolver, nil } return nil, fmt.Errorf("%w: %s", errUnknownResolver, typ) }