all: sync with master

This commit is contained in:
Eugene Burkov
2024-10-29 18:53:56 +03:00
parent 2aaf8ab3c1
commit 6affa96490
51 changed files with 1361 additions and 456 deletions

View File

@@ -90,7 +90,11 @@ func InitAuth(
trustedProxies: trustedProxies,
}
var err error
a.db, err = bbolt.Open(dbFilename, aghos.DefaultPermFile, nil)
opts := *bbolt.DefaultOptions
opts.OpenFile = aghos.OpenFile
a.db, err = bbolt.Open(dbFilename, aghos.DefaultPermFile, &opts)
if err != nil {
log.Error("auth: open DB: %s: %s", dbFilename, err)
if err.Error() == "invalid argument" {

View File

@@ -3,6 +3,7 @@ package home
import (
"context"
"fmt"
"log/slog"
"net/netip"
"slices"
"sync"
@@ -13,17 +14,23 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/stringutil"
)
// clientsContainer is the storage of all runtime and persistent clients.
type clientsContainer struct {
// baseLogger is used to create loggers with custom prefixes for safe search
// filter. It must not be nil.
baseLogger *slog.Logger
// storage stores information about persistent clients.
storage *client.Storage
@@ -61,6 +68,8 @@ type BlockedClientChecker interface {
// dhcpServer: optional
// Note: this function must be called only once
func (clients *clientsContainer) Init(
ctx context.Context,
baseLogger *slog.Logger,
objects []*clientObject,
dhcpServer client.DHCP,
etcHosts *aghnet.HostsContainer,
@@ -72,13 +81,14 @@ func (clients *clientsContainer) Init(
return errors.Error("clients container already initialized")
}
clients.baseLogger = baseLogger
clients.safeSearchCacheSize = filteringConf.SafeSearchCacheSize
clients.safeSearchCacheTTL = time.Minute * time.Duration(filteringConf.CacheTime)
confClients := make([]*client.Persistent, 0, len(objects))
for i, o := range objects {
var p *client.Persistent
p, err = o.toPersistent(clients.safeSearchCacheSize, clients.safeSearchCacheTTL)
p, err = o.toPersistent(ctx, baseLogger, clients.safeSearchCacheSize, clients.safeSearchCacheTTL)
if err != nil {
return fmt.Errorf("init persistent client at index %d: %w", i, err)
}
@@ -92,12 +102,13 @@ func (clients *clientsContainer) Init(
// TODO(e.burkov): The option should probably be returned, since hosts file
// currently used not only for clients' information enrichment, but also in
// the filtering module and upstream addresses resolution.
var hosts client.HostsContainer = etcHosts
if !config.Clients.Sources.HostsFile {
hosts = nil
var hosts client.HostsContainer
if config.Clients.Sources.HostsFile && etcHosts != nil {
hosts = etcHosts
}
clients.storage, err = client.NewStorage(&client.StorageConfig{
clients.storage, err = client.NewStorage(ctx, &client.StorageConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "client_storage"),
InitialClients: confClients,
DHCP: dhcpServer,
EtcHosts: hosts,
@@ -168,6 +179,8 @@ type clientObject struct {
// toPersistent returns an initialized persistent client if there are no errors.
func (o *clientObject) toPersistent(
ctx context.Context,
baseLogger *slog.Logger,
safeSearchCacheSize uint,
safeSearchCacheTTL time.Duration,
) (cli *client.Persistent, err error) {
@@ -203,14 +216,23 @@ func (o *clientObject) toPersistent(
}
if o.SafeSearchConf.Enabled {
err = cli.SetSafeSearch(
o.SafeSearchConf,
safeSearchCacheSize,
safeSearchCacheTTL,
logger := baseLogger.With(
slogutil.KeyPrefix, safesearch.LogPrefix,
safesearch.LogKeyClient, cli.Name,
)
var ss *safesearch.Default
ss, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: logger,
ServicesConfig: o.SafeSearchConf,
ClientName: cli.Name,
CacheSize: safeSearchCacheSize,
CacheTTL: safeSearchCacheTTL,
})
if err != nil {
return nil, fmt.Errorf("init safesearch %q: %w", cli.Name, err)
}
cli.SafeSearch = ss
}
if o.BlockedServices == nil {
@@ -396,6 +418,12 @@ func (clients *clientsContainer) UpstreamConfigByID(
)
c.UpstreamConfig = conf
// TODO(s.chzhen): Pass context.
err = clients.storage.Update(context.TODO(), c.Name, c)
if err != nil {
return nil, fmt.Errorf("setting upstream config: %w", err)
}
return conf, nil
}
@@ -404,8 +432,13 @@ var _ client.AddressUpdater = (*clientsContainer)(nil)
// UpdateAddress implements the [client.AddressUpdater] interface for
// *clientsContainer
func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
clients.storage.UpdateAddress(ip, host, info)
func (clients *clientsContainer) UpdateAddress(
ctx context.Context,
ip netip.Addr,
host string,
info *whois.Info,
) {
clients.storage.UpdateAddress(ctx, ip, host, info)
}
// close gracefully closes all the client-specific upstream configurations of

View File

@@ -7,6 +7,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -20,16 +22,28 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) {
testing: true,
}
require.NoError(t, c.Init(nil, client.EmptyDHCP{}, nil, nil, &filtering.Config{}))
ctx := testutil.ContextWithTimeout(t, testTimeout)
err := c.Init(
ctx,
slogutil.NewDiscardLogger(),
nil,
client.EmptyDHCP{},
nil,
nil,
&filtering.Config{},
)
require.NoError(t, err)
return c
}
func TestClientsCustomUpstream(t *testing.T) {
clients := newClientsContainer(t)
ctx := testutil.ContextWithTimeout(t, testTimeout)
// Add client with upstreams.
err := clients.storage.Add(&client.Persistent{
err := clients.storage.Add(ctx, &client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1:2:3::4")},

View File

@@ -1,6 +1,7 @@
package home
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -10,8 +11,10 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// clientJSON is a common structure used by several handlers to deal with
@@ -103,7 +106,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
return true
})
clients.storage.UpdateDHCP()
clients.storage.UpdateDHCP(r.Context())
clients.storage.RangeRuntime(func(rc *client.Runtime) (cont bool) {
src, host := rc.Info()
@@ -181,6 +184,7 @@ func initPrev(cj clientJSON, prev *client.Persistent) (c *client.Persistent, err
// jsonToClient converts JSON object to persistent client object if there are no
// errors.
func (clients *clientsContainer) jsonToClient(
ctx context.Context,
cj clientJSON,
prev *client.Persistent,
) (c *client.Persistent, err error) {
@@ -207,14 +211,23 @@ func (clients *clientsContainer) jsonToClient(
c.UseOwnBlockedServices = !cj.UseGlobalBlockedServices
if c.SafeSearchConf.Enabled {
err = c.SetSafeSearch(
c.SafeSearchConf,
clients.safeSearchCacheSize,
clients.safeSearchCacheTTL,
logger := clients.baseLogger.With(
slogutil.KeyPrefix, safesearch.LogPrefix,
safesearch.LogKeyClient, c.Name,
)
var ss *safesearch.Default
ss, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: logger,
ServicesConfig: c.SafeSearchConf,
ClientName: c.Name,
CacheSize: clients.safeSearchCacheSize,
CacheTTL: clients.safeSearchCacheTTL,
})
if err != nil {
return nil, fmt.Errorf("creating safesearch for client %q: %w", c.Name, err)
}
c.SafeSearch = ss
}
return c, nil
@@ -321,14 +334,14 @@ func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.
return
}
c, err := clients.jsonToClient(cj, nil)
c, err := clients.jsonToClient(r.Context(), cj, nil)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
err = clients.storage.Add(c)
err = clients.storage.Add(r.Context(), c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -391,14 +404,14 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
return
}
c, err := clients.jsonToClient(dj.Data, nil)
c, err := clients.jsonToClient(r.Context(), dj.Data, nil)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
err = clients.storage.Update(dj.Name, c)
err = clients.storage.Update(r.Context(), dj.Name, c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)

View File

@@ -11,14 +11,19 @@ import (
"net/url"
"slices"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// testTimeout is the common timeout for tests and contexts.
const testTimeout = 1 * time.Second
const (
testClientIP1 = "1.1.1.1"
testClientIP2 = "2.2.2.2"
@@ -103,9 +108,10 @@ func assertPersistentClients(tb testing.TB, clients *clientsContainer, want []*c
require.NoError(tb, err)
var got []*client.Persistent
ctx := testutil.ContextWithTimeout(tb, testTimeout)
for _, cj := range clientList.Clients {
var c *client.Persistent
c, err = clients.jsonToClient(*cj, nil)
c, err = clients.jsonToClient(ctx, *cj, nil)
require.NoError(tb, err)
got = append(got, c)
@@ -125,10 +131,11 @@ func assertPersistentClientsData(
tb.Helper()
var got []*client.Persistent
ctx := testutil.ContextWithTimeout(tb, testTimeout)
for _, cm := range data {
for _, cj := range cm {
var c *client.Persistent
c, err := clients.jsonToClient(*cj, nil)
c, err := clients.jsonToClient(ctx, *cj, nil)
require.NoError(tb, err)
got = append(got, c)
@@ -196,13 +203,14 @@ func TestClientsContainer_HandleAddClient(t *testing.T) {
func TestClientsContainer_HandleDelClient(t *testing.T) {
clients := newClientsContainer(t)
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(clientOne)
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.storage.Add(clientTwo)
err = clients.storage.Add(ctx, clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
@@ -258,9 +266,10 @@ func TestClientsContainer_HandleDelClient(t *testing.T) {
func TestClientsContainer_HandleUpdateClient(t *testing.T) {
clients := newClientsContainer(t)
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(clientOne)
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne})
@@ -341,12 +350,14 @@ func TestClientsContainer_HandleFindClient(t *testing.T) {
},
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(clientOne)
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.storage.Add(clientTwo)
err = clients.storage.Add(ctx, clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})

View File

@@ -708,7 +708,7 @@ func (c *configuration) write() (err error) {
return fmt.Errorf("generating config file: %w", err)
}
err = maybe.WriteFile(confPath, buf.Bytes(), aghos.DefaultPermFile)
err = aghos.WriteFile(confPath, buf.Bytes(), aghos.DefaultPermFile)
if err != nil {
return fmt.Errorf("writing config file: %w", err)
}

View File

@@ -7,6 +7,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -18,12 +20,15 @@ var testIPv4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
func newStorage(tb testing.TB, clients []*client.Persistent) (s *client.Storage) {
tb.Helper()
s, err := client.NewStorage(&client.StorageConfig{})
ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(tb, err)
for _, p := range clients {
p.UID = client.MustNewUID()
require.NoError(tb, s.Add(p))
require.NoError(tb, s.Add(ctx, p))
}
return s

View File

@@ -115,15 +115,16 @@ func Main(clientBuildFS fs.FS) {
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
go func() {
ctx := context.Background()
for {
sig := <-signals
log.Info("Received signal %q", sig)
switch sig {
case syscall.SIGHUP:
Context.clients.storage.ReloadARP()
Context.clients.storage.ReloadARP(ctx)
Context.tls.reload()
default:
cleanup(context.Background())
cleanup(ctx)
cleanupAlways()
close(done)
}
@@ -148,6 +149,14 @@ func setupContext(opts options) (err error) {
Context.tlsRoots = aghtls.SystemRootCAs()
Context.mux = http.NewServeMux()
if !opts.noEtcHosts {
err = setupHostsContainer()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
}
if Context.firstRun {
log.Info("This is the first time AdGuard Home is launched")
checkPermissions()
@@ -168,14 +177,6 @@ func setupContext(opts options) (err error) {
os.Exit(0)
}
if !opts.noEtcHosts {
err = setupHostsContainer()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
}
return nil
}
@@ -278,8 +279,8 @@ func setupOpts(opts options) (err error) {
}
// initContextClients initializes Context clients and related fields.
func initContextClients(logger *slog.Logger) (err error) {
err = setupDNSFilteringConf(config.Filtering)
func initContextClients(ctx context.Context, logger *slog.Logger) (err error) {
err = setupDNSFilteringConf(ctx, logger, config.Filtering)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
@@ -306,6 +307,8 @@ func initContextClients(logger *slog.Logger) (err error) {
}
return Context.clients.Init(
ctx,
logger,
config.Clients.Persistent,
Context.dhcpServer,
Context.etcHosts,
@@ -355,7 +358,11 @@ func setupBindOpts(opts options) (err error) {
}
// setupDNSFilteringConf sets up DNS filtering configuration settings.
func setupDNSFilteringConf(conf *filtering.Config) (err error) {
func setupDNSFilteringConf(
ctx context.Context,
baseLogger *slog.Logger,
conf *filtering.Config,
) (err error) {
const (
dnsTimeout = 3 * time.Second
@@ -446,12 +453,13 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
conf.ParentalBlockHost = host
}
conf.SafeSearch, err = safesearch.NewDefault(
conf.SafeSearchConf,
"default",
conf.SafeSearchCacheSize,
cacheTime,
)
logger := baseLogger.With(slogutil.KeyPrefix, safesearch.LogPrefix)
conf.SafeSearch, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: logger,
ServicesConfig: conf.SafeSearchConf,
CacheSize: conf.SafeSearchCacheSize,
CacheTTL: cacheTime,
})
if err != nil {
return fmt.Errorf("initializing safesearch: %w", err)
}
@@ -584,7 +592,10 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
// data first, but also to avoid relying on automatic Go init() function.
filtering.InitModule()
err = initContextClients(slogLogger)
// TODO(s.chzhen): Use it for the entire initialization process.
ctx := context.Background()
err = initContextClients(ctx, slogLogger)
fatalOnError(err)
err = setupOpts(opts)
@@ -632,7 +643,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
}
dataDir := Context.getDataDir()
err = os.MkdirAll(dataDir, aghos.DefaultPermDir)
err = aghos.MkdirAll(dataDir, aghos.DefaultPermDir)
fatalOnError(errors.Annotate(err, "creating DNS data dir at %s: %w", dataDir))
GLMode = opts.glinetMode

View File

@@ -24,10 +24,15 @@ func newSlogLogger(ls *logSettings) (l *slog.Logger) {
return slogutil.NewDiscardLogger()
}
lvl := slog.LevelInfo
if ls.Verbose {
lvl = slog.LevelDebug
}
return slogutil.New(&slogutil.Config{
Format: slogutil.FormatAdGuardLegacy,
Level: lvl,
AddTimestamp: true,
Verbose: ls.Verbose,
})
}