Merge: + clients: parse 'arp -a' output; periodically update info
Close #826 * commit 'db7efc24d381f6c8d88e14f485475e812ff5fb7b': + clients: parse 'arp -a' output; periodically update info
This commit is contained in:
commit
c631a6832f
|
@ -7,11 +7,18 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/golibs/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
clientsUpdatePeriod = 1 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client information
|
// Client information
|
||||||
|
@ -40,8 +47,10 @@ type clientJSON struct {
|
||||||
type clientSource uint
|
type clientSource uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ClientSourceHostsFile clientSource = 0 // from /etc/hosts
|
// Priority: etc/hosts > ARP > rDNS
|
||||||
ClientSourceRDNS clientSource = 1 // from rDNS
|
ClientSourceRDNS clientSource = 0 // from rDNS
|
||||||
|
ClientSourceARP clientSource = 1 // from 'arp -a'
|
||||||
|
ClientSourceHostsFile clientSource = 2 // from /etc/hosts
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientHost information
|
// ClientHost information
|
||||||
|
@ -68,7 +77,15 @@ func clientsInit() {
|
||||||
clients.ipIndex = make(map[string]*Client)
|
clients.ipIndex = make(map[string]*Client)
|
||||||
clients.ipHost = make(map[string]ClientHost)
|
clients.ipHost = make(map[string]ClientHost)
|
||||||
|
|
||||||
|
go periodicClientsUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func periodicClientsUpdate() {
|
||||||
|
for {
|
||||||
clientsAddFromHostsFile()
|
clientsAddFromHostsFile()
|
||||||
|
clientsAddFromSystemARP()
|
||||||
|
time.Sleep(clientsUpdatePeriod)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func clientsGetList() map[string]*Client {
|
func clientsGetList() map[string]*Client {
|
||||||
|
@ -240,13 +257,16 @@ func clientUpdate(name string, c Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add new IP -> Host pair
|
||||||
|
// Use priority of the source (etc/hosts > ARP > rDNS)
|
||||||
|
// so we overwrite existing entries with an equal or higher priority
|
||||||
func clientAddHost(ip, host string, source clientSource) (bool, error) {
|
func clientAddHost(ip, host string, source clientSource) (bool, error) {
|
||||||
clients.lock.Lock()
|
clients.lock.Lock()
|
||||||
defer clients.lock.Unlock()
|
defer clients.lock.Unlock()
|
||||||
|
|
||||||
// check index
|
// check index
|
||||||
_, ok := clients.ipHost[ip]
|
c, ok := clients.ipHost[ip]
|
||||||
if ok {
|
if ok && c.Source > source {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +274,7 @@ func clientAddHost(ip, host string, source clientSource) (bool, error) {
|
||||||
Host: host,
|
Host: host,
|
||||||
Source: source,
|
Source: source,
|
||||||
}
|
}
|
||||||
log.Tracef("'%s': '%s' -> [%d]", host, ip, len(clients.ipHost))
|
log.Tracef("'%s' -> '%s' [%d]", ip, host, len(clients.ipHost))
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,6 +316,52 @@ func clientsAddFromHostsFile() {
|
||||||
log.Info("Added %d client aliases from %s", n, hostsFn)
|
log.Info("Added %d client aliases from %s", n, hostsFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add IP -> Host pairs from the system's `arp -a` command output
|
||||||
|
// The command's output is:
|
||||||
|
// HOST (IP) at MAC on IFACE
|
||||||
|
func clientsAddFromSystemARP() {
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("arp", "-a")
|
||||||
|
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil || cmd.ProcessState.ExitCode() != 0 {
|
||||||
|
log.Debug("command %s has failed: %v code:%d",
|
||||||
|
cmd.Path, err, cmd.ProcessState.ExitCode())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
for _, ln := range lines {
|
||||||
|
|
||||||
|
open := strings.Index(ln, " (")
|
||||||
|
close := strings.Index(ln, ") ")
|
||||||
|
if open == -1 || close == -1 || open >= close {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
host := ln[:open]
|
||||||
|
ip := ln[open+2 : close]
|
||||||
|
if utils.IsValidHostname(host) != nil || net.ParseIP(ip) == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, e := clientAddHost(ip, host, ClientSourceARP)
|
||||||
|
if e != nil {
|
||||||
|
log.Tracef("%s", e)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Added %d client aliases from 'arp -a' command output", n)
|
||||||
|
}
|
||||||
|
|
||||||
type clientHostJSON struct {
|
type clientHostJSON struct {
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -342,8 +408,11 @@ func handleGetClients(w http.ResponseWriter, r *http.Request) {
|
||||||
Name: ch.Host,
|
Name: ch.Host,
|
||||||
}
|
}
|
||||||
cj.Source = "etc/hosts"
|
cj.Source = "etc/hosts"
|
||||||
if ch.Source == ClientSourceRDNS {
|
switch ch.Source {
|
||||||
|
case ClientSourceRDNS:
|
||||||
cj.Source = "rDNS"
|
cj.Source = "rDNS"
|
||||||
|
case ClientSourceARP:
|
||||||
|
cj.Source = "ARP"
|
||||||
}
|
}
|
||||||
data.AutoClients = append(data.AutoClients, cj)
|
data.AutoClients = append(data.AutoClients, cj)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,17 +104,29 @@ func TestClients(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add host client
|
// add host client
|
||||||
b, e = clientAddHost("1.1.1.1", "host", ClientSourceHostsFile)
|
b, e = clientAddHost("1.1.1.1", "host", ClientSourceARP)
|
||||||
if !b || e != nil {
|
if !b || e != nil {
|
||||||
t.Fatalf("clientAddHost")
|
t.Fatalf("clientAddHost")
|
||||||
}
|
}
|
||||||
|
|
||||||
// failed add - ip exists
|
// failed add - ip exists
|
||||||
b, e = clientAddHost("1.1.1.1", "host", ClientSourceHostsFile)
|
b, e = clientAddHost("1.1.1.1", "host1", ClientSourceRDNS)
|
||||||
if b || e != nil {
|
if b || e != nil {
|
||||||
t.Fatalf("clientAddHost - ip exists")
|
t.Fatalf("clientAddHost - ip exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overwrite with new data
|
||||||
|
b, e = clientAddHost("1.1.1.1", "host2", ClientSourceARP)
|
||||||
|
if !b || e != nil {
|
||||||
|
t.Fatalf("clientAddHost - overwrite with new data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// overwrite with new data (higher priority)
|
||||||
|
b, e = clientAddHost("1.1.1.1", "host3", ClientSourceHostsFile)
|
||||||
|
if !b || e != nil {
|
||||||
|
t.Fatalf("clientAddHost - overwrite with new data (higher priority)")
|
||||||
|
}
|
||||||
|
|
||||||
// get
|
// get
|
||||||
if !clientExists("1.1.1.1") {
|
if !clientExists("1.1.1.1") {
|
||||||
t.Fatalf("clientAddHost")
|
t.Fatalf("clientAddHost")
|
||||||
|
|
Loading…
Reference in New Issue