Pull request 2272: AGDNS-2374-slog-rdns-whois

Squashed commit of the following:

commit 695db57ec872ac8fa1b3a3b2c095bbe4c5c782cc
Merge: 6b3fda0a3 0b8bf1345
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Aug 29 16:08:39 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-rdns-whois

commit 6b3fda0a37dfb309cc0909cd86dcb86e6b22afef
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Aug 27 20:40:01 2024 +0300

    all: imp code

commit 0b1f022094e268f51391825c9d5ff9ff1ea27af0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Aug 26 20:35:06 2024 +0300

    all: slog rdns whois
This commit is contained in:
Stanislav Chzhen
2024-08-29 16:20:05 +03:00
parent 0b8bf13453
commit aab6769fa2
11 changed files with 104 additions and 65 deletions

View File

@@ -2,11 +2,13 @@
package rdns
import (
"context"
"log/slog"
"net/netip"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/bluele/gcache"
)
@@ -14,7 +16,7 @@ import (
type Interface interface {
// Process makes rDNS request and returns domain name. changed indicates
// that domain name was updated since last request.
Process(ip netip.Addr) (host string, changed bool)
Process(ctx context.Context, ip netip.Addr) (host string, changed bool)
}
// Empty is an empty [Interface] implementation which does nothing.
@@ -24,7 +26,7 @@ type Empty struct{}
var _ Interface = (*Empty)(nil)
// Process implements the [Interface] interface for Empty.
func (Empty) Process(_ netip.Addr) (host string, changed bool) {
func (Empty) Process(_ context.Context, _ netip.Addr) (host string, changed bool) {
return "", false
}
@@ -37,6 +39,10 @@ type Exchanger interface {
// Config is the configuration structure for Default.
type Config struct {
// Logger is used for logging the operation of the reverse DNS lookup
// queries. It must not be nil.
Logger *slog.Logger
// Exchanger resolves IP addresses to domain names.
Exchanger Exchanger
@@ -50,6 +56,10 @@ type Config struct {
// Default is the default rDNS query processor.
type Default struct {
// logger is used for logging the operation of the reverse DNS lookup
// queries. It must not be nil.
logger *slog.Logger
// cache is the cache containing IP addresses of clients. An active IP
// address is resolved once again after it expires. If IP address couldn't
// be resolved, it stays here for some time to prevent further attempts to
@@ -66,6 +76,7 @@ type Default struct {
// New returns a new default rDNS query processor. conf must not be nil.
func New(conf *Config) (r *Default) {
return &Default{
logger: conf.Logger,
cache: gcache.New(conf.CacheSize).LRU().Build(),
exchanger: conf.Exchanger,
cacheTTL: conf.CacheTTL,
@@ -76,15 +87,15 @@ func New(conf *Config) (r *Default) {
var _ Interface = (*Default)(nil)
// Process implements the [Interface] interface for Default.
func (r *Default) Process(ip netip.Addr) (host string, changed bool) {
fromCache, expired := r.findInCache(ip)
func (r *Default) Process(ctx context.Context, ip netip.Addr) (host string, changed bool) {
fromCache, expired := r.findInCache(ctx, ip)
if !expired {
return fromCache, false
}
host, ttl, err := r.exchanger.Exchange(ip)
if err != nil {
log.Debug("rdns: resolving %q: %s", ip, err)
r.logger.DebugContext(ctx, "resolving", "ip", ip, slogutil.KeyError, err)
}
ttl = max(ttl, r.cacheTTL)
@@ -96,7 +107,7 @@ func (r *Default) Process(ip netip.Addr) (host string, changed bool) {
err = r.cache.Set(ip, item)
if err != nil {
log.Debug("rdns: cache: adding item %q: %s", ip, err)
r.logger.DebugContext(ctx, "adding item to cache", "key", ip, slogutil.KeyError, err)
}
// TODO(e.burkov): The name doesn't change if it's neither stored in cache
@@ -106,22 +117,22 @@ func (r *Default) Process(ip netip.Addr) (host string, changed bool) {
// findInCache finds domain name in the cache. expired is true if host is not
// valid anymore.
func (r *Default) findInCache(ip netip.Addr) (host string, expired bool) {
func (r *Default) findInCache(ctx context.Context, ip netip.Addr) (host string, expired bool) {
val, err := r.cache.Get(ip)
if err != nil {
if !errors.Is(err, gcache.KeyNotFoundError) {
log.Debug("rdns: cache: retrieving %q: %s", ip, err)
r.logger.DebugContext(
ctx,
"retrieving item from cache",
"key", ip,
slogutil.KeyError, err,
)
}
return "", true
}
item, ok := val.(*cacheItem)
if !ok {
log.Debug("rdns: cache: %q bad type %T", ip, val)
return "", true
}
item := val.(*cacheItem)
return item.host, time.Now().After(item.expiry)
}

View File

@@ -8,10 +8,14 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// testTimeout is a common timeout for tests and contexts.
const testTimeout = 1 * time.Second
func TestDefault_Process(t *testing.T) {
ip1 := netip.MustParseAddr("1.2.3.4")
revAddr1, err := netutil.IPToReversedAddr(ip1.AsSlice())
@@ -71,14 +75,14 @@ func TestDefault_Process(t *testing.T) {
Exchanger: &aghtest.Exchanger{OnExchange: onExchange},
})
got, changed := r.Process(tc.addr)
got, changed := r.Process(testutil.ContextWithTimeout(t, testTimeout), tc.addr)
require.True(t, changed)
assert.Equal(t, tc.want, got)
assert.Equal(t, 1, hit)
// From cache.
got, changed = r.Process(tc.addr)
got, changed = r.Process(testutil.ContextWithTimeout(t, testTimeout), tc.addr)
require.False(t, changed)
assert.Equal(t, tc.want, got)
@@ -101,7 +105,7 @@ func TestDefault_Process(t *testing.T) {
Exchanger: zeroTTLExchanger,
})
got, changed := r.Process(ip1)
got, changed := r.Process(testutil.ContextWithTimeout(t, testTimeout), ip1)
require.True(t, changed)
assert.Equal(t, revAddr1, got)
@@ -109,14 +113,15 @@ func TestDefault_Process(t *testing.T) {
return revAddr2, time.Hour, nil
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
require.EventuallyWithT(t, func(t *assert.CollectT) {
got, changed = r.Process(ip1)
got, changed = r.Process(ctx, ip1)
assert.True(t, changed)
assert.Equal(t, revAddr2, got)
}, 2*cacheTTL, time.Millisecond*100)
assert.Never(t, func() (changed bool) {
_, changed = r.Process(ip1)
_, changed = r.Process(testutil.ContextWithTimeout(t, testTimeout), ip1)
return changed
}, 2*cacheTTL, time.Millisecond*100)