mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-05-02 12:45:10 +02:00
Implement new flow for LAN and private PTR resolution
- Use client info table. - If no sufficient data, use gateway/os/defined local upstreams. - If no data is returned, use remote upstream
This commit is contained in:
committed by
Cuong Manh Le
parent
a2cb895cdc
commit
f9a3f4c045
@@ -3,7 +3,9 @@ package clientinfo
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -69,25 +71,27 @@ type Table struct {
|
||||
refreshers []refresher
|
||||
initOnce sync.Once
|
||||
|
||||
dhcp *dhcp
|
||||
merlin *merlinDiscover
|
||||
arp *arpDiscover
|
||||
ptr *ptrDiscover
|
||||
mdns *mdns
|
||||
hf *hostsFile
|
||||
vni *virtualNetworkIface
|
||||
svcCfg ctrld.ServiceConfig
|
||||
quitCh chan struct{}
|
||||
selfIP string
|
||||
cdUID string
|
||||
dhcp *dhcp
|
||||
merlin *merlinDiscover
|
||||
arp *arpDiscover
|
||||
ptr *ptrDiscover
|
||||
mdns *mdns
|
||||
hf *hostsFile
|
||||
vni *virtualNetworkIface
|
||||
svcCfg ctrld.ServiceConfig
|
||||
quitCh chan struct{}
|
||||
selfIP string
|
||||
cdUID string
|
||||
ptrNameservers []string
|
||||
}
|
||||
|
||||
func NewTable(cfg *ctrld.Config, selfIP, cdUID string) *Table {
|
||||
func NewTable(cfg *ctrld.Config, selfIP, cdUID string, ns []string) *Table {
|
||||
return &Table{
|
||||
svcCfg: cfg.Service,
|
||||
quitCh: make(chan struct{}),
|
||||
selfIP: selfIP,
|
||||
cdUID: cdUID,
|
||||
svcCfg: cfg.Service,
|
||||
quitCh: make(chan struct{}),
|
||||
selfIP: selfIP,
|
||||
cdUID: cdUID,
|
||||
ptrNameservers: ns,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,9 +187,25 @@ func (t *Table) init() {
|
||||
// PTR lookup.
|
||||
if t.discoverPTR() {
|
||||
t.ptr = &ptrDiscover{resolver: ctrld.NewPrivateResolver()}
|
||||
if r := t.svcCfg.PtrResolver(); r != nil {
|
||||
ctrld.ProxyLogger.Load().Debug().Msgf("using nameservers %v for PTR discover", t.svcCfg.DiscoverPtrEndpoints)
|
||||
t.ptr.resolver = r
|
||||
if len(t.ptrNameservers) > 0 {
|
||||
nss := make([]string, 0, len(t.ptrNameservers))
|
||||
for _, ns := range t.ptrNameservers {
|
||||
host, port := ns, "53"
|
||||
if h, p, err := net.SplitHostPort(ns); err == nil {
|
||||
host, port = h, p
|
||||
}
|
||||
// Only use valid ip:port pair.
|
||||
if _, portErr := strconv.Atoi(port); portErr == nil && port != "0" && net.ParseIP(host) != nil {
|
||||
nss = append(nss, net.JoinHostPort(host, port))
|
||||
} else {
|
||||
ctrld.ProxyLogger.Load().Warn().Msgf("ignoring invalid nameserver for ptr discover: %q", ns)
|
||||
}
|
||||
}
|
||||
if len(nss) > 0 {
|
||||
t.ptr.resolver = ctrld.NewResolverWithNameserver(nss)
|
||||
ctrld.ProxyLogger.Load().Debug().Msgf("using nameservers %v for ptr discovery", nss)
|
||||
}
|
||||
|
||||
}
|
||||
ctrld.ProxyLogger.Load().Debug().Msg("start ptr discovery")
|
||||
if err := t.ptr.refresh(); err != nil {
|
||||
@@ -358,6 +378,27 @@ func (t *Table) StoreVPNClient(ci *ctrld.ClientInfo) {
|
||||
t.vni.ip2name.Store(ci.IP, ci.Hostname)
|
||||
}
|
||||
|
||||
// ipFinder is the interface for retrieving IP address from hostname.
|
||||
type ipFinder interface {
|
||||
lookupIPByHostname(name string, v6 bool) string
|
||||
}
|
||||
|
||||
// LookupIPByHostname returns the ip address of given hostname.
|
||||
// If v6 is true, return IPv6 instead of default IPv4.
|
||||
func (t *Table) LookupIPByHostname(hostname string, v6 bool) *netip.Addr {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
for _, finder := range []ipFinder{t.hf, t.ptr, t.mdns, t.dhcp} {
|
||||
if addr := finder.lookupIPByHostname(hostname, v6); addr != "" {
|
||||
if ip, err := netip.ParseAddr(addr); err == nil {
|
||||
return &ip
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) discoverDHCP() bool {
|
||||
if t.svcCfg.DiscoverDHCP == nil {
|
||||
return true
|
||||
|
||||
@@ -134,6 +134,23 @@ func (d *dhcp) List() []string {
|
||||
return ips
|
||||
}
|
||||
|
||||
func (d *dhcp) lookupIPByHostname(name string, v6 bool) string {
|
||||
if d == nil {
|
||||
return ""
|
||||
}
|
||||
var ip string
|
||||
d.ip2name.Range(func(key, value any) bool {
|
||||
if value == name {
|
||||
if addr, err := netip.ParseAddr(key.(string)); err == nil && addr.Is6() == v6 {
|
||||
ip = addr.String()
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return ip
|
||||
}
|
||||
|
||||
// AddLeaseFile adds given lease file for reading/watching clients info.
|
||||
func (d *dhcp) addLeaseFile(name string, format ctrld.LeaseFileFormat) error {
|
||||
if d.watcher == nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package clientinfo
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
@@ -109,6 +110,24 @@ func (hf *hostsFile) String() string {
|
||||
return "hosts"
|
||||
}
|
||||
|
||||
func (hf *hostsFile) lookupIPByHostname(name string, v6 bool) string {
|
||||
if hf == nil {
|
||||
return ""
|
||||
}
|
||||
hf.mu.Lock()
|
||||
defer hf.mu.Unlock()
|
||||
for addr, names := range hf.m {
|
||||
if ip, err := netip.ParseAddr(addr); err == nil && !ip.IsLoopback() {
|
||||
for _, n := range names {
|
||||
if n == name && ip.Is6() == v6 {
|
||||
return ip.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// isLocalhostName reports whether the given hostname represents localhost.
|
||||
func isLocalhostName(hostname string) bool {
|
||||
switch hostname {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -59,6 +60,23 @@ func (m *mdns) List() []string {
|
||||
return ips
|
||||
}
|
||||
|
||||
func (m *mdns) lookupIPByHostname(name string, v6 bool) string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
var ip string
|
||||
m.name.Range(func(key, value any) bool {
|
||||
if value == name {
|
||||
if addr, err := netip.ParseAddr(key.(string)); err == nil && addr.Is6() == v6 {
|
||||
ip = addr.String()
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return ip
|
||||
}
|
||||
|
||||
func (m *mdns) init(quitCh chan struct{}) error {
|
||||
ifaces, err := multicastInterfaces()
|
||||
if err != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package clientinfo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -94,6 +95,23 @@ func (p *ptrDiscover) lookupHostname(ip string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *ptrDiscover) lookupIPByHostname(name string, v6 bool) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
var ip string
|
||||
p.hostname.Range(func(key, value any) bool {
|
||||
if value == name {
|
||||
if addr, err := netip.ParseAddr(key.(string)); err == nil && addr.Is6() == v6 {
|
||||
ip = addr.String()
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return ip
|
||||
}
|
||||
|
||||
// checkServer monitors if the resolver can reach its nameserver. When the nameserver
|
||||
// is reachable, set p.serverDown to false, so p.lookupHostname can continue working.
|
||||
func (p *ptrDiscover) checkServer() {
|
||||
|
||||
Reference in New Issue
Block a user