mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-29 01:18:48 +02:00
Merge pull request #233 from Control-D-Inc/release-branch-v1.4.3
[WIP] Release branch v1.4.3
This commit is contained in:
+3
-1
@@ -227,7 +227,9 @@ func run(appCallback *AppCallback, stopCh chan struct{}) {
|
|||||||
consoleWriter.Out = io.MultiWriter(os.Stdout, lc)
|
consoleWriter.Out = io.MultiWriter(os.Stdout, lc)
|
||||||
p.logConn = lc
|
p.logConn = lc
|
||||||
} else {
|
} else {
|
||||||
mainLog.Load().Warn().Err(err).Msgf("unable to create log ipc connection")
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
mainLog.Load().Warn().Err(err).Msg("unable to create log ipc connection")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mainLog.Load().Warn().Err(err).Msgf("unable to resolve socket address: %s", sockPath)
|
mainLog.Load().Warn().Err(err).Msgf("unable to resolve socket address: %s", sockPath)
|
||||||
|
|||||||
@@ -427,11 +427,18 @@ func (uc *UpstreamConfig) UID() string {
|
|||||||
|
|
||||||
// SetupBootstrapIP manually find all available IPs of the upstream.
|
// SetupBootstrapIP manually find all available IPs of the upstream.
|
||||||
// The first usable IP will be used as bootstrap IP of the upstream.
|
// The first usable IP will be used as bootstrap IP of the upstream.
|
||||||
|
// The upstream domain will be looked up using following orders:
|
||||||
|
//
|
||||||
|
// - Current system DNS settings.
|
||||||
|
// - Direct IPs table for ControlD upstreams.
|
||||||
|
// - ControlD Bootstrap DNS 76.76.2.22
|
||||||
|
//
|
||||||
|
// The setup process will block until there's usable IPs found.
|
||||||
func (uc *UpstreamConfig) SetupBootstrapIP() {
|
func (uc *UpstreamConfig) SetupBootstrapIP() {
|
||||||
b := backoff.NewBackoff("setupBootstrapIP", func(format string, args ...any) {}, 10*time.Second)
|
b := backoff.NewBackoff("setupBootstrapIP", func(format string, args ...any) {}, 10*time.Second)
|
||||||
isControlD := uc.IsControlD()
|
isControlD := uc.IsControlD()
|
||||||
for {
|
for {
|
||||||
uc.bootstrapIPs = lookupIP(uc.Domain, uc.Timeout)
|
uc.bootstrapIPs = lookupIP(uc.Domain, uc.Timeout, defaultNameservers())
|
||||||
// For ControlD upstream, the bootstrap IPs could not be RFC 1918 addresses,
|
// For ControlD upstream, the bootstrap IPs could not be RFC 1918 addresses,
|
||||||
// filtering them out here to prevent weird behavior.
|
// filtering them out here to prevent weird behavior.
|
||||||
if isControlD {
|
if isControlD {
|
||||||
@@ -446,9 +453,14 @@ func (uc *UpstreamConfig) SetupBootstrapIP() {
|
|||||||
uc.bootstrapIPs = uc.bootstrapIPs[:n]
|
uc.bootstrapIPs = uc.bootstrapIPs[:n]
|
||||||
if len(uc.bootstrapIPs) == 0 {
|
if len(uc.bootstrapIPs) == 0 {
|
||||||
uc.bootstrapIPs = bootstrapIPsFromControlDDomain(uc.Domain)
|
uc.bootstrapIPs = bootstrapIPsFromControlDDomain(uc.Domain)
|
||||||
ProxyLogger.Load().Warn().Msgf("no bootstrap IPs found for %q, fallback to direct IPs", uc.Domain)
|
ProxyLogger.Load().Warn().Msgf("no record found for %q, lookup from direct IP table", uc.Domain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(uc.bootstrapIPs) == 0 {
|
||||||
|
ProxyLogger.Load().Warn().Msgf("no record found for %q, using bootstrap server: %s", uc.Domain, PremiumDNSBoostrapIP)
|
||||||
|
uc.bootstrapIPs = lookupIP(uc.Domain, uc.Timeout, []string{net.JoinHostPort(PremiumDNSBoostrapIP, "53")})
|
||||||
|
|
||||||
|
}
|
||||||
if len(uc.bootstrapIPs) > 0 {
|
if len(uc.bootstrapIPs) > 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -951,14 +963,14 @@ func (uc *UpstreamConfig) String() string {
|
|||||||
|
|
||||||
// bootstrapIPsFromControlDDomain returns bootstrap IPs for ControlD domain.
|
// bootstrapIPsFromControlDDomain returns bootstrap IPs for ControlD domain.
|
||||||
func bootstrapIPsFromControlDDomain(domain string) []string {
|
func bootstrapIPsFromControlDDomain(domain string) []string {
|
||||||
switch domain {
|
switch {
|
||||||
case PremiumDnsDomain:
|
case dns.IsSubDomain(PremiumDnsDomain, domain):
|
||||||
return []string{PremiumDNSBoostrapIP, PremiumDNSBoostrapIPv6}
|
return []string{PremiumDNSBoostrapIP, PremiumDNSBoostrapIPv6}
|
||||||
case FreeDnsDomain:
|
case dns.IsSubDomain(FreeDnsDomain, domain):
|
||||||
return []string{FreeDNSBoostrapIP, FreeDNSBoostrapIPv6}
|
return []string{FreeDNSBoostrapIP, FreeDNSBoostrapIPv6}
|
||||||
case premiumDnsDomainDev:
|
case dns.IsSubDomain(premiumDnsDomainDev, domain):
|
||||||
return []string{premiumDNSBoostrapIP, premiumDNSBoostrapIPv6}
|
return []string{premiumDNSBoostrapIP, premiumDNSBoostrapIPv6}
|
||||||
case freeDnsDomainDev:
|
case dns.IsSubDomain(freeDnsDomainDev, domain):
|
||||||
return []string{freeDNSBoostrapIP, freeDNSBoostrapIPv6}
|
return []string{freeDNSBoostrapIP, freeDNSBoostrapIPv6}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+35
-11
@@ -8,19 +8,43 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestUpstreamConfig_SetupBootstrapIP(t *testing.T) {
|
func TestUpstreamConfig_SetupBootstrapIP(t *testing.T) {
|
||||||
uc := &UpstreamConfig{
|
tests := []struct {
|
||||||
Name: "test",
|
name string
|
||||||
Type: ResolverTypeDOH,
|
uc *UpstreamConfig
|
||||||
Endpoint: "https://freedns.controld.com/p2",
|
}{
|
||||||
Timeout: 5000,
|
{
|
||||||
|
name: "doh/doh3",
|
||||||
|
uc: &UpstreamConfig{
|
||||||
|
Name: "doh",
|
||||||
|
Type: ResolverTypeDOH,
|
||||||
|
Endpoint: "https://freedns.controld.com/p2",
|
||||||
|
Timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doq/dot",
|
||||||
|
uc: &UpstreamConfig{
|
||||||
|
Name: "dot",
|
||||||
|
Type: ResolverTypeDOT,
|
||||||
|
Endpoint: "p2.freedns.controld.com",
|
||||||
|
Timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
uc.Init()
|
for _, tc := range tests {
|
||||||
uc.SetupBootstrapIP()
|
tc := tc
|
||||||
if len(uc.bootstrapIPs) == 0 {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Log(defaultNameservers())
|
// Enable parallel tests once https://github.com/microsoft/wmi/issues/165 fixed.
|
||||||
t.Fatal("could not bootstrap ip without bootstrap DNS")
|
// t.Parallel()
|
||||||
|
tc.uc.Init()
|
||||||
|
tc.uc.SetupBootstrapIP()
|
||||||
|
if len(tc.uc.bootstrapIPs) == 0 {
|
||||||
|
t.Log(defaultNameservers())
|
||||||
|
t.Fatalf("could not bootstrap ip: %s", tc.uc.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
t.Log(uc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpstreamConfig_Init(t *testing.T) {
|
func TestUpstreamConfig_Init(t *testing.T) {
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func dnsFns() []dnsFn {
|
func dnsFns() []dnsFn {
|
||||||
return []dnsFn{dnsFromRIB}
|
return []dnsFn{dnsFromResolvConf, dnsFromRIB}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dnsFromRIB() []string {
|
func dnsFromRIB() []string {
|
||||||
|
|||||||
@@ -16,58 +16,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
|
|
||||||
"github.com/Control-D-Inc/ctrld/internal/resolvconffile"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func dnsFns() []dnsFn {
|
func dnsFns() []dnsFn {
|
||||||
return []dnsFn{dnsFromResolvConf, getDNSFromScutil, getAllDHCPNameservers}
|
return []dnsFn{dnsFromResolvConf, getDNSFromScutil, getAllDHCPNameservers}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dnsFromResolvConf reads nameservers from /etc/resolv.conf
|
|
||||||
func dnsFromResolvConf() []string {
|
|
||||||
const (
|
|
||||||
maxRetries = 10
|
|
||||||
retryInterval = 100 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
regularIPs, loopbackIPs, _ := netmon.LocalAddresses()
|
|
||||||
|
|
||||||
var dns []string
|
|
||||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
|
||||||
if attempt > 0 {
|
|
||||||
time.Sleep(retryInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
nss := resolvconffile.NameServers("")
|
|
||||||
var localDNS []string
|
|
||||||
seen := make(map[string]bool)
|
|
||||||
|
|
||||||
for _, ns := range nss {
|
|
||||||
if ip := net.ParseIP(ns); ip != nil {
|
|
||||||
// skip loopback IPs
|
|
||||||
for _, v := range slices.Concat(regularIPs, loopbackIPs) {
|
|
||||||
ipStr := v.String()
|
|
||||||
if ip.String() == ipStr {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !seen[ip.String()] {
|
|
||||||
seen[ip.String()] = true
|
|
||||||
localDNS = append(localDNS, ip.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we successfully read the file and found nameservers, return them
|
|
||||||
if len(localDNS) > 0 {
|
|
||||||
return localDNS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDNSFromScutil() []string {
|
func getDNSFromScutil() []string {
|
||||||
logger := *ProxyLogger.Load()
|
logger := *ProxyLogger.Load()
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func dnsFns() []dnsFn {
|
func dnsFns() []dnsFn {
|
||||||
return []dnsFn{dns4, dns6, dnsFromSystemdResolver}
|
return []dnsFn{dnsFromResolvConf, dns4, dns6, dnsFromSystemdResolver}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dns4() []string {
|
func dns4() []string {
|
||||||
|
|||||||
+57
-2
@@ -2,8 +2,63 @@
|
|||||||
|
|
||||||
package ctrld
|
package ctrld
|
||||||
|
|
||||||
import "github.com/Control-D-Inc/ctrld/internal/resolvconffile"
|
import (
|
||||||
|
"net"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
func nameserversFromResolvconf() []string {
|
"tailscale.com/net/netmon"
|
||||||
|
|
||||||
|
"github.com/Control-D-Inc/ctrld/internal/resolvconffile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// currentNameserversFromResolvconf returns the current nameservers set from /etc/resolv.conf file.
|
||||||
|
func currentNameserversFromResolvconf() []string {
|
||||||
return resolvconffile.NameServers("")
|
return resolvconffile.NameServers("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dnsFromResolvConf reads usable nameservers from /etc/resolv.conf file.
|
||||||
|
// A nameserver is usable if it's not one of current machine's IP addresses
|
||||||
|
// and loopback IP addresses.
|
||||||
|
func dnsFromResolvConf() []string {
|
||||||
|
const (
|
||||||
|
maxRetries = 10
|
||||||
|
retryInterval = 100 * time.Millisecond
|
||||||
|
)
|
||||||
|
|
||||||
|
regularIPs, loopbackIPs, _ := netmon.LocalAddresses()
|
||||||
|
|
||||||
|
var dns []string
|
||||||
|
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||||
|
if attempt > 0 {
|
||||||
|
time.Sleep(retryInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
nss := resolvconffile.NameServers("")
|
||||||
|
var localDNS []string
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
|
for _, ns := range nss {
|
||||||
|
if ip := net.ParseIP(ns); ip != nil {
|
||||||
|
// skip loopback IPs
|
||||||
|
for _, v := range slices.Concat(regularIPs, loopbackIPs) {
|
||||||
|
ipStr := v.String()
|
||||||
|
if ip.String() == ipStr {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !seen[ip.String()] {
|
||||||
|
seen[ip.String()] = true
|
||||||
|
localDNS = append(localDNS, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we successfully read the file and found nameservers, return them
|
||||||
|
if len(localDNS) > 0 {
|
||||||
|
return localDNS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dns
|
||||||
|
}
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ func getDNSServers(ctx context.Context) ([]string, error) {
|
|||||||
0, // DomainGuid - not needed
|
0, // DomainGuid - not needed
|
||||||
0, // SiteName - not needed
|
0, // SiteName - not needed
|
||||||
uintptr(flags), // Flags
|
uintptr(flags), // Flags
|
||||||
uintptr(unsafe.Pointer(&info))) // DomainControllerInfo - output
|
uintptr(unsafe.Pointer(&info))) // DomainControllerInfo - output
|
||||||
|
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
switch ret {
|
switch ret {
|
||||||
@@ -330,7 +330,8 @@ func getDNSServers(ctx context.Context) ([]string, error) {
|
|||||||
return ns, nil
|
return ns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nameserversFromResolvconf() []string {
|
// currentNameserversFromResolvconf returns a nil slice of strings.
|
||||||
|
func currentNameserversFromResolvconf() []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-13
@@ -466,27 +466,26 @@ func (d dummyResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, err
|
|||||||
return ans, nil
|
return ans, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupIP looks up host using OS resolver.
|
// LookupIP looks up domain using current system nameservers settings.
|
||||||
// It returns a slice of that host's IPv4 and IPv6 addresses.
|
// It returns a slice of that host's IPv4 and IPv6 addresses.
|
||||||
func LookupIP(domain string) []string {
|
func LookupIP(domain string) []string {
|
||||||
return lookupIP(domain, -1)
|
return lookupIP(domain, -1, defaultNameservers())
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupIP(domain string, timeout int) (ips []string) {
|
// lookupIP looks up domain with given timeout and bootstrapDNS.
|
||||||
|
// If timeout is negative, default timeout 2000 ms will be used.
|
||||||
|
// It returns nil if bootstrapDNS is nil or empty.
|
||||||
|
func lookupIP(domain string, timeout int, bootstrapDNS []string) (ips []string) {
|
||||||
if net.ParseIP(domain) != nil {
|
if net.ParseIP(domain) != nil {
|
||||||
return []string{domain}
|
return []string{domain}
|
||||||
}
|
}
|
||||||
resolverMutex.Lock()
|
if bootstrapDNS == nil {
|
||||||
if or == nil {
|
ProxyLogger.Load().Debug().Msgf("empty bootstrap DNS")
|
||||||
ProxyLogger.Load().Debug().Msgf("Initialize OS resolver in lookupIP")
|
return nil
|
||||||
or = newResolverWithNameserver(defaultNameservers())
|
|
||||||
}
|
}
|
||||||
nss := *or.lanServers.Load()
|
|
||||||
nss = append(nss, *or.publicServers.Load()...)
|
|
||||||
resolverMutex.Unlock()
|
|
||||||
|
|
||||||
resolver := newResolverWithNameserver(nss)
|
resolver := newResolverWithNameserver(bootstrapDNS)
|
||||||
ProxyLogger.Load().Debug().Msgf("resolving %q using bootstrap DNS %q", domain, nss)
|
ProxyLogger.Load().Debug().Msgf("resolving %q using bootstrap DNS %q", domain, bootstrapDNS)
|
||||||
timeoutMs := 2000
|
timeoutMs := 2000
|
||||||
if timeout > 0 && timeout < timeoutMs {
|
if timeout > 0 && timeout < timeoutMs {
|
||||||
timeoutMs = timeout
|
timeoutMs = timeout
|
||||||
@@ -585,7 +584,7 @@ func NewPrivateResolver() Resolver {
|
|||||||
}
|
}
|
||||||
nss := *or.lanServers.Load()
|
nss := *or.lanServers.Load()
|
||||||
resolverMutex.Unlock()
|
resolverMutex.Unlock()
|
||||||
resolveConfNss := nameserversFromResolvconf()
|
resolveConfNss := currentNameserversFromResolvconf()
|
||||||
localRfc1918Addrs := Rfc1918Addresses()
|
localRfc1918Addrs := Rfc1918Addresses()
|
||||||
n := 0
|
n := 0
|
||||||
for _, ns := range nss {
|
for _, ns := range nss {
|
||||||
|
|||||||
Reference in New Issue
Block a user