Pull request 2122: AG-27492-client-persistent-ids
Squashed commit of the following:
commit a0527b86f10596a86357630117607a3c507e4ac2
Merge: 512edaf2d 9694f19ef
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Wed Jan 17 13:15:18 2024 +0300
Merge branch 'master' into AG-27492-client-persistent-ids
commit 512edaf2dc29f19c4fb7860b0c350a5e4180cda4
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Mon Jan 15 15:50:28 2024 +0300
home: imp docs
commit 4d4b3599918aab8ee6315c7f2f35f70db89e4d02
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Thu Jan 11 20:20:42 2024 +0300
home: imp code
commit 8031347b8613cc49a80968e162dd198851eafe7c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Thu Jan 11 18:46:20 2024 +0300
home: fix typo
commit 5932b181fe6a0c0bc605070fd9ddcc6617703ab7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Thu Jan 11 16:52:49 2024 +0300
home: imp code more
commit 9412f5846795acfb68b009491b1045d1e27d8ddc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Thu Jan 11 15:41:23 2024 +0300
home: imp code
commit 855d3201ab1b176ed5fdd32bce933a7795601a6d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Wed Jan 10 20:24:49 2024 +0300
home: add tests
commit 112f1bd13acf992b0ba9562c29365b22d5374ec2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Fri Dec 29 18:29:11 2023 +0300
home: imp code
commit 8b295bfa8968c3767bcfaf05c7f109d75af8c961
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Fri Dec 29 14:58:17 2023 +0300
home: persistent client ids
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
@@ -218,7 +217,6 @@ func (o *clientObject) toPersistent(
|
||||
cli = &persistentClient{
|
||||
Name: o.Name,
|
||||
|
||||
IDs: o.IDs,
|
||||
Upstreams: o.Upstreams,
|
||||
|
||||
UID: o.UID,
|
||||
@@ -235,6 +233,11 @@ func (o *clientObject) toPersistent(
|
||||
UpstreamsCacheSize: o.UpstreamsCacheSize,
|
||||
}
|
||||
|
||||
err = cli.setIDs(o.IDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing ids: %w", err)
|
||||
}
|
||||
|
||||
if (cli.UID == UID{}) {
|
||||
cli.UID, err = NewUID()
|
||||
if err != nil {
|
||||
@@ -262,15 +265,7 @@ func (o *clientObject) toPersistent(
|
||||
|
||||
cli.BlockedServices = o.BlockedServices.Clone()
|
||||
|
||||
for _, t := range o.Tags {
|
||||
if allTags.Has(t) {
|
||||
cli.Tags = append(cli.Tags, t)
|
||||
} else {
|
||||
log.Info("skipping unknown tag %q", t)
|
||||
}
|
||||
}
|
||||
|
||||
slices.Sort(cli.Tags)
|
||||
cli.setTags(o.Tags, allTags)
|
||||
|
||||
return cli, nil
|
||||
}
|
||||
@@ -310,7 +305,7 @@ func (clients *clientsContainer) forConfig() (objs []*clientObject) {
|
||||
|
||||
BlockedServices: cli.BlockedServices.Clone(),
|
||||
|
||||
IDs: stringutil.CloneSlice(cli.IDs),
|
||||
IDs: cli.ids(),
|
||||
Tags: stringutil.CloneSlice(cli.Tags),
|
||||
Upstreams: stringutil.CloneSlice(cli.Upstreams),
|
||||
|
||||
@@ -449,7 +444,7 @@ func (clients *clientsContainer) find(id string) (c *persistentClient, ok bool)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return c.ShallowClone(), true
|
||||
return c.shallowClone(), true
|
||||
}
|
||||
|
||||
// shouldCountClient is a wrapper around [clientsContainer.find] to make it a
|
||||
@@ -534,13 +529,7 @@ func (clients *clientsContainer) findLocked(id string) (c *persistentClient, ok
|
||||
}
|
||||
|
||||
for _, c = range clients.list {
|
||||
for _, id := range c.IDs {
|
||||
var subnet netip.Prefix
|
||||
subnet, err = netip.ParsePrefix(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subnet := range c.Subnets {
|
||||
if subnet.Contains(ip) {
|
||||
return c, true
|
||||
}
|
||||
@@ -560,15 +549,9 @@ func (clients *clientsContainer) findDHCP(ip netip.Addr) (c *persistentClient, o
|
||||
}
|
||||
|
||||
for _, c = range clients.list {
|
||||
for _, id := range c.IDs {
|
||||
mac, err := net.ParseMAC(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.Equal(mac, foundMAC) {
|
||||
return c, true
|
||||
}
|
||||
_, found := slices.BinarySearchFunc(c.MACs, foundMAC, slices.Compare[net.HardwareAddr])
|
||||
if found {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,35 +591,26 @@ func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *client.Ru
|
||||
return rc, ok
|
||||
}
|
||||
|
||||
// check validates the client.
|
||||
// check validates the client. It also sorts the client tags.
|
||||
func (clients *clientsContainer) check(c *persistentClient) (err error) {
|
||||
switch {
|
||||
case c == nil:
|
||||
return errors.Error("client is nil")
|
||||
case c.Name == "":
|
||||
return errors.Error("invalid name")
|
||||
case len(c.IDs) == 0:
|
||||
case c.idsLen() == 0:
|
||||
return errors.Error("id required")
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
for i, id := range c.IDs {
|
||||
var norm string
|
||||
norm, err = normalizeClientIdentifier(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client at index %d: %w", i, err)
|
||||
}
|
||||
|
||||
c.IDs[i] = norm
|
||||
}
|
||||
|
||||
for _, t := range c.Tags {
|
||||
if !clients.allTags.Has(t) {
|
||||
return fmt.Errorf("invalid tag: %q", t)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(s.chzhen): Move to the constructor.
|
||||
slices.Sort(c.Tags)
|
||||
|
||||
err = dnsforward.ValidateUpstreams(c.Upstreams)
|
||||
@@ -647,35 +621,6 @@ func (clients *clientsContainer) check(c *persistentClient) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// normalizeClientIdentifier returns a normalized version of idStr. If idStr
|
||||
// cannot be normalized, it returns an error.
|
||||
func normalizeClientIdentifier(idStr string) (norm string, err error) {
|
||||
if idStr == "" {
|
||||
return "", errors.Error("clientid is empty")
|
||||
}
|
||||
|
||||
var ip netip.Addr
|
||||
if ip, err = netip.ParseAddr(idStr); err == nil {
|
||||
return ip.String(), nil
|
||||
}
|
||||
|
||||
var subnet netip.Prefix
|
||||
if subnet, err = netip.ParsePrefix(idStr); err == nil {
|
||||
return subnet.String(), nil
|
||||
}
|
||||
|
||||
var mac net.HardwareAddr
|
||||
if mac, err = net.ParseMAC(idStr); err == nil {
|
||||
return mac.String(), nil
|
||||
}
|
||||
|
||||
if err = dnsforward.ValidateClientID(idStr); err == nil {
|
||||
return strings.ToLower(idStr), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("bad client identifier %q", idStr)
|
||||
}
|
||||
|
||||
// add adds a new client object. ok is false if such client already exists or
|
||||
// if an error occurred.
|
||||
func (clients *clientsContainer) add(c *persistentClient) (ok bool, err error) {
|
||||
@@ -694,7 +639,8 @@ func (clients *clientsContainer) add(c *persistentClient) (ok bool, err error) {
|
||||
}
|
||||
|
||||
// check ID index
|
||||
for _, id := range c.IDs {
|
||||
ids := c.ids()
|
||||
for _, id := range ids {
|
||||
var c2 *persistentClient
|
||||
c2, ok = clients.idIndex[id]
|
||||
if ok {
|
||||
@@ -704,7 +650,7 @@ func (clients *clientsContainer) add(c *persistentClient) (ok bool, err error) {
|
||||
|
||||
clients.addLocked(c)
|
||||
|
||||
log.Debug("clients: added %q: ID:%q [%d]", c.Name, c.IDs, len(clients.list))
|
||||
log.Debug("clients: added %q: ID:%q [%d]", c.Name, ids, len(clients.list))
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -715,7 +661,7 @@ func (clients *clientsContainer) addLocked(c *persistentClient) {
|
||||
clients.list[c.Name] = c
|
||||
|
||||
// update ID index
|
||||
for _, id := range c.IDs {
|
||||
for _, id := range c.ids() {
|
||||
clients.idIndex[id] = c
|
||||
}
|
||||
}
|
||||
@@ -747,7 +693,7 @@ func (clients *clientsContainer) removeLocked(c *persistentClient) {
|
||||
delete(clients.list, c.Name)
|
||||
|
||||
// Update the ID index.
|
||||
for _, id := range c.IDs {
|
||||
for _, id := range c.ids() {
|
||||
delete(clients.idIndex, id)
|
||||
}
|
||||
}
|
||||
@@ -771,13 +717,18 @@ func (clients *clientsContainer) update(prev, c *persistentClient) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if c.equalIDs(prev) {
|
||||
clients.removeLocked(prev)
|
||||
clients.addLocked(c)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check the ID index.
|
||||
if !slices.Equal(prev.IDs, c.IDs) {
|
||||
for _, id := range c.IDs {
|
||||
existing, ok := clients.idIndex[id]
|
||||
if ok && existing != prev {
|
||||
return fmt.Errorf("id %q is used by client with name %q", id, existing.Name)
|
||||
}
|
||||
for _, id := range c.ids() {
|
||||
existing, ok := clients.idIndex[id]
|
||||
if ok && existing != prev {
|
||||
return fmt.Errorf("id %q is used by client with name %q", id, existing.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user