Files
AdGuardHome/internal/home/clientaddr.go
2023-07-14 17:39:42 +03:00

146 lines
3.9 KiB
Go

package home
import (
"context"
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/log"
)
// TODO(a.garipov): It is currently hard to add tests for this structure due to
// strong coupling between it and Context.dnsServer with Context.clients.
// Resolve this coupling and add proper testing.
// clientAddrProcessor processes incoming client addresses with rDNS and WHOIS,
// if configured.
type clientAddrProcessor struct {
rdns rdns.Interface
whois whois.Interface
}
const (
// defaultQueueSize is the size of queue of IPs for rDNS and WHOIS
// processing.
defaultQueueSize = 255
// defaultCacheSize is the maximum size of the cache for rDNS and WHOIS
// processing. It must be greater than zero.
defaultCacheSize = 10_000
// defaultIPTTL is the Time to Live duration for IP addresses cached by
// rDNS and WHOIS.
defaultIPTTL = 1 * time.Hour
)
// newClientAddrProcessor returns a new client address processor. c must not be
// nil.
func newClientAddrProcessor(c *clientSourcesConfig) (p *clientAddrProcessor) {
p = &clientAddrProcessor{
rdns: &rdns.Empty{},
whois: &whois.Empty{},
}
if c.RDNS {
p.rdns = rdns.New(&rdns.Config{
Exchanger: Context.dnsServer,
CacheSize: defaultCacheSize,
CacheTTL: defaultIPTTL,
})
}
if c.WHOIS {
// TODO(s.chzhen): Consider making configurable.
const (
// defaultTimeout is the timeout for WHOIS requests.
defaultTimeout = 5 * time.Second
// defaultMaxConnReadSize is an upper limit in bytes for reading from a
// net.Conn.
defaultMaxConnReadSize = 64 * 1024
// defaultMaxRedirects is the maximum redirects count.
defaultMaxRedirects = 5
// defaultMaxInfoLen is the maximum length of whois.Info fields.
defaultMaxInfoLen = 250
)
p.whois = whois.New(&whois.Config{
DialContext: customDialContext,
ServerAddr: whois.DefaultServer,
Port: whois.DefaultPort,
Timeout: defaultTimeout,
CacheSize: defaultCacheSize,
MaxConnReadSize: defaultMaxConnReadSize,
MaxRedirects: defaultMaxRedirects,
MaxInfoLen: defaultMaxInfoLen,
CacheTTL: defaultIPTTL,
})
}
return p
}
// process processes the incoming client IP-address information. It is intended
// to be used as a goroutine. Once clientIPs is closed, process exits.
func (p *clientAddrProcessor) process(clientIPs <-chan netip.Addr) {
defer log.OnPanic("clientAddrProcessor.process")
log.Info("home: processing client addresses")
for ip := range clientIPs {
p.processRDNS(ip)
p.processWHOIS(ip)
}
log.Info("home: finished processing client addresses")
}
// processRDNS resolves the clients' IP addresses using reverse DNS.
func (p *clientAddrProcessor) processRDNS(ip netip.Addr) {
start := time.Now()
log.Debug("home: processing client %s with rdns", ip)
defer func() {
log.Debug("home: finished processing client %s with rdns in %s", ip, time.Since(start))
}()
ok := Context.dnsServer.ShouldResolveClient(ip)
if !ok {
return
}
host, changed := p.rdns.Process(ip)
if host == "" || !changed {
return
}
ok = Context.clients.AddHost(ip, host, ClientSourceRDNS)
if ok {
return
}
log.Debug("dns: setting rdns info for client %q: already set with higher priority source", ip)
}
// processWHOIS looks up the information aobut clients' IP addresses in the
// WHOIS databases.
func (p *clientAddrProcessor) processWHOIS(ip netip.Addr) {
start := time.Now()
log.Debug("home: processing client %s with whois", ip)
defer func() {
log.Debug("home: finished processing client %s with whois in %s", ip, time.Since(start))
}()
// TODO(s.chzhen): Move the timeout logic from WHOIS configuration to the
// context.
info, changed := p.whois.Process(context.Background(), ip)
if info == nil || !changed {
return
}
Context.clients.setWHOISInfo(ip, info)
}