mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-10-10 01:50:11 +08:00
🐞 TXT Records: only ONE string per record
Previously we were returning one TXT record with multiple strings for _sslip.io_. That did not work for ProtonMail's domain verification. It seems a convention that each TXT record has one string. _google.com_, for example, has a separate TXT record for each string. It turns out I had misunderstood the [StackExchange](https://serverfault.com/questions/815841/multiple-txt-fields-for-same-subdomain) thread. fixes (from ProtonMail domain verification): > Verification did not succeed, please try again in an hour.
This commit is contained in:
@@ -31,7 +31,7 @@ type DomainCustomization struct {
|
||||
AAAA []dnsmessage.AAAAResource
|
||||
CNAME dnsmessage.CNAMEResource
|
||||
MX []dnsmessage.MXResource
|
||||
TXT dnsmessage.TXTResource
|
||||
TXT []dnsmessage.TXTResource
|
||||
}
|
||||
|
||||
type DomainCustomizations map[string]DomainCustomization
|
||||
@@ -80,14 +80,12 @@ var (
|
||||
},
|
||||
},
|
||||
},
|
||||
// although multiple TXT records with multiple strings are allowed, we're sticking
|
||||
// with a single TXT record with multiple strings to simplify things, just like AWS
|
||||
// does: https://serverfault.com/questions/815841/multiple-txt-fields-for-same-subdomain
|
||||
TXT: dnsmessage.TXTResource{
|
||||
TXT: []string{
|
||||
"protonmail-verification=ce0ca3f5010aa7a2cf8bcc693778338ffde73e26", // protonmail verification; don't delete
|
||||
"v=spf1 include:_spf.protonmail.ch mx ~all",
|
||||
},
|
||||
// Although multiple TXT records with multiple strings are allowed, we're sticking
|
||||
// with a multiple TXT records with a single string apiece because that's what ProtonMail requires
|
||||
// and that's what google.com does.
|
||||
TXT: []dnsmessage.TXTResource{
|
||||
{TXT: []string{"protonmail-verification=ce0ca3f5010aa7a2cf8bcc693778338ffde73e26"}}, // ProtonMail verification; don't delete
|
||||
{TXT: []string{"v=spf1 include:_spf.protonmail.ch mx ~all"}}, // Sender Policy Framework
|
||||
},
|
||||
},
|
||||
// nameserver addresses; we get queries for those every once in a while
|
||||
@@ -307,29 +305,33 @@ func processQuestion(q dnsmessage.Question, b *dnsmessage.Builder) (logMessage s
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var txt dnsmessage.TXTResource
|
||||
txt, err = TXTResource(q.Name.String())
|
||||
var txts []dnsmessage.TXTResource
|
||||
txts, err = TXTResources(q.Name.String())
|
||||
if err != nil {
|
||||
err = noAnswersOnlyAuthorities(q, b, &logMessage)
|
||||
return
|
||||
}
|
||||
err = b.TXTResource(dnsmessage.ResourceHeader{
|
||||
Name: q.Name,
|
||||
Type: dnsmessage.TypeTXT,
|
||||
Class: dnsmessage.ClassINET,
|
||||
// aggressively expire (5 mins) TXT records, long enough to obtain a Let's Encrypt cert,
|
||||
// but short enough to free up frequently-used domains (e.g. 192.168.0.1.sslip.io) for the next user
|
||||
TTL: 300,
|
||||
Length: 0,
|
||||
}, txt)
|
||||
if err != nil {
|
||||
return
|
||||
var logMessageTXTss []string
|
||||
for _, txt := range txts {
|
||||
err = b.TXTResource(dnsmessage.ResourceHeader{
|
||||
Name: q.Name,
|
||||
Type: dnsmessage.TypeTXT,
|
||||
Class: dnsmessage.ClassINET,
|
||||
// aggressively expire (5 mins) TXT records, long enough to obtain a Let's Encrypt cert,
|
||||
// but short enough to free up frequently-used domains (e.g. 192.168.0.1.sslip.io) for the next user
|
||||
TTL: 300,
|
||||
Length: 0,
|
||||
}, txt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var logMessageTXTs []string
|
||||
for _, TXTstring := range txt.TXT {
|
||||
logMessageTXTs = append(logMessageTXTs, TXTstring)
|
||||
}
|
||||
logMessageTXTss = append(logMessageTXTss, `TXT "`+strings.Join(logMessageTXTs, `", "`)+`"`)
|
||||
}
|
||||
var logMessageTXTs []string
|
||||
for _, TXTstring := range txt.TXT {
|
||||
logMessageTXTs = append(logMessageTXTs, TXTstring)
|
||||
}
|
||||
logMessage += `TXT "` + strings.Join(logMessageTXTs, `", "`) + `"`
|
||||
logMessage += strings.Join(logMessageTXTss, " ")
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -459,12 +461,12 @@ func SOAResource(fqdnString string) dnsmessage.SOAResource {
|
||||
}
|
||||
}
|
||||
|
||||
func TXTResource(fqdnString string) (dnsmessage.TXTResource, error) {
|
||||
func TXTResources(fqdnString string) ([]dnsmessage.TXTResource, error) {
|
||||
// is it a customized TXT record? If so, return early
|
||||
if domain, ok := Customizations[fqdnString]; ok {
|
||||
return domain.TXT, nil
|
||||
}
|
||||
return dnsmessage.TXTResource{}, ErrNotFound
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func noAnswersOnlyAuthorities(q dnsmessage.Question, b *dnsmessage.Builder, logMessage *string) error {
|
||||
|
@@ -334,27 +334,27 @@ var _ = Describe("Xip", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("TXTResource()", func() {
|
||||
Describe("TXTResources()", func() {
|
||||
It("returns no TXT resources", func() {
|
||||
domain := "example.com."
|
||||
_, err := xip.TXTResource(domain)
|
||||
_, err := xip.TXTResources(domain)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
When("queried for the sslip.io domain", func() {
|
||||
It("returns mail-related TXT resources for the sslip.io domain", func() {
|
||||
domain := "sslip.io."
|
||||
txt, err := xip.TXTResource(domain)
|
||||
txt, err := xip.TXTResources(domain)
|
||||
Expect(err).To(Not(HaveOccurred()))
|
||||
Expect(len(txt.TXT)).To(Equal(2))
|
||||
Expect(txt.TXT[0]).To(MatchRegexp("protonmail-verification="))
|
||||
Expect(txt.TXT[1]).To(MatchRegexp("v=spf1"))
|
||||
Expect(len(txt)).To(Equal(2))
|
||||
Expect(txt[0].TXT[0]).To(MatchRegexp("protonmail-verification="))
|
||||
Expect(txt[1].TXT[0]).To(MatchRegexp("v=spf1"))
|
||||
})
|
||||
})
|
||||
When("a domain has been customized", func() { // Unnecessary, but confirms Golang's behavior for me, a doubting Thomas
|
||||
customDomain := "some-crazy-domain-name-no-really.io."
|
||||
xip.Customizations[customDomain] = xip.DomainCustomization{}
|
||||
It("returns no TXT resources", func() {
|
||||
_, err := xip.TXTResource(customDomain)
|
||||
_, err := xip.TXTResources(customDomain)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
delete(xip.Customizations, customDomain) // clean-up
|
||||
|
Reference in New Issue
Block a user