rio/internal/dns/dns.go

118 lines
2.5 KiB
Go
Raw Permalink Normal View History

2024-01-01 13:19:19 +00:00
package dns
2023-12-31 12:26:56 +00:00
import (
"net"
"errors"
2023-12-31 21:41:51 +00:00
"strings"
2023-12-31 12:26:56 +00:00
"time"
"github.com/patrickmn/go-cache"
)
2023-12-31 21:41:51 +00:00
const (
2024-01-01 13:19:19 +00:00
// TXT record name that lookupRepoTXT will try to lookup.
TxtRepoRecord = "_rio-pages."
// The key that the TXT record will have to start with, e.g.
// "repo=some-random-repo".
2023-12-31 21:41:51 +00:00
TxtRepoKey = "repo="
TxtCNAMERecord = "_rio-cname."
TxtCNAMEKey = "cname="
2023-12-31 21:41:51 +00:00
)
2023-12-31 12:26:56 +00:00
var (
2024-01-01 13:19:19 +00:00
// Cache for CNAME resolution results.
cnameCache = cache.New(1*time.Hour, 1*time.Hour)
// Cache for TXT resolution results.
2023-12-31 23:38:39 +00:00
txtRepoCache = cache.New(1*time.Hour, 1*time.Hour)
2023-12-31 12:26:56 +00:00
)
2024-01-01 13:19:19 +00:00
// Query the domain for the a repository redirect.
// Returns the new repository name or "", if we could not
// resolve a repository redirect.
func LookupRepoTXT(domain string) (string, error) {
2023-12-31 21:41:51 +00:00
repoLookup, found := txtRepoCache.Get(domain)
if found {
return repoLookup.(string), nil
}
txts, err := net.LookupTXT("_rio-pages." + domain)
if err != nil {
return "", err
}
repo := ""
for _, txt := range txts {
if !strings.HasPrefix(txt, TxtRepoKey) {
continue
}
repo = strings.TrimPrefix(txt, TxtRepoKey)
break
}
txtRepoCache.Set(domain, repo, cache.DefaultExpiration)
return repo, nil
}
2024-01-01 13:19:19 +00:00
// Query the domain for a CNAME record. Returns the resolved
// CNAME or "", if no CNAME could be queried.
func LookupCNAME(domain string) (string, error) {
2023-12-31 12:26:56 +00:00
cname, found := cnameCache.Get(domain)
if found {
return cname.(string), nil
}
cname, err := lookupCNAME(domain)
if err == nil {
cnameCache.Set(domain, cname, cache.DefaultExpiration)
return cname.(string), nil
}
altCname, err := lookupCNAMETxt(domain)
if err == nil {
cnameCache.Set(domain, altCname, cache.DefaultExpiration)
return altCname, nil
}
return "", err
}
// Lookup the CNAME by trying to find a CNAME RR. Contrary to net.LookupCNAME,
// this method fails if we find no CNAME RR.
func lookupCNAME(domain string) (string, error) {
2024-01-06 19:00:55 +00:00
query, err := net.LookupCNAME(domain)
2023-12-31 12:26:56 +00:00
if err == nil {
2024-01-06 19:00:55 +00:00
if query[len(query)-1] == '.' {
query = query[:len(query)-1]
}
// Fail if we have no CNAME RR.
if query == domain {
return "", errors.New("CNAME is equal to domain")
}
2024-01-06 19:00:55 +00:00
return query, nil
2023-12-31 12:26:56 +00:00
}
2023-12-31 21:41:51 +00:00
2023-12-31 12:26:56 +00:00
return "", err
}
// Performs an alternative CNAME lookup by looking for a special TXT record.
func lookupCNAMETxt(domain string) (string, error) {
txts, err := net.LookupTXT(TxtCNAMERecord+domain)
if err == nil {
for _, txt := range txts {
if !strings.HasPrefix(txt, TxtCNAMEKey) {
continue
}
return strings.TrimPrefix(txt, TxtCNAMEKey), nil
}
}
return "", err
}