Skip to content

Commit

Permalink
all: add client runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
schzhn committed Nov 22, 2023
1 parent f28dcef commit e84f176
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 49 deletions.
76 changes: 76 additions & 0 deletions internal/client/client.go
Expand Up @@ -7,6 +7,8 @@ package client
import (
"encoding"
"fmt"

"github.com/AdguardTeam/AdGuardHome/internal/whois"
)

// Source represents the source from which the information about the client has
Expand Down Expand Up @@ -52,3 +54,77 @@ var _ encoding.TextMarshaler = Source(0)
func (cs Source) MarshalText() (text []byte, err error) {
return []byte(cs.String()), nil
}

// Runtime is a client information from different sources.
type Runtime struct {
// WHOIS is the filtered WHOIS data of a client.
WHOIS *whois.Info

// ARP is the ARP data of a client.
ARP string

// RDNS is the RDNS data of a client.
RDNS string

// DHCP is the DHCP data of a client.
DHCP string

// HostsFile is the data from the hosts file.
HostsFile string
}

// Info returns client information with highest priority.
func (r *Runtime) Info() (cs Source, host string) {
if r == nil {
return SourceNone, ""
}

switch {
case r.HostsFile != "":
return SourceHostsFile, r.HostsFile
case r.DHCP != "":
return SourceDHCP, r.DHCP
case r.RDNS != "":
return SourceRDNS, r.RDNS
case r.ARP != "":
return SourceARP, r.ARP
}

return SourceNone, ""
}

// SetInfo sets client information.
func (r *Runtime) SetInfo(cs Source, host string) {
if host == "" {
host = "–"
}

switch cs {
case SourceARP:
r.ARP = host
case SourceRDNS:
r.RDNS = host
case SourceDHCP:
r.DHCP = host
case SourceHostsFile:
r.HostsFile = host
}
}

// IsSet returns true if a cs data is present.
func (r *Runtime) IsSet(cs Source) (ok bool) {
switch cs {
case SourceWHOIS:
return r.WHOIS != nil
case SourceARP:
return r.ARP != ""
case SourceRDNS:
return r.RDNS != ""
case SourceDHCP:
return r.DHCP != ""
case SourceHostsFile:
return r.HostsFile != ""
}

return false
}
16 changes: 0 additions & 16 deletions internal/home/client.go
Expand Up @@ -4,10 +4,8 @@ import (
"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"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/stringutil"
)
Expand Down Expand Up @@ -84,17 +82,3 @@ func (c *Client) setSafeSearch(

return nil
}

// RuntimeClient is a client information about which has been obtained using the
// source described in the Source field.
type RuntimeClient struct {
// WHOIS is the filtered WHOIS data of a client.
WHOIS *whois.Info

// Host is the host name of a client.
Host string

// Source is the source from which the information about the client has
// been obtained.
Source client.Source
}
51 changes: 25 additions & 26 deletions internal/home/clients.go
Expand Up @@ -50,8 +50,8 @@ type clientsContainer struct {
list map[string]*Client // name -> client
idIndex map[string]*Client // ID -> client

// ipToRC is the IP address to *RuntimeClient map.
ipToRC map[netip.Addr]*RuntimeClient
// ipToRC is a map where key is IP address and value is *client.Runtime.
ipToRC map[netip.Addr]*client.Runtime

allTags *stringutil.Set

Expand Down Expand Up @@ -104,7 +104,7 @@ func (clients *clientsContainer) Init(

clients.list = make(map[string]*Client)
clients.idIndex = make(map[string]*Client)
clients.ipToRC = map[netip.Addr]*RuntimeClient{}
clients.ipToRC = make(map[netip.Addr]*client.Runtime)

clients.allTags = stringutil.NewSet(clientTags...)

Expand Down Expand Up @@ -338,7 +338,7 @@ func (clients *clientsContainer) clientSource(ip netip.Addr) (src client.Source)

rc, ok := clients.ipToRC[ip]
if ok {
src = rc.Source
src, _ = rc.Info()
}

if src < client.SourceDHCP && clients.dhcp.HostByIP(ip) != "" {
Expand Down Expand Up @@ -385,19 +385,21 @@ func (clients *clientsContainer) clientOrArtificial(
}
}()

client, ok := clients.Find(id)
cli, ok := clients.Find(id)
if ok {
return &querylog.Client{
Name: client.Name,
IgnoreQueryLog: client.IgnoreQueryLog,
Name: cli.Name,
IgnoreQueryLog: cli.IgnoreQueryLog,
}, false
}

var rc *RuntimeClient
var rc *client.Runtime
rc, ok = clients.findRuntimeClient(ip)
if ok {
_, host := rc.Info()

return &querylog.Client{
Name: rc.Host,
Name: host,
WHOIS: rc.WHOIS,
}, false
}
Expand Down Expand Up @@ -545,7 +547,7 @@ func (clients *clientsContainer) findDHCP(ip netip.Addr) (c *Client, ok bool) {

// runtimeClient returns a runtime client from internal index. Note that it
// doesn't include DHCP clients.
func (clients *clientsContainer) runtimeClient(ip netip.Addr) (rc *RuntimeClient, ok bool) {
func (clients *clientsContainer) runtimeClient(ip netip.Addr) (rc *client.Runtime, ok bool) {
if ip == (netip.Addr{}) {
return nil, false
}
Expand All @@ -559,8 +561,10 @@ 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 > client.SourceDHCP {
func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *client.Runtime, ok bool) {
rc, ok = clients.runtimeClient(ip)
src, _ := rc.Info()
if src > client.SourceDHCP {
return rc, ok
}

Expand All @@ -569,10 +573,9 @@ func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *RuntimeCl
return rc, ok
}

return &RuntimeClient{
Host: host,
Source: client.SourceDHCP,
WHOIS: &whois.Info{},
return &client.Runtime{
DHCP: host,
WHOIS: &whois.Info{},
}, true
}

Expand Down Expand Up @@ -770,14 +773,13 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
if !ok {
// Create a RuntimeClient implicitly so that we don't do this check
// again.
rc = &RuntimeClient{
Source: client.SourceWHOIS,
}
rc = &client.Runtime{}
clients.ipToRC[ip] = rc

log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi)
} else {
log.Debug("clients: set whois info for runtime client %s: %+v", rc.Host, wi)
host, _ := rc.Info()
log.Debug("clients: set whois info for runtime client %s: %+v", host, wi)
}

rc.WHOIS = wi
Expand Down Expand Up @@ -839,16 +841,13 @@ func (clients *clientsContainer) addHostLocked(
}
}

rc = &RuntimeClient{
rc = &client.Runtime{
WHOIS: &whois.Info{},
}
clients.ipToRC[ip] = rc
} else if src < rc.Source {
return false
}

rc.Host = host
rc.Source = src
rc.SetInfo(src, host)

log.Debug("clients: added %s -> %q [%d]", ip, host, len(clients.ipToRC))

Expand All @@ -859,7 +858,7 @@ func (clients *clientsContainer) addHostLocked(
func (clients *clientsContainer) rmHostsBySrc(src client.Source) {
n := 0
for ip, rc := range clients.ipToRC {
if rc.Source == src {
if rc.IsSet(src) {
delete(clients.ipToRC, ip)
n++
}
Expand Down
6 changes: 4 additions & 2 deletions internal/home/clients_internal_test.go
Expand Up @@ -215,10 +215,12 @@ func TestClients(t *testing.T) {
assert.Equal(t, clients.clientSource(ip), client.SourceDHCP)
})

t.Run("addhost_fail", func(t *testing.T) {
t.Run("addhost_priority", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
ok := clients.addHost(ip, "host1", client.SourceRDNS)
assert.False(t, ok)
assert.True(t, ok)

assert.Equal(t, clients.clientSource(ip), client.SourceHostsFile)
})
}

Expand Down
11 changes: 6 additions & 5 deletions internal/home/clientshttp.go
Expand Up @@ -113,11 +113,11 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
}

for ip, rc := range clients.ipToRC {
src, host := rc.Info()
cj := runtimeClientJSON{
WHOIS: rc.WHOIS,

Name: rc.Host,
Source: rc.Source,
WHOIS: rc.WHOIS,
Name: host,
Source: src,
IP: ip,
}

Expand Down Expand Up @@ -409,8 +409,9 @@ func (clients *clientsContainer) findRuntime(ip netip.Addr, idStr string) (cj *c
return cj
}

_, host := rc.Info()
cj = &clientJSON{
Name: rc.Host,
Name: host,
IDs: []string{idStr},
WHOIS: rc.WHOIS,
}
Expand Down

0 comments on commit e84f176

Please sign in to comment.