Pull request 1989: AG-24794-client-source

Squashed commit of the following:

commit 5cf83aafc8ddfea15b4f8e9b4061af021727f68c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Sep 1 14:58:03 2023 +0300

    client: imp code

commit 4325c8f610d1329ad38c800e20275668f1690f51
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Sep 1 14:38:33 2023 +0300

    all: add client source
This commit is contained in:
Stanislav Chzhen 2023-09-01 15:24:44 +03:00
parent 0182b9ec18
commit 905f9615a8
6 changed files with 93 additions and 86 deletions

View file

@ -77,8 +77,8 @@ func (n Neighbor) Clone() (clone Neighbor) {
}
}
// validatedHostname returns valid hostname. Otherwise returns empty string and
// logs the error if hostname is not valid.
// validatedHostname returns h if it's a valid hostname, or an empty string
// otherwise, logging the validation error.
func validatedHostname(h string) (host string) {
err := netutil.ValidateHostname(h)
if err != nil {

View file

@ -3,3 +3,52 @@
//
// TODO(a.garipov): Expand.
package client
import (
"encoding"
"fmt"
)
// Source represents the source from which the information about the client has
// been obtained.
type Source uint8
// Clients information sources. The order determines the priority.
const (
SourceNone Source = iota
SourceWHOIS
SourceARP
SourceRDNS
SourceDHCP
SourceHostsFile
SourcePersistent
)
// type check
var _ fmt.Stringer = Source(0)
// String returns a human-readable name of cs.
func (cs Source) String() (s string) {
switch cs {
case SourceWHOIS:
return "WHOIS"
case SourceARP:
return "ARP"
case SourceRDNS:
return "rDNS"
case SourceDHCP:
return "DHCP"
case SourceHostsFile:
return "etc/hosts"
default:
return ""
}
}
// type check
var _ encoding.TextMarshaler = Source(0)
// MarshalText implements encoding.TextMarshaler for the Source.
func (cs Source) MarshalText() (text []byte, err error) {
return []byte(cs.String()), nil
}

View file

@ -1,10 +1,10 @@
package home
import (
"encoding"
"fmt"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
@ -83,50 +83,6 @@ func (c *Client) setSafeSearch(
return nil
}
// clientSource represents the source from which the information about the
// client has been obtained.
type clientSource uint
// Clients information sources. The order determines the priority.
const (
ClientSourceNone clientSource = iota
ClientSourceWHOIS
ClientSourceARP
ClientSourceRDNS
ClientSourceDHCP
ClientSourceHostsFile
ClientSourcePersistent
)
// type check
var _ fmt.Stringer = clientSource(0)
// String returns a human-readable name of cs.
func (cs clientSource) String() (s string) {
switch cs {
case ClientSourceWHOIS:
return "WHOIS"
case ClientSourceARP:
return "ARP"
case ClientSourceRDNS:
return "rDNS"
case ClientSourceDHCP:
return "DHCP"
case ClientSourceHostsFile:
return "etc/hosts"
default:
return ""
}
}
// type check
var _ encoding.TextMarshaler = clientSource(0)
// MarshalText implements encoding.TextMarshaler for the clientSource.
func (cs clientSource) MarshalText() (text []byte, err error) {
return []byte(cs.String()), nil
}
// RuntimeClient is a client information about which has been obtained using the
// source described in the Source field.
type RuntimeClient struct {
@ -138,5 +94,5 @@ type RuntimeClient struct {
// Source is the source from which the information about the client has
// been obtained.
Source clientSource
Source client.Source
}

View file

@ -307,15 +307,15 @@ func (clients *clientsContainer) periodicUpdate() {
}
// clientSource checks if client with this IP address already exists and returns
// the source which updated it last. It returns [ClientSourceNone] if the
// the source which updated it last. It returns [client.SourceNone] if the
// client doesn't exist.
func (clients *clientsContainer) clientSource(ip netip.Addr) (src clientSource) {
func (clients *clientsContainer) clientSource(ip netip.Addr) (src client.Source) {
clients.lock.Lock()
defer clients.lock.Unlock()
_, ok := clients.findLocked(ip.String())
if ok {
return ClientSourcePersistent
return client.SourcePersistent
}
rc, ok := clients.ipToRC[ip]
@ -323,8 +323,8 @@ func (clients *clientsContainer) clientSource(ip netip.Addr) (src clientSource)
src = rc.Source
}
if src < ClientSourceDHCP && clients.dhcp.HostByIP(ip) != "" {
src = ClientSourceDHCP
if src < client.SourceDHCP && clients.dhcp.HostByIP(ip) != "" {
src = client.SourceDHCP
}
return src
@ -533,7 +533,7 @@ func (clients *clientsContainer) runtimeClient(ip netip.Addr) (rc *RuntimeClient
// findRuntimeClient finds a runtime client by their IP.
func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *RuntimeClient, ok bool) {
if rc, ok = clients.runtimeClient(ip); ok && rc.Source > ClientSourceDHCP {
if rc, ok = clients.runtimeClient(ip); ok && rc.Source > client.SourceDHCP {
return rc, ok
}
@ -544,7 +544,7 @@ func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *RuntimeCl
return &RuntimeClient{
Host: host,
Source: ClientSourceDHCP,
Source: client.SourceDHCP,
WHOIS: &whois.Info{},
}, true
}
@ -744,7 +744,7 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
// Create a RuntimeClient implicitly so that we don't do this check
// again.
rc = &RuntimeClient{
Source: ClientSourceWHOIS,
Source: client.SourceWHOIS,
}
clients.ipToRC[ip] = rc
@ -763,7 +763,7 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
func (clients *clientsContainer) addHost(
ip netip.Addr,
host string,
src clientSource,
src client.Source,
) (ok bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
@ -786,7 +786,7 @@ func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info
defer clients.lock.Unlock()
if host != "" {
ok := clients.addHostLocked(ip, host, ClientSourceRDNS)
ok := clients.addHostLocked(ip, host, client.SourceRDNS)
if !ok {
log.Debug("clients: host for client %q already set with higher priority source", ip)
}
@ -802,11 +802,11 @@ func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info
func (clients *clientsContainer) addHostLocked(
ip netip.Addr,
host string,
src clientSource,
src client.Source,
) (ok bool) {
rc, ok := clients.ipToRC[ip]
if !ok {
if src < ClientSourceDHCP {
if src < client.SourceDHCP {
if clients.dhcp.HostByIP(ip) != "" {
return false
}
@ -829,7 +829,7 @@ func (clients *clientsContainer) addHostLocked(
}
// rmHostsBySrc removes all entries that match the specified source.
func (clients *clientsContainer) rmHostsBySrc(src clientSource) {
func (clients *clientsContainer) rmHostsBySrc(src client.Source) {
n := 0
for ip, rc := range clients.ipToRC {
if rc.Source == src {
@ -847,7 +847,7 @@ func (clients *clientsContainer) addFromHostsFile(hosts aghnet.Hosts) {
clients.lock.Lock()
defer clients.lock.Unlock()
clients.rmHostsBySrc(ClientSourceHostsFile)
clients.rmHostsBySrc(client.SourceHostsFile)
n := 0
for addr, rec := range hosts {
@ -855,7 +855,7 @@ func (clients *clientsContainer) addFromHostsFile(hosts aghnet.Hosts) {
// hostname for the IP address.
//
// TODO(e.burkov): Consider using all the names from all the records.
clients.addHostLocked(addr, rec[0].Names[0], ClientSourceHostsFile)
clients.addHostLocked(addr, rec[0].Names[0], client.SourceHostsFile)
n++
}
@ -883,11 +883,11 @@ func (clients *clientsContainer) addFromSystemARP() {
clients.lock.Lock()
defer clients.lock.Unlock()
clients.rmHostsBySrc(ClientSourceARP)
clients.rmHostsBySrc(client.SourceARP)
added := 0
for _, n := range ns {
if clients.addHostLocked(n.IP, n.Name, ClientSourceARP) {
if clients.addHostLocked(n.IP, n.Name, client.SourceARP) {
added++
}
}

View file

@ -7,6 +7,7 @@ import (
"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"
@ -99,9 +100,9 @@ func TestClients(t *testing.T) {
assert.Equal(t, "client2", c.Name)
assert.Equal(t, clients.clientSource(cliNoneIP), ClientSourceNone)
assert.Equal(t, clients.clientSource(cli1IP), ClientSourcePersistent)
assert.Equal(t, clients.clientSource(cli2IP), ClientSourcePersistent)
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) {
@ -148,8 +149,8 @@ func TestClients(t *testing.T) {
})
require.NoError(t, err)
assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
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)
@ -181,7 +182,7 @@ func TestClients(t *testing.T) {
ok := clients.Del("client1-renamed")
require.True(t, ok)
assert.Equal(t, clients.clientSource(netip.MustParseAddr("1.1.1.2")), ClientSourceNone)
assert.Equal(t, clients.clientSource(netip.MustParseAddr("1.1.1.2")), client.SourceNone)
})
t.Run("del_fail", func(t *testing.T) {
@ -191,32 +192,32 @@ func TestClients(t *testing.T) {
t.Run("addhost_success", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
ok := clients.addHost(ip, "host", ClientSourceARP)
ok := clients.addHost(ip, "host", client.SourceARP)
assert.True(t, ok)
ok = clients.addHost(ip, "host2", ClientSourceARP)
ok = clients.addHost(ip, "host2", client.SourceARP)
assert.True(t, ok)
ok = clients.addHost(ip, "host3", ClientSourceHostsFile)
ok = clients.addHost(ip, "host3", client.SourceHostsFile)
assert.True(t, ok)
assert.Equal(t, clients.clientSource(ip), ClientSourceHostsFile)
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", ClientSourceARP)
ok := clients.addHost(ip, "from_arp", client.SourceARP)
assert.True(t, ok)
assert.Equal(t, clients.clientSource(ip), ClientSourceARP)
assert.Equal(t, clients.clientSource(ip), client.SourceARP)
ok = clients.addHost(ip, "from_dhcp", ClientSourceDHCP)
ok = clients.addHost(ip, "from_dhcp", client.SourceDHCP)
assert.True(t, ok)
assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
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", ClientSourceRDNS)
ok := clients.addHost(ip, "host1", client.SourceRDNS)
assert.False(t, ok)
})
}
@ -239,7 +240,7 @@ func TestClientsWHOIS(t *testing.T) {
t.Run("existing_auto-client", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
ok := clients.addHost(ip, "host", ClientSourceRDNS)
ok := clients.addHost(ip, "host", client.SourceRDNS)
assert.True(t, ok)
clients.setWHOISInfo(ip, whois)
@ -282,7 +283,7 @@ func TestClientsAddExisting(t *testing.T) {
assert.True(t, ok)
// Now add an auto-client with the same IP.
ok = clients.addHost(ip, "test", ClientSourceRDNS)
ok = clients.addHost(ip, "test", client.SourceRDNS)
assert.True(t, ok)
})

View file

@ -8,6 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
@ -88,9 +89,9 @@ func (j *clientJSON) copySettings(
type runtimeClientJSON struct {
WHOIS *whois.Info `json:"whois_info"`
IP netip.Addr `json:"ip"`
Name string `json:"name"`
Source clientSource `json:"source"`
IP netip.Addr `json:"ip"`
Name string `json:"name"`
Source client.Source `json:"source"`
}
type clientListJSON struct {
@ -126,7 +127,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
for _, l := range clients.dhcp.Leases() {
cj := runtimeClientJSON{
Name: l.Hostname,
Source: ClientSourceDHCP,
Source: client.SourceDHCP,
IP: l.IP,
WHOIS: &whois.Info{},
}