package main import ( "github.com/OrlovEvgeny/go-mcache" "github.com/valyala/fasthttp" "net" "strings" "time" ) // DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. var DnsLookupCacheTimeout = 15 * time.Minute // dnsLookupCache stores DNS lookups for custom domains var dnsLookupCache = mcache.New() // getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error if cachedName, ok := dnsLookupCache.Get(domain); ok { cname = cachedName.(string) } else { cname, err = net.LookupCNAME(domain) cname = strings.TrimSuffix(cname, ".") if err != nil || !strings.HasSuffix(cname, string(MainDomainSuffix)) { cname = "" // TODO: check if the A record matches! names, err := net.LookupTXT(domain) if err == nil { for _, name := range names { name = strings.TrimSuffix(name, ".") if strings.HasSuffix(name, string(MainDomainSuffix)) { cname = name break } } } } _ = dnsLookupCache.Set(domain, cname, DnsLookupCacheTimeout) } if cname == "" { return } cnameParts := strings.Split(strings.TrimSuffix(cname, string(MainDomainSuffix)), ".") targetOwner = cnameParts[len(cnameParts)-1] if len(cnameParts) > 1 { targetRepo = cnameParts[len(cnameParts)-2] } if len(cnameParts) > 2 { targetBranch = cnameParts[len(cnameParts)-3] } if targetRepo == "" { targetRepo = "pages" } if targetBranch == "" && targetRepo != "pages" { targetBranch = "pages" } // if targetBranch is still empty, the caller must find the default branch return } // CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. var CanonicalDomainCacheTimeout = 15 * time.Minute // canonicalDomainCache stores canonical domains var canonicalDomainCache = mcache.New() // checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain string) (canonicalDomain string, valid bool) { domains := []string{} if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { domains = cachedValue.([]string) for _, domain := range domains { if domain == actualDomain { valid = true break } } } else { req := fasthttp.AcquireRequest() req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains") res := fasthttp.AcquireResponse() err := upstreamClient.Do(req, res) if err == nil && res.StatusCode() == fasthttp.StatusOK { for _, domain := range strings.Split(string(res.Body()), "\n") { domain = strings.ToLower(domain) domain = strings.TrimSpace(domain) domain = strings.TrimPrefix(domain, "http://") domain = strings.TrimPrefix(domain, "https://") if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { domains = append(domains, domain) } if domain == actualDomain { valid = true } } } domains = append(domains, targetOwner+string(MainDomainSuffix)) if domains[len(domains)-1] == actualDomain { valid = true } if targetRepo != "" && targetRepo != "pages" { domains[len(domains)-1] += "/" + targetRepo } _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, CanonicalDomainCacheTimeout) } canonicalDomain = domains[0] return }