From dead10e0337ae8aac1121a70a78cf596c329ff65 Mon Sep 17 00:00:00 2001 From: Ainar Garipov Date: Tue, 18 Jul 2023 14:02:32 +0300 Subject: [PATCH] Pull request 1925: 6006-client-processor Updates #6006. Squashed commit of the following: commit c72d6375e9c472c73b0bb9d025a8e197f404ba38 Merge: 02d64b10e 0cd441f04 Author: Ainar Garipov Date: Tue Jul 18 13:56:26 2023 +0300 Merge branch 'master' into 6006-client-processor commit 02d64b10e19b2e937e45cab58d2310231a19bfbc Author: Ainar Garipov Date: Mon Jul 17 19:42:07 2023 +0300 client: imp code, tests commit b1613463089b4dde97484ff6a44b05888f0c2276 Author: Ainar Garipov Date: Mon Jul 17 18:42:19 2023 +0300 client: imp code, docs, tests commit f71a17983b70d79839cf35dbe3279f0fdcac2ed7 Author: Ainar Garipov Date: Fri Jul 14 21:53:47 2023 +0300 all: add new client processor; imp code --- internal/aghtest/interface.go | 57 ++++++ internal/client/addrproc.go | 293 +++++++++++++++++++++++++++++++ internal/client/addrproc_test.go | 259 +++++++++++++++++++++++++++ internal/client/client.go | 5 + internal/client/client_test.go | 25 +++ internal/rdns/rdns.go | 2 +- internal/rdns/rdns_test.go | 16 +- internal/whois/whois.go | 21 ++- internal/whois/whois_test.go | 14 +- scripts/make/go-lint.sh | 5 + 10 files changed, 664 insertions(+), 33 deletions(-) create mode 100644 internal/client/addrproc.go create mode 100644 internal/client/addrproc_test.go create mode 100644 internal/client/client.go create mode 100644 internal/client/client_test.go diff --git a/internal/aghtest/interface.go b/internal/aghtest/interface.go index cce49776..779e2fe5 100644 --- a/internal/aghtest/interface.go +++ b/internal/aghtest/interface.go @@ -4,9 +4,13 @@ import ( "context" "io" "io/fs" + "net/netip" "github.com/AdguardTeam/AdGuardHome/internal/aghos" + "github.com/AdguardTeam/AdGuardHome/internal/client" "github.com/AdguardTeam/AdGuardHome/internal/next/agh" + "github.com/AdguardTeam/AdGuardHome/internal/rdns" + "github.com/AdguardTeam/AdGuardHome/internal/whois" "github.com/AdguardTeam/dnsproxy/upstream" "github.com/miekg/dns" ) @@ -135,6 +139,59 @@ func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) { return s.OnConfig() } +// Package client + +// AddressProcessor is a fake [client.AddressProcessor] implementation for +// tests. +type AddressProcessor struct { + OnProcess func(ip netip.Addr) + OnClose func() (err error) +} + +// type check +var _ client.AddressProcessor = (*AddressProcessor)(nil) + +// Process implements the [client.AddressProcessor] interface for +// *AddressProcessor. +func (p *AddressProcessor) Process(ip netip.Addr) { + p.OnProcess(ip) +} + +// Close implements the [client.AddressProcessor] interface for +// *AddressProcessor. +func (p *AddressProcessor) Close() (err error) { + return p.OnClose() +} + +// AddressUpdater is a fake [client.AddressUpdater] implementation for tests. +type AddressUpdater struct { + OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info) +} + +// type check +var _ client.AddressUpdater = (*AddressUpdater)(nil) + +// UpdateAddress implements the [client.AddressUpdater] interface for +// *AddressUpdater. +func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) { + p.OnUpdateAddress(ip, host, info) +} + +// Package rdns + +// Exchanger is a fake [rdns.Exchanger] implementation for tests. +type Exchanger struct { + OnExchange func(ip netip.Addr) (host string, err error) +} + +// type check +var _ rdns.Exchanger = (*Exchanger)(nil) + +// Exchange implements [rdns.Exchanger] interface for *Exchanger. +func (e *Exchanger) Exchange(ip netip.Addr) (host string, err error) { + return e.OnExchange(ip) +} + // Module dnsproxy // Package upstream diff --git a/internal/client/addrproc.go b/internal/client/addrproc.go new file mode 100644 index 00000000..72969ced --- /dev/null +++ b/internal/client/addrproc.go @@ -0,0 +1,293 @@ +package client + +import ( + "context" + "net/netip" + "sync" + "time" + + "github.com/AdguardTeam/AdGuardHome/internal/rdns" + "github.com/AdguardTeam/AdGuardHome/internal/whois" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/log" + "github.com/AdguardTeam/golibs/netutil" +) + +// ErrClosed is returned from [AddressProcessor.Close] if it's closed more than +// once. +const ErrClosed errors.Error = "use of closed address processor" + +// AddressProcessor is the interface for types that can process clients. +type AddressProcessor interface { + Process(ip netip.Addr) + Close() (err error) +} + +// EmptyAddrProc is an [AddressProcessor] that does nothing. +type EmptyAddrProc struct{} + +// type check +var _ AddressProcessor = EmptyAddrProc{} + +// Process implements the [AddressProcessor] interface for EmptyAddrProc. +func (EmptyAddrProc) Process(_ netip.Addr) {} + +// Close implements the [AddressProcessor] interface for EmptyAddrProc. +func (EmptyAddrProc) Close() (_ error) { return nil } + +// DefaultAddrProcConfig is the configuration structure for address processors. +type DefaultAddrProcConfig struct { + // DialContext is used to create TCP connections to WHOIS servers. + // DialContext must not be nil if [DefaultAddrProcConfig.UseWHOIS] is true. + DialContext whois.DialContextFunc + + // Exchanger is used to perform rDNS queries. Exchanger must not be nil if + // [DefaultAddrProcConfig.UseRDNS] is true. + Exchanger rdns.Exchanger + + // PrivateSubnets are used to determine if an incoming IP address is + // private. It must not be nil. + PrivateSubnets netutil.SubnetSet + + // AddressUpdater is used to update the information about a client's IP + // address. It must not be nil. + AddressUpdater AddressUpdater + + // InitialAddresses are the addresses that are queued for processing + // immediately by [NewDefaultAddrProc]. + InitialAddresses []netip.Addr + + // UseRDNS, if true, enables resolving of client IP addresses using reverse + // DNS. + UseRDNS bool + + // UsePrivateRDNS, if true, enables resolving of private client IP addresses + // using reverse DNS. See [DefaultAddrProcConfig.PrivateSubnets]. + UsePrivateRDNS bool + + // UseWHOIS, if true, enables resolving of client IP addresses using WHOIS. + UseWHOIS bool +} + +// AddressUpdater is the interface for storages of DNS clients that can update +// information about them. +// +// TODO(a.garipov): Consider using the actual client storage once it is moved +// into this package. +type AddressUpdater interface { + // UpdateAddress updates information about an IP address, setting host (if + // not empty) and WHOIS information (if not nil). + UpdateAddress(ip netip.Addr, host string, info *whois.Info) +} + +// DefaultAddrProc processes incoming client addresses with rDNS and WHOIS, if +// configured, and updates that information in a client storage. +type DefaultAddrProc struct { + // clientIPsMu serializes closure of clientIPs and access to isClosed. + clientIPsMu *sync.Mutex + + // clientIPs is the channel queueing client processing tasks. + clientIPs chan netip.Addr + + // rdns is used to perform rDNS lookups of clients' IP addresses. + rdns rdns.Interface + + // whois is used to perform WHOIS lookups of clients' IP addresses. + whois whois.Interface + + // addrUpdater is used to update the information about a client's IP + // address. + addrUpdater AddressUpdater + + // privateSubnets are used to determine if an incoming IP address is + // private. + privateSubnets netutil.SubnetSet + + // isClosed is set to true once the address processor is closed. + isClosed bool + + // usePrivateRDNS, if true, enables resolving of private client IP addresses + // using reverse DNS. + usePrivateRDNS bool +} + +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 +) + +// NewDefaultAddrProc returns a new running client address processor. c must +// not be nil. +func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) { + p = &DefaultAddrProc{ + clientIPsMu: &sync.Mutex{}, + clientIPs: make(chan netip.Addr, defaultQueueSize), + rdns: &rdns.Empty{}, + addrUpdater: c.AddressUpdater, + whois: &whois.Empty{}, + privateSubnets: c.PrivateSubnets, + usePrivateRDNS: c.UsePrivateRDNS, + } + + if c.UseRDNS { + p.rdns = rdns.New(&rdns.Config{ + Exchanger: c.Exchanger, + CacheSize: defaultCacheSize, + CacheTTL: defaultIPTTL, + }) + } + + if c.UseWHOIS { + p.whois = newWHOIS(c.DialContext) + } + + go p.process() + + for _, ip := range c.InitialAddresses { + p.Process(ip) + } + + return p +} + +// newWHOIS returns a whois.Interface instance using the given function for +// dialing. +func newWHOIS(dialFunc whois.DialContextFunc) (w whois.Interface) { + // 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 + ) + + return whois.New(&whois.Config{ + DialContext: dialFunc, + ServerAddr: whois.DefaultServer, + Port: whois.DefaultPort, + Timeout: defaultTimeout, + CacheSize: defaultCacheSize, + MaxConnReadSize: defaultMaxConnReadSize, + MaxRedirects: defaultMaxRedirects, + MaxInfoLen: defaultMaxInfoLen, + CacheTTL: defaultIPTTL, + }) +} + +// type check +var _ AddressProcessor = (*DefaultAddrProc)(nil) + +// Process implements the [AddressProcessor] interface for *DefaultAddrProc. +func (p *DefaultAddrProc) Process(ip netip.Addr) { + p.clientIPsMu.Lock() + defer p.clientIPsMu.Unlock() + + if p.isClosed { + return + } + + select { + case p.clientIPs <- ip: + // Go on. + default: + log.Debug("clients: ip channel is full; len: %d", len(p.clientIPs)) + } +} + +// 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 *DefaultAddrProc) process() { + defer log.OnPanic("addrProcessor.process") + + log.Info("clients: processing addresses") + + for ip := range p.clientIPs { + host := p.processRDNS(ip) + info := p.processWHOIS(ip) + + p.addrUpdater.UpdateAddress(ip, host, info) + } + + log.Info("clients: finished processing addresses") +} + +// processRDNS resolves the clients' IP addresses using reverse DNS. host is +// empty if there were errors or if the information hasn't changed. +func (p *DefaultAddrProc) processRDNS(ip netip.Addr) (host string) { + start := time.Now() + log.Debug("clients: processing %s with rdns", ip) + defer func() { + log.Debug("clients: finished processing %s with rdns in %s", ip, time.Since(start)) + }() + + ok := p.shouldResolve(ip) + if !ok { + return + } + + host, changed := p.rdns.Process(ip) + if !changed { + host = "" + } + + return host +} + +// shouldResolve returns false if ip is a loopback address, or ip is private and +// resolving of private addresses is disabled. +func (p *DefaultAddrProc) shouldResolve(ip netip.Addr) (ok bool) { + return !ip.IsLoopback() && + (p.usePrivateRDNS || !p.privateSubnets.Contains(ip.AsSlice())) +} + +// processWHOIS looks up the information about clients' IP addresses in the +// WHOIS databases. info is nil if there were errors or if the information +// hasn't changed. +func (p *DefaultAddrProc) processWHOIS(ip netip.Addr) (info *whois.Info) { + start := time.Now() + log.Debug("clients: processing %s with whois", ip) + defer func() { + log.Debug("clients: finished processing %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 !changed { + info = nil + } + + return info +} + +// Close implements the [AddressProcessor] interface for *DefaultAddrProc. +func (p *DefaultAddrProc) Close() (err error) { + p.clientIPsMu.Lock() + defer p.clientIPsMu.Unlock() + + if p.isClosed { + return ErrClosed + } + + close(p.clientIPs) + p.isClosed = true + + return nil +} diff --git a/internal/client/addrproc_test.go b/internal/client/addrproc_test.go new file mode 100644 index 00000000..5690be37 --- /dev/null +++ b/internal/client/addrproc_test.go @@ -0,0 +1,259 @@ +package client_test + +import ( + "context" + "io" + "net" + "net/netip" + "testing" + "time" + + "github.com/AdguardTeam/AdGuardHome/internal/aghtest" + "github.com/AdguardTeam/AdGuardHome/internal/client" + "github.com/AdguardTeam/AdGuardHome/internal/whois" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/testutil" + "github.com/AdguardTeam/golibs/testutil/fakenet" + "github.com/stretchr/testify/assert" +) + +func TestEmptyAddrProc(t *testing.T) { + t.Parallel() + + p := client.EmptyAddrProc{} + + assert.NotPanics(t, func() { + p.Process(testIP) + }) + + assert.NotPanics(t, func() { + err := p.Close() + assert.NoError(t, err) + }) +} + +func TestDefaultAddrProc_Process_rDNS(t *testing.T) { + t.Parallel() + + privateIP := netip.MustParseAddr("192.168.0.1") + + testCases := []struct { + rdnsErr error + ip netip.Addr + name string + host string + usePrivate bool + wantUpd bool + }{{ + rdnsErr: nil, + ip: testIP, + name: "success", + host: testHost, + usePrivate: false, + wantUpd: true, + }, { + rdnsErr: nil, + ip: testIP, + name: "no_host", + host: "", + usePrivate: false, + wantUpd: false, + }, { + rdnsErr: nil, + ip: netip.MustParseAddr("127.0.0.1"), + name: "localhost", + host: "", + usePrivate: false, + wantUpd: false, + }, { + rdnsErr: nil, + ip: privateIP, + name: "private_ignored", + host: "", + usePrivate: false, + wantUpd: false, + }, { + rdnsErr: nil, + ip: privateIP, + name: "private_processed", + host: "private.example", + usePrivate: true, + wantUpd: true, + }, { + rdnsErr: errors.Error("rdns error"), + ip: testIP, + name: "rdns_error", + host: "", + usePrivate: false, + wantUpd: false, + }} + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + updIPCh := make(chan netip.Addr, 1) + updHostCh := make(chan string, 1) + updInfoCh := make(chan *whois.Info, 1) + + p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{ + DialContext: func(_ context.Context, _, _ string) (conn net.Conn, err error) { + panic("not implemented") + }, + Exchanger: &aghtest.Exchanger{ + OnExchange: func(ip netip.Addr) (host string, err error) { + return tc.host, tc.rdnsErr + }, + }, + PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed), + AddressUpdater: &aghtest.AddressUpdater{ + OnUpdateAddress: newOnUpdateAddress(tc.wantUpd, updIPCh, updHostCh, updInfoCh), + }, + UseRDNS: true, + UsePrivateRDNS: tc.usePrivate, + UseWHOIS: false, + }) + testutil.CleanupAndRequireSuccess(t, p.Close) + + p.Process(tc.ip) + + if !tc.wantUpd { + return + } + + gotIP, _ := testutil.RequireReceive(t, updIPCh, testTimeout) + assert.Equal(t, tc.ip, gotIP) + + gotHost, _ := testutil.RequireReceive(t, updHostCh, testTimeout) + assert.Equal(t, tc.host, gotHost) + + gotInfo, _ := testutil.RequireReceive(t, updInfoCh, testTimeout) + assert.Nil(t, gotInfo) + }) + } +} + +// newOnUpdateAddress is a test helper that returns a new OnUpdateAddress +// callback using the provided channels if an update is expected and panicking +// otherwise. +func newOnUpdateAddress( + want bool, + ips chan<- netip.Addr, + hosts chan<- string, + infos chan<- *whois.Info, +) (f func(ip netip.Addr, host string, info *whois.Info)) { + return func(ip netip.Addr, host string, info *whois.Info) { + if !want { + panic("got unexpected update") + } + + ips <- ip + hosts <- host + infos <- info + } +} + +func TestDefaultAddrProc_Process_WHOIS(t *testing.T) { + t.Parallel() + + testCases := []struct { + wantInfo *whois.Info + exchErr error + name string + wantUpd bool + }{{ + wantInfo: &whois.Info{ + City: testWHOISCity, + }, + exchErr: nil, + name: "success", + wantUpd: true, + }, { + wantInfo: nil, + exchErr: nil, + name: "no_info", + wantUpd: false, + }, { + wantInfo: nil, + exchErr: errors.Error("whois error"), + name: "whois_error", + wantUpd: false, + }} + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + whoisConn := &fakenet.Conn{ + OnClose: func() (err error) { return nil }, + OnRead: func(b []byte) (n int, err error) { + if tc.wantInfo == nil { + return 0, tc.exchErr + } + + data := "city: " + tc.wantInfo.City + "\n" + copy(b, data) + + return len(data), io.EOF + }, + OnSetDeadline: func(_ time.Time) (err error) { return nil }, + OnWrite: func(b []byte) (n int, err error) { return len(b), nil }, + } + + updIPCh := make(chan netip.Addr, 1) + updHostCh := make(chan string, 1) + updInfoCh := make(chan *whois.Info, 1) + + p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{ + DialContext: func(_ context.Context, _, _ string) (conn net.Conn, err error) { + return whoisConn, nil + }, + Exchanger: &aghtest.Exchanger{ + OnExchange: func(_ netip.Addr) (host string, err error) { + panic("not implemented") + }, + }, + PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed), + AddressUpdater: &aghtest.AddressUpdater{ + OnUpdateAddress: newOnUpdateAddress(tc.wantUpd, updIPCh, updHostCh, updInfoCh), + }, + UseRDNS: false, + UsePrivateRDNS: false, + UseWHOIS: true, + }) + testutil.CleanupAndRequireSuccess(t, p.Close) + + p.Process(testIP) + + if !tc.wantUpd { + return + } + + gotIP, _ := testutil.RequireReceive(t, updIPCh, testTimeout) + assert.Equal(t, testIP, gotIP) + + gotHost, _ := testutil.RequireReceive(t, updHostCh, testTimeout) + assert.Empty(t, gotHost) + + gotInfo, _ := testutil.RequireReceive(t, updInfoCh, testTimeout) + assert.Equal(t, tc.wantInfo, gotInfo) + }) + } +} + +func TestDefaultAddrProc_Close(t *testing.T) { + t.Parallel() + + p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{}) + + err := p.Close() + assert.NoError(t, err) + + err = p.Close() + assert.ErrorIs(t, err, client.ErrClosed) +} diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 00000000..6cfcec79 --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,5 @@ +// Package client contains types and logic dealing with AdGuard Home's DNS +// clients. +// +// TODO(a.garipov): Expand. +package client diff --git a/internal/client/client_test.go b/internal/client/client_test.go new file mode 100644 index 00000000..b579f4f8 --- /dev/null +++ b/internal/client/client_test.go @@ -0,0 +1,25 @@ +package client_test + +import ( + "net/netip" + "testing" + "time" + + "github.com/AdguardTeam/golibs/testutil" +) + +func TestMain(m *testing.M) { + testutil.DiscardLogOutput(m) +} + +// testHost is the common hostname for tests. +const testHost = "client.example" + +// testTimeout is the common timeout for tests. +const testTimeout = 1 * time.Second + +// testWHOISCity is the common city for tests. +const testWHOISCity = "Brussels" + +// testIP is the common IP address for tests. +var testIP = netip.MustParseAddr("1.2.3.4") diff --git a/internal/rdns/rdns.go b/internal/rdns/rdns.go index e352da52..ff1998a5 100644 --- a/internal/rdns/rdns.go +++ b/internal/rdns/rdns.go @@ -17,7 +17,7 @@ type Interface interface { Process(ip netip.Addr) (host string, changed bool) } -// Empty is an empty [Inteface] implementation which does nothing. +// Empty is an empty [Interface] implementation which does nothing. type Empty struct{} // type check diff --git a/internal/rdns/rdns_test.go b/internal/rdns/rdns_test.go index 8694eba3..642e4567 100644 --- a/internal/rdns/rdns_test.go +++ b/internal/rdns/rdns_test.go @@ -5,25 +5,13 @@ import ( "testing" "time" + "github.com/AdguardTeam/AdGuardHome/internal/aghtest" "github.com/AdguardTeam/AdGuardHome/internal/rdns" "github.com/AdguardTeam/golibs/netutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// fakeRDNSExchanger is a mock [rdns.Exchanger] implementation for tests. -type fakeRDNSExchanger struct { - OnExchange func(ip netip.Addr) (host string, err error) -} - -// type check -var _ rdns.Exchanger = (*fakeRDNSExchanger)(nil) - -// Exchange implements [rdns.Exchanger] interface for *fakeRDNSExchanger. -func (e *fakeRDNSExchanger) Exchange(ip netip.Addr) (host string, err error) { - return e.OnExchange(ip) -} - func TestDefault_Process(t *testing.T) { ip1 := netip.MustParseAddr("1.2.3.4") revAddr1, err := netutil.IPToReversedAddr(ip1.AsSlice()) @@ -81,7 +69,7 @@ func TestDefault_Process(t *testing.T) { return "", nil } } - exchanger := &fakeRDNSExchanger{ + exchanger := &aghtest.Exchanger{ OnExchange: onExchange, } diff --git a/internal/whois/whois.go b/internal/whois/whois.go index 2a64bdd1..49c179b8 100644 --- a/internal/whois/whois.go +++ b/internal/whois/whois.go @@ -48,9 +48,8 @@ func (Empty) Process(_ context.Context, _ netip.Addr) (info *Info, changed bool) // Config is the configuration structure for Default. type Config struct { - // DialContext specifies the dial function for creating unencrypted TCP - // connections. - DialContext func(ctx context.Context, network, addr string) (conn net.Conn, err error) + // DialContext is used to create TCP connections to WHOIS servers. + DialContext DialContextFunc // ServerAddr is the address of the WHOIS server. ServerAddr string @@ -78,6 +77,13 @@ type Config struct { Port uint16 } +// DialContextFunc is the semantic alias for dialing functions, such as +// [http.Transport.DialContext]. +// +// TODO(a.garipov): Move to aghnet once it stops importing aghtest, because +// otherwise there is an import cycle. +type DialContextFunc = func(ctx context.Context, network, addr string) (conn net.Conn, err error) + // Default is the default WHOIS information processor. type Default struct { // cache is the cache containing IP addresses of clients. An active IP @@ -86,9 +92,8 @@ type Default struct { // resolve the same IP. cache gcache.Cache - // dialContext connects to a remote server resolving hostname using our own - // DNS server and unecrypted TCP connection. - dialContext func(ctx context.Context, network, addr string) (conn net.Conn, err error) + // dialContext is used to create TCP connections to WHOIS servers. + dialContext DialContextFunc // serverAddr is the address of the WHOIS server. serverAddr string @@ -215,7 +220,7 @@ func (w *Default) query(ctx context.Context, target, serverAddr string) (data [] return nil, err } - _ = conn.SetReadDeadline(time.Now().Add(w.timeout)) + _ = conn.SetDeadline(time.Now().Add(w.timeout)) _, err = io.WriteString(conn, target+"\r\n") if err != nil { // Don't wrap the error since it's informative enough as is. @@ -310,7 +315,7 @@ func (w *Default) requestInfo( kv, err := w.queryAll(ctx, ip.String()) if err != nil { - log.Debug("whois: quering about %q: %s", ip, err) + log.Debug("whois: querying %q: %s", ip, err) return nil, true } diff --git a/internal/whois/whois_test.go b/internal/whois/whois_test.go index 2fe32255..109ee1e9 100644 --- a/internal/whois/whois_test.go +++ b/internal/whois/whois_test.go @@ -113,20 +113,14 @@ func TestDefault_Process(t *testing.T) { return copy(b, tc.data), io.EOF }, - OnWrite: func(b []byte) (n int, err error) { - return len(b), nil - }, - OnClose: func() (err error) { - return nil - }, - OnSetReadDeadline: func(t time.Time) (err error) { - return nil - }, + OnWrite: func(b []byte) (n int, err error) { return len(b), nil }, + OnClose: func() (err error) { return nil }, + OnSetDeadline: func(t time.Time) (err error) { return nil }, } w := whois.New(&whois.Config{ Timeout: 5 * time.Second, - DialContext: func(_ context.Context, _, addr string) (_ net.Conn, _ error) { + DialContext: func(_ context.Context, _, _ string) (_ net.Conn, _ error) { hit = 0 return fakeConn, nil diff --git a/scripts/make/go-lint.sh b/scripts/make/go-lint.sh index b36db02f..b8a0c706 100644 --- a/scripts/make/go-lint.sh +++ b/scripts/make/go-lint.sh @@ -176,10 +176,13 @@ run_linter gocognit --over 10\ ./internal/aghchan/\ ./internal/aghhttp/\ ./internal/aghio/\ + ./internal/client/\ + ./internal/dhcpsvc\ ./internal/filtering/hashprefix/\ ./internal/filtering/rulelist/\ ./internal/next/\ ./internal/rdns/\ + ./internal/schedule/\ ./internal/tools/\ ./internal/version/\ ./internal/whois/\ @@ -211,12 +214,14 @@ run_linter gosec --quiet\ ./internal/aghnet\ ./internal/aghos\ ./internal/aghtest\ + ./internal/client\ ./internal/dhcpd\ ./internal/dhcpsvc\ ./internal/dnsforward\ ./internal/filtering/hashprefix/\ ./internal/filtering/rulelist/\ ./internal/next\ + ./internal/rdns\ ./internal/schedule\ ./internal/stats\ ./internal/tools\