mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-04 01:07:49 +02:00
internal/clientinfo: add host_entries.conf parser
This commit is contained in:
committed by
Cuong Manh Le
parent
cfaf32f71a
commit
eaad24e5e5
@@ -1,8 +1,12 @@
|
|||||||
package clientinfo
|
package clientinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
@@ -12,9 +16,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ipv4LocalhostName = "localhost"
|
ipv4LocalhostName = "localhost"
|
||||||
ipv6LocalhostName = "ip6-localhost"
|
ipv6LocalhostName = "ip6-localhost"
|
||||||
ipv6LoopbackName = "ip6-loopback"
|
ipv6LoopbackName = "ip6-loopback"
|
||||||
|
hostEntriesConfPath = "/var/unbound/host_entries.conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// hostsFile provides client discovery functionality using system hosts file.
|
// hostsFile provides client discovery functionality using system hosts file.
|
||||||
@@ -34,14 +39,9 @@ func (hf *hostsFile) init() error {
|
|||||||
if err := hf.watcher.Add(hostsfile.HostsPath); err != nil {
|
if err := hf.watcher.Add(hostsfile.HostsPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m, err := hostsfile.ParseHosts(hostsfile.ReadHostsFile())
|
// Conservatively adding hostEntriesConfPath, since it is not available everywhere.
|
||||||
if err != nil {
|
_ = hf.watcher.Add(hostEntriesConfPath)
|
||||||
return err
|
return hf.refresh()
|
||||||
}
|
|
||||||
hf.mu.Lock()
|
|
||||||
hf.m = m
|
|
||||||
hf.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh reloads hosts file entries.
|
// refresh reloads hosts file entries.
|
||||||
@@ -52,6 +52,14 @@ func (hf *hostsFile) refresh() error {
|
|||||||
}
|
}
|
||||||
hf.mu.Lock()
|
hf.mu.Lock()
|
||||||
hf.m = m
|
hf.m = m
|
||||||
|
// override hosts file with host_entries.conf content if present.
|
||||||
|
hem, err := parseHostEntriesConf(hostEntriesConfPath)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
ctrld.ProxyLogger.Load().Debug().Err(err).Msg("could not read host_entries.conf file")
|
||||||
|
}
|
||||||
|
for k, v := range hem {
|
||||||
|
hf.m[k] = v
|
||||||
|
}
|
||||||
hf.mu.Unlock()
|
hf.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -137,3 +145,46 @@ func isLocalhostName(hostname string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseHostEntriesConf parses host_entries.conf file and returns parsed result.
|
||||||
|
func parseHostEntriesConf(path string) (map[string][]string, error) {
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseHostEntriesConfFromReader(bytes.NewReader(b)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseHostEntriesConfFromReader is like parseHostEntriesConf, but read from an io.Reader instead of file.
|
||||||
|
func parseHostEntriesConfFromReader(r io.Reader) map[string][]string {
|
||||||
|
hostsMap := map[string][]string{}
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
|
||||||
|
localZone := ""
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if after, found := strings.CutPrefix(line, "local-zone:"); found {
|
||||||
|
after = strings.TrimSpace(after)
|
||||||
|
fields := strings.Fields(after)
|
||||||
|
if len(fields) > 1 {
|
||||||
|
localZone = strings.Trim(fields[0], `""`)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Only read "local-data-ptr: ..." line, it has all necessary information.
|
||||||
|
after, found := strings.CutPrefix(line, "local-data-ptr:")
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
after = strings.TrimSpace(after)
|
||||||
|
after = strings.Trim(after, `"`)
|
||||||
|
fields := strings.Fields(after)
|
||||||
|
if len(fields) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip := fields[0]
|
||||||
|
name := strings.TrimSuffix(fields[1], "."+localZone)
|
||||||
|
hostsMap[ip] = append(hostsMap[ip], name)
|
||||||
|
}
|
||||||
|
return hostsMap
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package clientinfo
|
package clientinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,3 +32,46 @@ func Test_hostsFile_LookupHostnameByIP(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_parseHostEntriesConfFromReader(t *testing.T) {
|
||||||
|
const content = `local-zone: "localdomain" transparent
|
||||||
|
local-data-ptr: "127.0.0.1 localhost"
|
||||||
|
local-data: "localhost A 127.0.0.1"
|
||||||
|
local-data: "localhost.localdomain A 127.0.0.1"
|
||||||
|
local-data-ptr: "::1 localhost"
|
||||||
|
local-data: "localhost AAAA ::1"
|
||||||
|
local-data: "localhost.localdomain AAAA ::1"
|
||||||
|
local-data-ptr: "10.0.10.227 OPNsense.localdomain"
|
||||||
|
local-data: "OPNsense.localdomain A 10.0.10.227"
|
||||||
|
local-data: "OPNsense A 10.0.10.227"
|
||||||
|
local-data-ptr: "fe80::5a78:4e29:caa3:f9f7 OPNsense.localdomain"
|
||||||
|
local-data: "OPNsense.localdomain AAAA fe80::5a78:4e29:caa3:f9f7"
|
||||||
|
local-data: "OPNsense AAAA fe80::5a78:4e29:caa3:f9f7"
|
||||||
|
local-data-ptr: "1.1.1.1 banana-party.local.com"
|
||||||
|
local-data: "banana-party.local.com IN A 1.1.1.1"
|
||||||
|
local-data-ptr: "1.1.1.1 cheese-land.lan"
|
||||||
|
local-data: "cheese-land.lan IN A 1.1.1.1"
|
||||||
|
`
|
||||||
|
r := strings.NewReader(content)
|
||||||
|
hostsMap := parseHostEntriesConfFromReader(r)
|
||||||
|
if len(hostsMap) != 5 {
|
||||||
|
t.Fatalf("unexpected number of entries, want 5, got: %d", len(hostsMap))
|
||||||
|
}
|
||||||
|
for ip, names := range hostsMap {
|
||||||
|
switch ip {
|
||||||
|
case "1.1.1.1":
|
||||||
|
for _, name := range names {
|
||||||
|
if name != "banana-party.local.com" && name != "cheese-land.lan" {
|
||||||
|
t.Fatalf("unexpected names for 1.1.1.1: %v", names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "10.0.10.227":
|
||||||
|
if len(names) != 1 {
|
||||||
|
t.Fatalf("unexpected names for 10.0.10.227: %v", names)
|
||||||
|
}
|
||||||
|
if names[0] != "OPNsense" {
|
||||||
|
t.Fatalf("unexpected name: %s", names[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user