all: eliminate usage of global ProxyLogger

So setting up logging for ctrld binary and ctrld packages could be done
more easily, decouple the required setup for interactive vs daemon
running.

This is the first step toward replacing rs/zerolog libary with a
different logging library.
This commit is contained in:
Cuong Manh Le
2025-04-03 21:17:02 +07:00
committed by Cuong Manh Le
parent 47c04bf0f6
commit 0e66697247
39 changed files with 425 additions and 420 deletions
+58 -77
View File
@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"io"
"net"
"net/netip"
"runtime"
@@ -15,7 +14,6 @@ import (
"time"
"github.com/miekg/dns"
"github.com/rs/zerolog"
"golang.org/x/sync/singleflight"
"tailscale.com/net/netmon"
"tailscale.com/net/tsaddr"
@@ -50,10 +48,6 @@ var controldPublicDnsWithPort = net.JoinHostPort(controldPublicDns, "53")
var localResolver Resolver
func init() {
// Initializing ProxyLogger here, so other places don't have to do nil check.
l := zerolog.New(io.Discard)
ProxyLogger.Store(&l)
localResolver = newLocalResolver()
}
@@ -81,8 +75,8 @@ func LanQueryCtx(ctx context.Context) context.Context {
}
// defaultNameservers is like nameservers with each element formed "ip:53".
func defaultNameservers() []string {
ns := nameservers()
func defaultNameservers(ctx context.Context) []string {
ns := nameservers(ctx)
nss := make([]string, len(ns))
for i := range ns {
nss[i] = net.JoinHostPort(ns[i], "53")
@@ -91,42 +85,36 @@ func defaultNameservers() []string {
}
// availableNameservers returns list of current available DNS servers of the system.
func availableNameservers() []string {
func availableNameservers(ctx context.Context) []string {
var nss []string
// Ignore local addresses to prevent loop.
regularIPs, loopbackIPs, _ := netmon.LocalAddresses()
machineIPsMap := make(map[string]struct{}, len(regularIPs))
//load the logger
logger := *ProxyLogger.Load()
Log(context.Background(), logger.Debug(),
"Got local addresses - regular IPs: %v, loopback IPs: %v", regularIPs, loopbackIPs)
// Load the logger.
logger := LoggerFromCtx(ctx)
logger.Debug().Msgf("Got local addresses - regular IPs: %v, loopback IPs: %v", regularIPs, loopbackIPs)
for _, v := range slices.Concat(regularIPs, loopbackIPs) {
ipStr := v.String()
machineIPsMap[ipStr] = struct{}{}
Log(context.Background(), logger.Debug(),
"Added local IP to OS resolverexclusion map: %s", ipStr)
logger.Debug().Msgf("Added local IP to OS resolverexclusion map: %s", ipStr)
}
systemNameservers := nameservers()
Log(context.Background(), logger.Debug(),
"Got system nameservers: %v", systemNameservers)
systemNameservers := nameservers(ctx)
logger.Debug().Msgf("Got system nameservers: %v", systemNameservers)
for _, ns := range systemNameservers {
if _, ok := machineIPsMap[ns]; ok {
Log(context.Background(), logger.Debug(),
"Skipping local nameserver: %s", ns)
logger.Debug().Msgf("Skipping local nameserver: %s", ns)
continue
}
nss = append(nss, ns)
Log(context.Background(), logger.Debug(),
"Added non-local nameserver: %s", ns)
logger.Debug().Msgf("Added non-local nameserver: %s", ns)
}
Log(context.Background(), logger.Debug(),
"Final available nameservers: %v", nss)
logger.Debug().Msgf("Final available nameservers: %v", nss)
return nss
}
@@ -135,8 +123,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(guardAgainstNoNameservers bool) []string {
nameservers := availableNameservers()
func InitializeOsResolver(ctx context.Context, guardAgainstNoNameservers bool) []string {
nameservers := availableNameservers(ctx)
// if no nameservers, return empty slice so we dont remove all nameservers
if len(nameservers) == 0 && guardAgainstNoNameservers {
return []string{}
@@ -188,7 +176,7 @@ type Resolver interface {
var errUnknownResolver = errors.New("unknown resolver")
// NewResolver creates a Resolver based on the given upstream config.
func NewResolver(uc *UpstreamConfig) (Resolver, error) {
func NewResolver(ctx context.Context, uc *UpstreamConfig) (Resolver, error) {
typ := uc.Type
switch typ {
case ResolverTypeDOH, ResolverTypeDOH3:
@@ -200,15 +188,16 @@ func NewResolver(uc *UpstreamConfig) (Resolver, error) {
case ResolverTypeOS:
resolverMutex.Lock()
if or == nil {
ProxyLogger.Load().Debug().Msgf("Initialize new OS resolver")
or = newResolverWithNameserver(defaultNameservers())
logger := LoggerFromCtx(ctx)
logger.Debug().Msgf("Initialize new OS resolver")
or = newResolverWithNameserver(defaultNameservers(ctx))
}
resolverMutex.Unlock()
return or, nil
case ResolverTypeLegacy:
return &legacyResolver{uc: uc}, nil
case ResolverTypePrivate:
return NewPrivateResolver(), nil
return NewPrivateResolver(ctx), nil
case ResolverTypeLocal:
return localResolver, nil
}
@@ -235,14 +224,16 @@ type publicResponse struct {
}
// SetDefaultLocalIPv4 updates the stored local IPv4.
func SetDefaultLocalIPv4(ip net.IP) {
Log(context.Background(), ProxyLogger.Load().Debug(), "SetDefaultLocalIPv4: %s", ip)
func SetDefaultLocalIPv4(ctx context.Context, ip net.IP) {
logger := LoggerFromCtx(ctx)
logger.Debug().Msgf("SetDefaultLocalIPv4: %s", ip)
defaultLocalIPv4.Store(ip)
}
// SetDefaultLocalIPv6 updates the stored local IPv6.
func SetDefaultLocalIPv6(ip net.IP) {
Log(context.Background(), ProxyLogger.Load().Debug(), "SetDefaultLocalIPv6: %s", ip)
func SetDefaultLocalIPv6(ctx context.Context, ip net.IP) {
logger := LoggerFromCtx(ctx)
logger.Debug().Msgf("SetDefaultLocalIPv6: %s", ip)
defaultLocalIPv6.Store(ip)
}
@@ -300,10 +291,11 @@ func (o *osResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
// Unique key for the singleflight group.
key := fmt.Sprintf("%s:%d:", domain, qtype)
logger := LoggerFromCtx(ctx)
// Checking the cache first.
if val, ok := o.cache.Load(key); ok {
if val, ok := val.(*dns.Msg); ok {
Log(ctx, ProxyLogger.Load().Debug(), "hit hot cached result: %s - %s", domain, dns.TypeToString[qtype])
Log(ctx, logger.Debug(), "hit hot cached result: %s - %s", domain, dns.TypeToString[qtype])
res := val.Copy()
SetCacheReply(res, msg, val.Rcode)
return res, nil
@@ -338,7 +330,7 @@ func (o *osResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
res := sharedMsg.Copy()
SetCacheReply(res, msg, sharedMsg.Rcode)
if shared {
Log(ctx, ProxyLogger.Load().Debug(), "shared result: %s - %s", domain, dns.TypeToString[qtype])
Log(ctx, logger.Debug(), "shared result: %s - %s", domain, dns.TypeToString[qtype])
}
return res, nil
@@ -368,7 +360,8 @@ func (o *osResolver) resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
if msg != nil && len(msg.Question) > 0 {
question = msg.Question[0].Name
}
Log(ctx, ProxyLogger.Load().Debug(), "os resolver query for %s with nameservers: %v public: %v", question, nss, publicServers)
logger := LoggerFromCtx(ctx)
Log(ctx, logger.Debug(), "os resolver query for %s with nameservers: %v public: %v", question, nss, publicServers)
// New check: If no resolvers are available, return an error.
if numServers == 0 {
@@ -417,7 +410,7 @@ func (o *osResolver) resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
// If splitting fails, fallback to the original server string
host = server
}
Log(ctx, ProxyLogger.Load().Debug(), "got answer from nameserver: %s", host)
Log(ctx, logger.Debug(), "got answer from nameserver: %s", host)
}
// try local nameservers
@@ -444,7 +437,7 @@ func (o *osResolver) resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
switch {
case res.lan:
// Always prefer LAN responses immediately
Log(ctx, ProxyLogger.Load().Debug(), "using LAN answer from: %s", res.server)
Log(ctx, logger.Debug(), "using LAN answer from: %s", res.server)
cancel()
logAnswer(res.server)
return res.answer, nil
@@ -454,7 +447,7 @@ func (o *osResolver) resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
// if there are no LAN nameservers, we should not wait
// just use the first response
if len(nss) == 0 {
Log(ctx, ProxyLogger.Load().Debug(), "using public answer from: %s", res.server)
Log(ctx, logger.Debug(), "using public answer from: %s", res.server)
cancel()
logAnswer(res.server)
return res.answer, nil
@@ -465,12 +458,12 @@ func (o *osResolver) resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
})
}
case res.answer != nil:
Log(ctx, ProxyLogger.Load().Debug(), "got non-success answer from: %s with code: %d",
Log(ctx, logger.Debug(), "got non-success answer from: %s with code: %d",
res.server, res.answer.Rcode)
// When there are no LAN nameservers, we should not wait
// for other nameservers to respond.
if len(nss) == 0 {
Log(ctx, ProxyLogger.Load().Debug(), "no lan nameservers using public non success answer")
Log(ctx, logger.Debug(), "no lan nameservers using public non success answer")
cancel()
logAnswer(res.server)
return res.answer, nil
@@ -483,17 +476,17 @@ func (o *osResolver) resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error
if len(publicResponses) > 0 {
resp := publicResponses[0]
Log(ctx, ProxyLogger.Load().Debug(), "using public answer from: %s", resp.server)
Log(ctx, logger.Debug(), "using public answer from: %s", resp.server)
logAnswer(resp.server)
return resp.answer, nil
}
if controldSuccessAnswer != nil {
Log(ctx, ProxyLogger.Load().Debug(), "using ControlD answer from: %s", controldPublicDnsWithPort)
Log(ctx, logger.Debug(), "using ControlD answer from: %s", controldPublicDnsWithPort)
logAnswer(controldPublicDnsWithPort)
return controldSuccessAnswer, nil
}
if nonSuccessAnswer != nil {
Log(ctx, ProxyLogger.Load().Debug(), "using non-success answer from: %s", nonSuccessServer)
Log(ctx, logger.Debug(), "using non-success answer from: %s", nonSuccessServer)
logAnswer(nonSuccessServer)
return nonSuccessAnswer, nil
}
@@ -515,7 +508,7 @@ func (r *legacyResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, e
if msg != nil && len(msg.Question) > 0 {
dnsTyp = msg.Question[0].Qtype
}
_, udpNet := r.uc.netForDNSType(dnsTyp)
_, udpNet := r.uc.netForDNSType(ctx, dnsTyp)
dnsClient := &dns.Client{
Net: udpNet,
Dialer: dialer,
@@ -541,39 +534,43 @@ func (d dummyResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, err
// LookupIP looks up domain using current system nameservers settings.
// It returns a slice of that host's IPv4 and IPv6 addresses.
func LookupIP(domain string) []string {
nss := initDefaultOsResolver()
return lookupIP(domain, -1, nss)
func LookupIP(ctx context.Context, domain string) []string {
nss := initDefaultOsResolver(ctx)
return lookupIP(ctx, domain, -1, nss)
}
// initDefaultOsResolver initializes the default OS resolver with system's default nameservers if it hasn't been initialized yet.
// It returns the combined list of LAN and public nameservers currently held by the resolver.
func initDefaultOsResolver() []string {
func initDefaultOsResolver(ctx context.Context) []string {
logger := LoggerFromCtx(ctx)
resolverMutex.Lock()
defer resolverMutex.Unlock()
if or == nil {
ProxyLogger.Load().Debug().Msgf("Initialize new OS resolver with default nameservers")
or = newResolverWithNameserver(defaultNameservers())
logger.Debug().Msgf("Initialize new OS resolver with default nameservers")
or = newResolverWithNameserver(defaultNameservers(ctx))
}
nss := *or.lanServers.Load()
nss = append(nss, *or.publicServers.Load()...)
return nss
}
// lookupIP looks up domain with given timeout and bootstrapDNS.
// If the timeout is negative, default timeout 2000 ms will be used.
// It returns nil if bootstrapDNS is nil or empty.
func lookupIP(domain string, timeout int, bootstrapDNS []string) (ips []string) {
func lookupIP(ctx context.Context, domain string, timeout int, bootstrapDNS []string) (ips []string) {
if net.ParseIP(domain) != nil {
return []string{domain}
}
logger := LoggerFromCtx(ctx)
if bootstrapDNS == nil {
ProxyLogger.Load().Debug().Msgf("empty bootstrap DNS")
logger.Debug().Msgf("empty bootstrap DNS")
return nil
}
resolver := newResolverWithNameserver(bootstrapDNS)
ProxyLogger.Load().Debug().Msgf("resolving %q using bootstrap DNS %q", domain, bootstrapDNS)
logger.Debug().Msgf("resolving %q using bootstrap DNS %q", domain, bootstrapDNS)
timeoutMs := 2000
if timeout > 0 && timeout < timeoutMs {
timeoutMs = timeout
@@ -616,15 +613,15 @@ func lookupIP(domain string, timeout int, bootstrapDNS []string) (ips []string)
r, err := resolver.Resolve(ctx, m)
if err != nil {
ProxyLogger.Load().Error().Err(err).Msgf("could not lookup %q record for domain %q", dns.TypeToString[dnsType], domain)
logger.Error().Err(err).Msgf("could not lookup %q record for domain %q", dns.TypeToString[dnsType], domain)
return
}
if r.Rcode != dns.RcodeSuccess {
ProxyLogger.Load().Error().Msgf("could not resolve domain %q, return code: %s", domain, dns.RcodeToString[r.Rcode])
logger.Error().Msgf("could not resolve domain %q, return code: %s", domain, dns.RcodeToString[r.Rcode])
return
}
if len(r.Answer) == 0 {
ProxyLogger.Load().Error().Msg("no answer from OS resolver")
logger.Error().Msg("no answer from OS resolver")
return
}
target := targetDomain(r.Answer)
@@ -641,22 +638,6 @@ func lookupIP(domain string, timeout int, bootstrapDNS []string) (ips []string)
return ips
}
// NewBootstrapResolver returns an OS resolver, which use following nameservers:
//
// - Gateway IP address (depends on OS).
// - Input servers.
func NewBootstrapResolver(servers ...string) Resolver {
logger := *ProxyLogger.Load()
Log(context.Background(), logger.Debug(), "NewBootstrapResolver called with servers: %v", servers)
nss := defaultNameservers()
nss = append([]string{controldPublicDnsWithPort}, nss...)
for _, ns := range servers {
nss = append([]string{net.JoinHostPort(ns, "53")}, nss...)
}
return NewResolverWithNameserver(nss)
}
// NewPrivateResolver returns an OS resolver, which includes only private DNS servers,
// excluding:
//
@@ -664,8 +645,8 @@ func NewBootstrapResolver(servers ...string) Resolver {
// - Nameservers which is local RFC1918 addresses.
//
// This is useful for doing PTR lookup in LAN network.
func NewPrivateResolver() Resolver {
nss := initDefaultOsResolver()
func NewPrivateResolver(ctx context.Context) Resolver {
nss := initDefaultOsResolver(ctx)
resolveConfNss := currentNameserversFromResolvconf()
localRfc1918Addrs := Rfc1918Addresses()
n := 0