Files
ctrld/internal/clientinfo/hostsfile.go
Cuong Manh Le 9d2ea15346 internal/clientinfo: ignoring localhost entry for hostsfile mapping
Otherwise, actual hostname will be overriden with "localhost", which is
rather confusing/bad for UX.
2023-10-10 22:04:59 +07:00

121 lines
2.8 KiB
Go

package clientinfo
import (
"os"
"sync"
"github.com/fsnotify/fsnotify"
"github.com/jaytaylor/go-hostsfile"
"github.com/Control-D-Inc/ctrld"
)
const (
ipv4LocalhostName = "localhost"
ipv6LocalhostName = "ip6-localhost"
ipv6LoopbackName = "ip6-loopback"
)
// hostsFile provides client discovery functionality using system hosts file.
type hostsFile struct {
watcher *fsnotify.Watcher
mu sync.Mutex
m map[string][]string
}
// init performs initialization works, which is necessary before hostsFile can be fully operated.
func (hf *hostsFile) init() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
hf.watcher = watcher
if err := hf.watcher.Add(hostsfile.HostsPath); err != nil {
return err
}
m, err := hostsfile.ParseHosts(hostsfile.ReadHostsFile())
if err != nil {
return err
}
hf.mu.Lock()
hf.m = m
hf.mu.Unlock()
return nil
}
// refresh reloads hosts file entries.
func (hf *hostsFile) refresh() error {
m, err := hostsfile.ParseHosts(hostsfile.ReadHostsFile())
if err != nil {
return err
}
hf.mu.Lock()
hf.m = m
hf.mu.Unlock()
return nil
}
// watchChanges watches and updates hosts file data if any changes happens.
func (hf *hostsFile) watchChanges() {
if hf.watcher == nil {
return
}
for {
select {
case event, ok := <-hf.watcher.Events:
if !ok {
return
}
if event.Has(fsnotify.Write) || event.Has(fsnotify.Rename) || event.Has(fsnotify.Chmod) || event.Has(fsnotify.Remove) {
if err := hf.refresh(); err != nil && !os.IsNotExist(err) {
ctrld.ProxyLogger.Load().Err(err).Msg("hosts file changed but failed to update client info")
}
}
case err, ok := <-hf.watcher.Errors:
if !ok {
return
}
ctrld.ProxyLogger.Load().Err(err).Msg("could not watch client info file")
}
}
}
// LookupHostnameByIP returns hostname for given IP from current hosts file entries.
func (hf *hostsFile) LookupHostnameByIP(ip string) string {
hf.mu.Lock()
defer hf.mu.Unlock()
if names := hf.m[ip]; len(names) > 0 {
isLoopback := ip == "127.0.0.1" || ip == "::1"
for _, hostname := range names {
name := normalizeHostname(hostname)
// Ignoring ipv4/ipv6 loopback entry.
if isLoopback && isLocalhostName(name) {
continue
}
return name
}
}
return ""
}
// LookupHostnameByMac returns hostname for given Mac from current hosts file entries.
func (hf *hostsFile) LookupHostnameByMac(mac string) string {
return ""
}
// String returns human-readable format of hostsFile.
func (hf *hostsFile) String() string {
return "hosts"
}
// isLocalhostName reports whether the given hostname represents localhost.
func isLocalhostName(hostname string) bool {
switch hostname {
case ipv4LocalhostName, ipv6LocalhostName, ipv6LoopbackName:
return true
default:
return false
}
}