mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-16 10:22:45 +00:00
In case the resolver could not reach nameserver, ptr discover should only print error message once, then stop doing the query until the nameserver is reachable. This would prevent ptr discover from flooding ctrld log with a lot of duplicated messages.
117 lines
2.6 KiB
Go
117 lines
2.6 KiB
Go
package clientinfo
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
"tailscale.com/logtail/backoff"
|
|
|
|
"github.com/Control-D-Inc/ctrld"
|
|
)
|
|
|
|
type ptrDiscover struct {
|
|
hostname sync.Map // ip => hostname
|
|
resolver ctrld.Resolver
|
|
serverDown atomic.Bool
|
|
}
|
|
|
|
func (p *ptrDiscover) refresh() error {
|
|
p.hostname.Range(func(key, value any) bool {
|
|
ip := key.(string)
|
|
if name := p.lookupHostname(ip); name != "" {
|
|
p.hostname.Store(ip, name)
|
|
}
|
|
return true
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (p *ptrDiscover) LookupHostnameByIP(ip string) string {
|
|
if val, ok := p.hostname.Load(ip); ok {
|
|
return val.(string)
|
|
}
|
|
return p.lookupHostname(ip)
|
|
}
|
|
func (p *ptrDiscover) LookupHostnameByMac(mac string) string {
|
|
return ""
|
|
}
|
|
|
|
func (p *ptrDiscover) String() string {
|
|
return "ptr"
|
|
}
|
|
|
|
func (p *ptrDiscover) List() []string {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
var ips []string
|
|
p.hostname.Range(func(key, value any) bool {
|
|
ips = append(ips, key.(string))
|
|
return true
|
|
})
|
|
return ips
|
|
}
|
|
|
|
func (p *ptrDiscover) lookupHostnameFromCache(ip string) string {
|
|
if val, ok := p.hostname.Load(ip); ok {
|
|
return val.(string)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (p *ptrDiscover) lookupHostname(ip string) string {
|
|
// If nameserver is down, do nothing.
|
|
if p.serverDown.Load() {
|
|
return ""
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
msg := new(dns.Msg)
|
|
addr, err := dns.ReverseAddr(ip)
|
|
if err != nil {
|
|
ctrld.ProxyLogger.Load().Warn().Str("discovery", "ptr").Err(err).Msg("invalid ip address")
|
|
return ""
|
|
}
|
|
msg.SetQuestion(addr, dns.TypePTR)
|
|
ans, err := p.resolver.Resolve(ctx, msg)
|
|
if err != nil {
|
|
ctrld.ProxyLogger.Load().Warn().Str("discovery", "ptr").Err(err).Msg("could not perform PTR lookup")
|
|
p.serverDown.Store(true)
|
|
go p.checkServer()
|
|
return ""
|
|
}
|
|
for _, rr := range ans.Answer {
|
|
if ptr, ok := rr.(*dns.PTR); ok {
|
|
hostname := normalizeHostname(ptr.Ptr)
|
|
p.hostname.Store(ip, hostname)
|
|
return hostname
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// 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() {
|
|
bo := backoff.NewBackoff("ptrDiscover", func(format string, args ...any) {}, time.Minute*5)
|
|
m := new(dns.Msg)
|
|
m.SetQuestion(".", dns.TypeNS)
|
|
ping := func() error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
_, err := p.resolver.Resolve(ctx, m)
|
|
return err
|
|
}
|
|
for {
|
|
if err := ping(); err != nil {
|
|
bo.BackOff(context.Background(), err)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
p.serverDown.Store(false)
|
|
}
|