all: add UpstreamConfig.VerifyDomain

So the self-check process is only done for ControlD upstream, and can be
distinguished between .com and .dev resolvers.
This commit is contained in:
Cuong Manh Le
2023-05-26 21:33:24 +07:00
committed by Cuong Manh Le
parent 54e63ccf9b
commit 8fda856e24
4 changed files with 76 additions and 9 deletions

View File

@@ -36,8 +36,6 @@ import (
"github.com/Control-D-Inc/ctrld/internal/router"
)
const selfCheckFQDN = "verify.controld.com"
var (
version = "dev"
commit = "none"
@@ -289,6 +287,10 @@ func initCLI() {
processCDFlags()
if err := ctrld.ValidateConfig(validator.New(), &cfg); err != nil {
mainLog.Fatal().Msgf("invalid config: %v", err)
}
// Explicitly passing config, so on system where home directory could not be obtained,
// or sub-process env is different with the parent, we still behave correctly and use
// the expected config file.
@@ -320,7 +322,8 @@ func initCLI() {
return
}
status = selfCheckStatus(status)
domain := cfg.Upstream["0"].VerifyDomain()
status = selfCheckStatus(status, domain)
switch status {
case service.StatusRunning:
mainLog.Notice().Msg("Service started")
@@ -855,7 +858,11 @@ func defaultIfaceName() string {
return dri
}
func selfCheckStatus(status service.Status) service.Status {
func selfCheckStatus(status service.Status, domain string) service.Status {
if domain == "" {
// Nothing to do, return the status as-is.
return status
}
c := new(dns.Client)
bo := backoff.NewBackoff("self-check", logf, 10*time.Second)
bo.LogLongerThan = 500 * time.Millisecond
@@ -883,16 +890,16 @@ func selfCheckStatus(status service.Status) service.Status {
}
mu.Unlock()
m := new(dns.Msg)
m.SetQuestion(selfCheckFQDN+".", dns.TypeA)
m.SetQuestion(domain+".", dns.TypeA)
m.RecursionDesired = true
r, _, err := c.ExchangeContext(ctx, m, net.JoinHostPort(lc.IP, strconv.Itoa(lc.Port)))
if r != nil && r.Rcode == dns.RcodeSuccess && len(r.Answer) > 0 {
mainLog.Debug().Msgf("self-check against %q succeeded", selfCheckFQDN)
mainLog.Debug().Msgf("self-check against %q succeeded", domain)
return status
}
bo.BackOff(ctx, fmt.Errorf("ExchangeContext: %w", err))
}
mainLog.Debug().Msgf("self-check against %q failed", selfCheckFQDN)
mainLog.Debug().Msgf("self-check against %q failed", domain)
return service.StatusUnknown
}

View File

@@ -174,7 +174,7 @@ func Test_macFromMsg(t *testing.T) {
t.Fatal(err)
}
m := new(dns.Msg)
m.SetQuestion(selfCheckFQDN+".", dns.TypeA)
m.SetQuestion("example.com.", dns.TypeA)
o := &dns.OPT{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeOPT}}
if tc.wantMac {
ec1 := &dns.EDNS0_LOCAL{Code: EDNS0_OPTION_MAC, Data: hw}

View File

@@ -29,9 +29,19 @@ const (
IpStackV4 = "v4"
IpStackV6 = "v6"
IpStackSplit = "split"
controlDComDomain = "controld.com"
controlDNetDomain = "controld.net"
controlDDevDomain = "controld.dev"
)
var controldParentDomains = []string{"controld.com", "controld.net", "controld.dev"}
var (
controldParentDomains = []string{controlDComDomain, controlDNetDomain, controlDDevDomain}
controldVerifiedDomain = map[string]string{
controlDComDomain: "verify.controld.com",
controlDDevDomain: "verify.controld.dev",
}
)
// SetConfigName set the config name that ctrld will look for.
// DEPRECATED: use SetConfigNameWithPath instead.
@@ -201,6 +211,23 @@ func (uc *UpstreamConfig) Init() {
}
}
// VerifyDomain returns the domain name that could be resolved by the upstream endpoint.
// It returns empty for non-ControlD upstream endpoint.
func (uc *UpstreamConfig) VerifyDomain() string {
domain := uc.Domain
if domain == "" {
if u, err := url.Parse(uc.Endpoint); err == nil {
domain = u.Hostname()
}
}
for _, parent := range controldParentDomains {
if dns.IsSubDomain(parent, domain) {
return controldVerifiedDomain[parent]
}
}
return ""
}
// UpstreamSendClientInfo reports whether the upstream is
// configured to send client info to Control D DNS server.
//

View File

@@ -190,6 +190,39 @@ func TestUpstreamConfig_Init(t *testing.T) {
}
}
func TestUpstreamConfig_VerifyDomain(t *testing.T) {
tests := []struct {
name string
uc *UpstreamConfig
verifyDomain string
}{
{
controlDComDomain,
&UpstreamConfig{Endpoint: "https://freedns.controld.com/p2"},
controldVerifiedDomain[controlDComDomain],
},
{
controlDDevDomain,
&UpstreamConfig{Endpoint: "https://freedns.controld.dev/p2"},
controldVerifiedDomain[controlDDevDomain],
},
{
"non-ControlD upstream",
&UpstreamConfig{Endpoint: "https://dns.google/dns-query"},
"",
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.uc.VerifyDomain(); got != tc.verifyDomain {
t.Errorf("unexpected verify domain, want: %q, got: %q", tc.verifyDomain, got)
}
})
}
}
func ptrBool(b bool) *bool {
return &b
}