2021-12-05 13:45:17 +00:00
|
|
|
package server
|
2021-03-16 23:34:31 +00:00
|
|
|
|
2021-07-08 23:16:00 +00:00
|
|
|
import (
|
|
|
|
"github.com/OrlovEvgeny/go-mcache"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache.
|
2021-11-25 15:12:28 +00:00
|
|
|
var DnsLookupCacheTimeout = 15 * time.Minute
|
|
|
|
|
2021-07-08 23:16:00 +00:00
|
|
|
// dnsLookupCache stores DNS lookups for custom domains
|
|
|
|
var dnsLookupCache = mcache.New()
|
|
|
|
|
2021-07-13 13:45:28 +00:00
|
|
|
// getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix.
|
|
|
|
// If everything is fine, it returns the target data.
|
2021-12-05 13:45:17 +00:00
|
|
|
func getTargetFromDNS(domain, mainDomainSuffix string) (targetOwner, targetRepo, targetBranch string) {
|
2021-07-08 23:16:00 +00:00
|
|
|
// 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, ".")
|
2021-12-05 13:45:17 +00:00
|
|
|
if err != nil || !strings.HasSuffix(cname, mainDomainSuffix) {
|
2021-07-08 23:16:00 +00:00
|
|
|
cname = ""
|
2021-07-13 08:28:06 +00:00
|
|
|
// TODO: check if the A record matches!
|
2021-07-08 23:16:00 +00:00
|
|
|
names, err := net.LookupTXT(domain)
|
|
|
|
if err == nil {
|
|
|
|
for _, name := range names {
|
|
|
|
name = strings.TrimSuffix(name, ".")
|
2021-12-05 13:45:17 +00:00
|
|
|
if strings.HasSuffix(name, mainDomainSuffix) {
|
2021-07-08 23:16:00 +00:00
|
|
|
cname = name
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ = dnsLookupCache.Set(domain, cname, DnsLookupCacheTimeout)
|
|
|
|
}
|
|
|
|
if cname == "" {
|
|
|
|
return
|
|
|
|
}
|
2021-12-05 13:45:17 +00:00
|
|
|
cnameParts := strings.Split(strings.TrimSuffix(cname, mainDomainSuffix), ".")
|
2021-07-08 23:16:00 +00:00
|
|
|
targetOwner = cnameParts[len(cnameParts)-1]
|
|
|
|
if len(cnameParts) > 1 {
|
2021-11-20 14:54:52 +00:00
|
|
|
targetRepo = cnameParts[len(cnameParts)-2]
|
2021-07-08 23:16:00 +00:00
|
|
|
}
|
|
|
|
if len(cnameParts) > 2 {
|
2021-11-20 14:54:52 +00:00
|
|
|
targetBranch = cnameParts[len(cnameParts)-3]
|
2021-07-08 23:16:00 +00:00
|
|
|
}
|
|
|
|
if targetRepo == "" {
|
|
|
|
targetRepo = "pages"
|
|
|
|
}
|
|
|
|
if targetBranch == "" && targetRepo != "pages" {
|
|
|
|
targetBranch = "pages"
|
|
|
|
}
|
|
|
|
// if targetBranch is still empty, the caller must find the default branch
|
2021-03-16 23:34:31 +00:00
|
|
|
return
|
|
|
|
}
|
2021-03-19 12:20:00 +00:00
|
|
|
|
2021-07-08 23:16:00 +00:00
|
|
|
// CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache.
|
2021-11-25 15:12:28 +00:00
|
|
|
var CanonicalDomainCacheTimeout = 15 * time.Minute
|
|
|
|
|
2021-07-08 23:16:00 +00:00
|
|
|
// canonicalDomainCache stores canonical domains
|
|
|
|
var canonicalDomainCache = mcache.New()
|
|
|
|
|
|
|
|
// checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`).
|
2021-12-05 13:45:17 +00:00
|
|
|
func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string) (canonicalDomain string, valid bool) {
|
2021-11-20 14:30:58 +00:00
|
|
|
domains := []string{}
|
2021-07-13 08:28:06 +00:00
|
|
|
if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok {
|
2021-11-20 14:30:58 +00:00
|
|
|
domains = cachedValue.([]string)
|
|
|
|
for _, domain := range domains {
|
|
|
|
if domain == actualDomain {
|
|
|
|
valid = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2021-07-08 23:16:00 +00:00
|
|
|
} else {
|
2021-07-13 08:28:06 +00:00
|
|
|
req := fasthttp.AcquireRequest()
|
2021-12-05 13:45:17 +00:00
|
|
|
req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken)
|
2021-07-13 08:28:06 +00:00
|
|
|
res := fasthttp.AcquireResponse()
|
|
|
|
|
2021-07-08 23:16:00 +00:00
|
|
|
err := upstreamClient.Do(req, res)
|
|
|
|
if err == nil && res.StatusCode() == fasthttp.StatusOK {
|
2021-11-20 14:30:58 +00:00
|
|
|
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
|
|
|
|
}
|
2021-07-08 23:16:00 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-05 13:45:17 +00:00
|
|
|
domains = append(domains, targetOwner+mainDomainSuffix)
|
2021-11-25 15:12:28 +00:00
|
|
|
if domains[len(domains)-1] == actualDomain {
|
2021-11-20 14:30:58 +00:00
|
|
|
valid = true
|
|
|
|
}
|
|
|
|
if targetRepo != "" && targetRepo != "pages" {
|
2021-11-25 15:12:28 +00:00
|
|
|
domains[len(domains)-1] += "/" + targetRepo
|
2021-07-08 23:16:00 +00:00
|
|
|
}
|
2021-11-25 15:12:28 +00:00
|
|
|
_ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, CanonicalDomainCacheTimeout)
|
2021-07-08 23:16:00 +00:00
|
|
|
}
|
2021-11-20 14:30:58 +00:00
|
|
|
canonicalDomain = domains[0]
|
2021-07-08 23:16:00 +00:00
|
|
|
return
|
|
|
|
}
|