AdGuardHome/internal/home/clients_internal_test.go
Dimitry Kolyshev 388583cefe Pull request: custom upstreams cache
Merge in DNS/adguard-home from custom-ups-cache to master

Squashed commit of the following:

commit 98428a87520f70cb522701d8eccfe4c529be1e40
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 17 10:53:32 2023 +0200

    all: upd dep

commit 775a639af4a2a45220b17e8b0037edc126ff62e4
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 17 09:52:31 2023 +0200

    dnsforward: imp test

commit e9e2a58b48e8588dfcb28df319d4651e1fe77af5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 17 09:44:46 2023 +0200

    docs: changelog

commit a6d67218f037c8fec29e5fa2967476d63c3cfc32
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 17 09:37:17 2023 +0200

    all: upd dep

commit b101ff6e0cf393dacdee6fb68d33ba8f11c36280
Merge: d61f4eb88 8bb1aad73
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Nov 16 15:54:05 2023 +0200

    Merge remote-tracking branch 'origin/master' into custom-ups-cache

commit d61f4eb8871f8ae8504259998bf9015b29001cfb
Merge: 567a8a4af fdf60eeed
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Nov 13 10:32:22 2023 +0200

    Merge remote-tracking branch 'origin/master' into custom-ups-cache

commit 567a8a4af34ad001d0e6d7d2efdc123205569e8c
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Nov 13 10:30:24 2023 +0200

    home: imp code

commit a3c16facbebc166e5c0c731c1e892b61c0950d9e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 10 14:34:04 2023 +0200

    all: imp code

commit 84160eafee1d0f2d0cd3f025f2d5070e4f597ad6
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 10 14:31:26 2023 +0200

    all: conf custom ups cache

commit b7f6581901ebad96c87e765a305a1fa5b336efbb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 10 14:29:47 2023 +0200

    all: conf custom ups cache

commit d07df945d4e7614a679ef5dc77756096abf1e66c
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 10 09:26:29 2023 +0200

    all: docs

commit 998124bac08889c7d354dd1a099929726725bccc
Merge: f665e2f85 53170d871
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 10 09:24:28 2023 +0200

    Merge remote-tracking branch 'origin/master' into custom-ups-cache

commit f665e2f85bce12d95f80aba6614b6bfd4874b122
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Nov 10 09:22:46 2023 +0200

    all: conf custom ups cache

commit a4b26973bef4f3b339198ffbe52a50baca303daf
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Nov 9 12:46:39 2023 +0200

    all: conf custom ups cache
2023-11-17 15:51:51 +03:00

365 lines
8.8 KiB
Go

package home
import (
"net"
"net/netip"
"runtime"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type testDHCP struct {
OnLeases func() (leases []*dhcpsvc.Lease)
OnHostBy func(ip netip.Addr) (host string)
OnMACBy func(ip netip.Addr) (mac net.HardwareAddr)
}
// Lease implements the [DHCP] interface for testDHCP.
func (t *testDHCP) Leases() (leases []*dhcpsvc.Lease) { return t.OnLeases() }
// HostByIP implements the [DHCP] interface for testDHCP.
func (t *testDHCP) HostByIP(ip netip.Addr) (host string) { return t.OnHostBy(ip) }
// MACByIP implements the [DHCP] interface for testDHCP.
func (t *testDHCP) MACByIP(ip netip.Addr) (mac net.HardwareAddr) { return t.OnMACBy(ip) }
// newClientsContainer is a helper that creates a new clients container for
// tests.
func newClientsContainer(t *testing.T) (c *clientsContainer) {
t.Helper()
c = &clientsContainer{
testing: true,
}
dhcp := &testDHCP{
OnLeases: func() (leases []*dhcpsvc.Lease) { panic("not implemented") },
OnHostBy: func(ip netip.Addr) (host string) { return "" },
OnMACBy: func(ip netip.Addr) (mac net.HardwareAddr) { return nil },
}
require.NoError(t, c.Init(nil, dhcp, nil, nil, &filtering.Config{}))
return c
}
func TestClients(t *testing.T) {
clients := newClientsContainer(t)
t.Run("add_success", func(t *testing.T) {
var (
cliNone = "1.2.3.4"
cli1 = "1.1.1.1"
cli2 = "2.2.2.2"
cliNoneIP = netip.MustParseAddr(cliNone)
cli1IP = netip.MustParseAddr(cli1)
cli2IP = netip.MustParseAddr(cli2)
)
c := &Client{
IDs: []string{cli1, "1:2:3::4", "aa:aa:aa:aa:aa:aa"},
Name: "client1",
}
ok, err := clients.Add(c)
require.NoError(t, err)
assert.True(t, ok)
c = &Client{
IDs: []string{cli2},
Name: "client2",
}
ok, err = clients.Add(c)
require.NoError(t, err)
assert.True(t, ok)
c, ok = clients.Find(cli1)
require.True(t, ok)
assert.Equal(t, "client1", c.Name)
c, ok = clients.Find("1:2:3::4")
require.True(t, ok)
assert.Equal(t, "client1", c.Name)
c, ok = clients.Find(cli2)
require.True(t, ok)
assert.Equal(t, "client2", c.Name)
assert.Equal(t, clients.clientSource(cliNoneIP), client.SourceNone)
assert.Equal(t, clients.clientSource(cli1IP), client.SourcePersistent)
assert.Equal(t, clients.clientSource(cli2IP), client.SourcePersistent)
})
t.Run("add_fail_name", func(t *testing.T) {
ok, err := clients.Add(&Client{
IDs: []string{"1.2.3.5"},
Name: "client1",
})
require.NoError(t, err)
assert.False(t, ok)
})
t.Run("add_fail_ip", func(t *testing.T) {
ok, err := clients.Add(&Client{
IDs: []string{"2.2.2.2"},
Name: "client3",
})
require.Error(t, err)
assert.False(t, ok)
})
t.Run("update_fail_ip", func(t *testing.T) {
err := clients.Update(&Client{Name: "client1"}, &Client{
IDs: []string{"2.2.2.2"},
Name: "client1",
})
assert.Error(t, err)
})
t.Run("update_success", func(t *testing.T) {
var (
cliOld = "1.1.1.1"
cliNew = "1.1.1.2"
cliOldIP = netip.MustParseAddr(cliOld)
cliNewIP = netip.MustParseAddr(cliNew)
)
prev, ok := clients.list["client1"]
require.True(t, ok)
err := clients.Update(prev, &Client{
IDs: []string{cliNew},
Name: "client1",
})
require.NoError(t, err)
assert.Equal(t, clients.clientSource(cliOldIP), client.SourceNone)
assert.Equal(t, clients.clientSource(cliNewIP), client.SourcePersistent)
prev, ok = clients.list["client1"]
require.True(t, ok)
err = clients.Update(prev, &Client{
IDs: []string{cliNew},
Name: "client1-renamed",
UseOwnSettings: true,
})
require.NoError(t, err)
c, ok := clients.Find(cliNew)
require.True(t, ok)
assert.Equal(t, "client1-renamed", c.Name)
assert.True(t, c.UseOwnSettings)
nilCli, ok := clients.list["client1"]
require.False(t, ok)
assert.Nil(t, nilCli)
require.Len(t, c.IDs, 1)
assert.Equal(t, cliNew, c.IDs[0])
})
t.Run("del_success", func(t *testing.T) {
ok := clients.Del("client1-renamed")
require.True(t, ok)
assert.Equal(t, clients.clientSource(netip.MustParseAddr("1.1.1.2")), client.SourceNone)
})
t.Run("del_fail", func(t *testing.T) {
ok := clients.Del("client3")
assert.False(t, ok)
})
t.Run("addhost_success", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
ok := clients.addHost(ip, "host", client.SourceARP)
assert.True(t, ok)
ok = clients.addHost(ip, "host2", client.SourceARP)
assert.True(t, ok)
ok = clients.addHost(ip, "host3", client.SourceHostsFile)
assert.True(t, ok)
assert.Equal(t, clients.clientSource(ip), client.SourceHostsFile)
})
t.Run("dhcp_replaces_arp", func(t *testing.T) {
ip := netip.MustParseAddr("1.2.3.4")
ok := clients.addHost(ip, "from_arp", client.SourceARP)
assert.True(t, ok)
assert.Equal(t, clients.clientSource(ip), client.SourceARP)
ok = clients.addHost(ip, "from_dhcp", client.SourceDHCP)
assert.True(t, ok)
assert.Equal(t, clients.clientSource(ip), client.SourceDHCP)
})
t.Run("addhost_fail", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
ok := clients.addHost(ip, "host1", client.SourceRDNS)
assert.False(t, ok)
})
}
func TestClientsWHOIS(t *testing.T) {
clients := newClientsContainer(t)
whois := &whois.Info{
Country: "AU",
Orgname: "Example Org",
}
t.Run("new_client", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.255")
clients.setWHOISInfo(ip, whois)
rc := clients.ipToRC[ip]
require.NotNil(t, rc)
assert.Equal(t, rc.WHOIS, whois)
})
t.Run("existing_auto-client", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
ok := clients.addHost(ip, "host", client.SourceRDNS)
assert.True(t, ok)
clients.setWHOISInfo(ip, whois)
rc := clients.ipToRC[ip]
require.NotNil(t, rc)
assert.Equal(t, rc.WHOIS, whois)
})
t.Run("can't_set_manually-added", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.2")
ok, err := clients.Add(&Client{
IDs: []string{"1.1.1.2"},
Name: "client1",
})
require.NoError(t, err)
assert.True(t, ok)
clients.setWHOISInfo(ip, whois)
rc := clients.ipToRC[ip]
require.Nil(t, rc)
assert.True(t, clients.Del("client1"))
})
}
func TestClientsAddExisting(t *testing.T) {
clients := newClientsContainer(t)
t.Run("simple", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
// Add a client.
ok, err := clients.Add(&Client{
IDs: []string{ip.String(), "1:2:3::4", "aa:aa:aa:aa:aa:aa", "2.2.2.0/24"},
Name: "client1",
})
require.NoError(t, err)
assert.True(t, ok)
// Now add an auto-client with the same IP.
ok = clients.addHost(ip, "test", client.SourceRDNS)
assert.True(t, ok)
})
t.Run("complicated", func(t *testing.T) {
// TODO(a.garipov): Properly decouple the DHCP server from the client
// storage.
if runtime.GOOS == "windows" {
t.Skip("skipping dhcp test on windows")
}
ip := netip.MustParseAddr("1.2.3.4")
// First, init a DHCP server with a single static lease.
config := &dhcpd.ServerConfig{
Enabled: true,
DataDir: t.TempDir(),
Conf4: dhcpd.V4ServerConf{
Enabled: true,
GatewayIP: netip.MustParseAddr("1.2.3.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("1.2.3.2"),
RangeEnd: netip.MustParseAddr("1.2.3.10"),
},
}
dhcpServer, err := dhcpd.Create(config)
require.NoError(t, err)
clients.dhcp = dhcpServer
err = dhcpServer.AddStaticLease(&dhcpsvc.Lease{
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: ip,
Hostname: "testhost",
Expiry: time.Now().Add(time.Hour),
})
require.NoError(t, err)
// Add a new client with the same IP as for a client with MAC.
ok, err := clients.Add(&Client{
IDs: []string{ip.String()},
Name: "client2",
})
require.NoError(t, err)
assert.True(t, ok)
// Add a new client with the IP from the first client's IP range.
ok, err = clients.Add(&Client{
IDs: []string{"2.2.2.2"},
Name: "client3",
})
require.NoError(t, err)
assert.True(t, ok)
})
}
func TestClientsCustomUpstream(t *testing.T) {
clients := newClientsContainer(t)
// Add client with upstreams.
ok, err := clients.Add(&Client{
IDs: []string{"1.1.1.1", "1:2:3::4", "aa:aa:aa:aa:aa:aa"},
Name: "client1",
Upstreams: []string{
"1.1.1.1",
"[/example.org/]8.8.8.8",
},
})
require.NoError(t, err)
assert.True(t, ok)
upsConf, err := clients.UpstreamConfigByID("1.2.3.4", net.DefaultResolver)
assert.Nil(t, upsConf)
assert.NoError(t, err)
upsConf, err = clients.UpstreamConfigByID("1.1.1.1", net.DefaultResolver)
require.NotNil(t, upsConf)
assert.NoError(t, err)
}