Initial commit
This commit is contained in:
commit
1d93a25983
23 changed files with 2654 additions and 0 deletions
152
checker/dns.go
Normal file
152
checker/dns.go
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const dnsTimeout = 5 * time.Second
|
||||
|
||||
// FallbackResolver is the resolver used when /etc/resolv.conf is missing or
|
||||
// empty.
|
||||
var FallbackResolver = net.JoinHostPort("1.1.1.1", "53")
|
||||
|
||||
func systemResolver() string {
|
||||
cfg, err := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
if err != nil || len(cfg.Servers) == 0 {
|
||||
return FallbackResolver
|
||||
}
|
||||
return net.JoinHostPort(cfg.Servers[0], cfg.Port)
|
||||
}
|
||||
|
||||
func dnsExchange(ctx context.Context, server string, q dns.Question) (*dns.Msg, error) {
|
||||
client := dns.Client{Timeout: dnsTimeout}
|
||||
m := new(dns.Msg)
|
||||
m.Id = dns.Id()
|
||||
m.Question = []dns.Question{q}
|
||||
m.RecursionDesired = true
|
||||
m.SetEdns0(4096, true)
|
||||
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
if d := time.Until(deadline); d > 0 && d < client.Timeout {
|
||||
client.Timeout = d
|
||||
}
|
||||
}
|
||||
|
||||
r, _, err := client.Exchange(m, server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r != nil && r.Truncated {
|
||||
if ctx.Err() != nil {
|
||||
return r, nil
|
||||
}
|
||||
tcpClient := dns.Client{Net: "tcp", Timeout: client.Timeout}
|
||||
if r2, _, err2 := tcpClient.Exchange(m, server); err2 == nil && r2 != nil {
|
||||
return r2, nil
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func lowerFQDN(name string) string {
|
||||
return strings.ToLower(dns.Fqdn(name))
|
||||
}
|
||||
|
||||
func isReverseArpa(name string) bool {
|
||||
n := lowerFQDN(name)
|
||||
return strings.HasSuffix(n, ".in-addr.arpa.") || n == "in-addr.arpa." ||
|
||||
strings.HasSuffix(n, ".ip6.arpa.") || n == "ip6.arpa."
|
||||
}
|
||||
|
||||
func isIPv6Arpa(name string) bool {
|
||||
n := lowerFQDN(name)
|
||||
return strings.HasSuffix(n, ".ip6.arpa.") || n == "ip6.arpa."
|
||||
}
|
||||
|
||||
// reverseNameToIP returns nil for partial zone apexes (e.g. covering only part of an octet).
|
||||
func reverseNameToIP(name string) net.IP {
|
||||
n := strings.ToLower(strings.TrimSuffix(dns.Fqdn(name), "."))
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(n, ".in-addr.arpa"):
|
||||
labels := strings.Split(strings.TrimSuffix(n, ".in-addr.arpa"), ".")
|
||||
if len(labels) != 4 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 4)
|
||||
for i, l := range labels {
|
||||
if _, err := strconv.Atoi(l); err != nil {
|
||||
return nil
|
||||
}
|
||||
out[3-i] = l
|
||||
}
|
||||
return net.ParseIP(strings.Join(out, "."))
|
||||
|
||||
case strings.HasSuffix(n, ".ip6.arpa"):
|
||||
labels := strings.Split(strings.TrimSuffix(n, ".ip6.arpa"), ".")
|
||||
if len(labels) != 32 {
|
||||
return nil
|
||||
}
|
||||
var sb strings.Builder
|
||||
for i := len(labels) - 1; i >= 0; i-- {
|
||||
if len(labels[i]) != 1 {
|
||||
return nil
|
||||
}
|
||||
sb.WriteString(labels[i])
|
||||
if i > 0 && (len(labels)-i)%4 == 0 {
|
||||
sb.WriteByte(':')
|
||||
}
|
||||
}
|
||||
return net.ParseIP(sb.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveForward(ctx context.Context, name string) ([]ForwardAddress, string) {
|
||||
resolver := systemResolver()
|
||||
var out []ForwardAddress
|
||||
var lastErr string
|
||||
anySuccess := false
|
||||
|
||||
for _, qt := range []uint16{dns.TypeA, dns.TypeAAAA} {
|
||||
if ctx.Err() != nil {
|
||||
break
|
||||
}
|
||||
q := dns.Question{Name: dns.Fqdn(name), Qtype: qt, Qclass: dns.ClassINET}
|
||||
r, err := dnsExchange(ctx, resolver, q)
|
||||
if err != nil {
|
||||
lastErr = err.Error()
|
||||
continue
|
||||
}
|
||||
anySuccess = true
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
for _, rr := range r.Answer {
|
||||
switch v := rr.(type) {
|
||||
case *dns.A:
|
||||
out = append(out, ForwardAddress{Type: "A", Address: v.A.String(), TTL: v.Hdr.Ttl})
|
||||
case *dns.AAAA:
|
||||
out = append(out, ForwardAddress{Type: "AAAA", Address: v.AAAA.String(), TTL: v.Hdr.Ttl})
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(out) > 0 || anySuccess {
|
||||
return out, ""
|
||||
}
|
||||
return out, lastErr
|
||||
}
|
||||
|
||||
func ipEqual(addr string, ip net.IP) bool {
|
||||
parsed := net.ParseIP(addr)
|
||||
if parsed == nil {
|
||||
return false
|
||||
}
|
||||
return parsed.Equal(ip)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue