From 850f4444d4c881b6bd1ffea95e0d3ba13c52d214 Mon Sep 17 00:00:00 2001
From: Mechiel Lukkien <mechiel@ueber.net>
Date: Fri, 13 Oct 2023 08:16:46 +0200
Subject: [PATCH] when suggesting DNS records, leave "IN" out

people will either paste the records in their zone file. in that case, the
records will inherit "IN" from earlier records, and there will always be one
record. if anyone uses a different class, their smart enough to know they need
to add IN manually.

plenty of people will add their records through some clunky web interface of
their dns operator. they probably won't even have the choice to set the class,
it'll always be IN.
---
 doc.go                                |  2 +-
 http/autoconf.go                      |  2 +-
 main.go                               |  4 +--
 mox-/admin.go                         | 36 +++++++++++++--------------
 testdata/integration/example.zone     | 18 +++++++-------
 testdata/integration/moxacmepebble.sh |  4 +--
 testdata/integration/moxmail2.sh      |  2 +-
 testdata/integration/reverse.zone     | 14 +++++------
 webadmin/admin.go                     | 22 ++++++++--------
 9 files changed, 52 insertions(+), 52 deletions(-)

diff --git a/doc.go b/doc.go
index 352aa4b..2e34d06 100644
--- a/doc.go
+++ b/doc.go
@@ -660,7 +660,7 @@ Common DANE TLSA record parameters are: dane-ee spki sha2-256, or 3 1 1,
 followed by a sha2-256 hash of the DER-encoded "SPKI" (subject public key info)
 from the certificate. An example DNS zone file entry:
 
-	_25._tcp.example.com. IN TLSA 3 1 1 133b919c9d65d8b1488157315327334ead8d83372db57465ecabf53ee5748aee
+	_25._tcp.example.com. TLSA 3 1 1 133b919c9d65d8b1488157315327334ead8d83372db57465ecabf53ee5748aee
 
 The first usable information from the pem file is used to compose the TLSA
 record. In case of selector "cert", a certificate is required. Otherwise the
diff --git a/http/autoconf.go b/http/autoconf.go
index e767555..3133891 100644
--- a/http/autoconf.go
+++ b/http/autoconf.go
@@ -133,7 +133,7 @@ func autoconfHandle(w http.ResponseWriter, r *http.Request) {
 }
 
 // Autodiscover from Microsoft, also used by Thunderbird.
-// User should create a DNS record: _autodiscover._tcp.<domain> IN SRV 0 0 443 <hostname or autodiscover.<domain>>
+// User should create a DNS record: _autodiscover._tcp.<domain> SRV 0 0 443 <hostname>
 //
 // In practice, autodiscover does not seem to work wit microsoft clients. A
 // connectivity test tool for outlook is available on
diff --git a/main.go b/main.go
index 2893fa1..f0ce3e7 100644
--- a/main.go
+++ b/main.go
@@ -1809,7 +1809,7 @@ Common DANE TLSA record parameters are: dane-ee spki sha2-256, or 3 1 1,
 followed by a sha2-256 hash of the DER-encoded "SPKI" (subject public key info)
 from the certificate. An example DNS zone file entry:
 
-	_25._tcp.example.com. IN TLSA 3 1 1 133b919c9d65d8b1488157315327334ead8d83372db57465ecabf53ee5748aee
+	_25._tcp.example.com. TLSA 3 1 1 133b919c9d65d8b1488157315327334ead8d83372db57465ecabf53ee5748aee
 
 The first usable information from the pem file is used to compose the TLSA
 record. In case of selector "cert", a certificate is required. Otherwise the
@@ -2131,7 +2131,7 @@ The DNS should be configured as a TXT record at $selector._domainkey.$domain.
 
 	record, err := r.Record()
 	xcheckf(err, "making record")
-	fmt.Print("<selector>._domainkey.<your.domain.> IN TXT ")
+	fmt.Print("<selector>._domainkey.<your.domain.> TXT ")
 	for record != "" {
 		s := record
 		if len(s) > 255 {
diff --git a/mox-/admin.go b/mox-/admin.go
index 18778f3..574203d 100644
--- a/mox-/admin.go
+++ b/mox-/admin.go
@@ -495,9 +495,9 @@ func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool) ([]
 			}
 			var s string
 			if hasDNSSEC {
-				s = fmt.Sprintf("_25._tcp.%-*s IN TLSA %s", 20+len(d)-len("_25._tcp."), h+".", tlsaRecord.Record())
+				s = fmt.Sprintf("_25._tcp.%-*s TLSA %s", 20+len(d)-len("_25._tcp."), h+".", tlsaRecord.Record())
 			} else {
-				s = fmt.Sprintf(";; _25._tcp.%-*s IN TLSA %s", 20+len(d)-len(";; _25._tcp."), h+".", tlsaRecord.Record())
+				s = fmt.Sprintf(";; _25._tcp.%-*s TLSA %s", 20+len(d)-len(";; _25._tcp."), h+".", tlsaRecord.Record())
 			}
 			records = append(records, s)
 			return nil
@@ -518,7 +518,7 @@ func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool) ([]
 	if d != h {
 		records = append(records,
 			"; For the machine, only needs to be created once, for the first domain added.",
-			fmt.Sprintf(`%-*s IN TXT "v=spf1 a -all"`, 20+len(d), h+"."), // ../rfc/7208:2263 ../rfc/7208:2287
+			fmt.Sprintf(`%-*s TXT "v=spf1 a -all"`, 20+len(d), h+"."), // ../rfc/7208:2263 ../rfc/7208:2287
 			"",
 		)
 	}
@@ -561,7 +561,7 @@ func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool) ([]
 				"; of multiple strings (max size of each is 255 bytes).",
 			)
 		}
-		s := fmt.Sprintf("%s._domainkey.%s.   IN TXT %s", name, d, TXTStrings(txt))
+		s := fmt.Sprintf("%s._domainkey.%s.   TXT %s", name, d, TXTStrings(txt))
 		records = append(records, s)
 
 	}
@@ -582,14 +582,14 @@ func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool) ([]
 		"; Specify the MX host is allowed to send for our domain and for itself (for DSNs).",
 		"; ~all means softfail for anything else, which is done instead of -all to prevent older",
 		"; mail servers from rejecting the message because they never get to looking for a dkim/dmarc pass.",
-		fmt.Sprintf(`%s.                    IN TXT "v=spf1 mx ~all"`, d),
+		fmt.Sprintf(`%s.                    TXT "v=spf1 mx ~all"`, d),
 		"",
 
 		"; Emails that fail the DMARC check (without aligned DKIM and without aligned SPF)",
 		"; should be rejected, and request reports. If you email through mailing lists that",
 		"; strip DKIM-Signature headers and don't rewrite the From header, you may want to",
 		"; set the policy to p=none.",
-		fmt.Sprintf(`_dmarc.%s.             IN TXT "%s"`, d, dmarcr.String()),
+		fmt.Sprintf(`_dmarc.%s.             TXT "%s"`, d, dmarcr.String()),
 		"",
 	)
 
@@ -598,8 +598,8 @@ func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool) ([]
 			"; Remote servers can use MTA-STS to verify our TLS certificate with the",
 			"; WebPKI pool of CA's (certificate authorities) when delivering over SMTP with",
 			"; STARTTLSTLS.",
-			fmt.Sprintf(`mta-sts.%s.            IN CNAME %s.`, d, h),
-			fmt.Sprintf(`_mta-sts.%s.           IN TXT "v=STSv1; id=%s"`, d, sts.PolicyID),
+			fmt.Sprintf(`mta-sts.%s.            CNAME %s.`, d, h),
+			fmt.Sprintf(`_mta-sts.%s.           TXT "v=STSv1; id=%s"`, d, sts.PolicyID),
 			"",
 		)
 	} else {
@@ -618,36 +618,36 @@ func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool) ([]
 		tlsrptr := tlsrpt.Record{Version: "TLSRPTv1", RUAs: [][]string{{uri.String()}}}
 		records = append(records,
 			"; Request reporting about TLS failures.",
-			fmt.Sprintf(`_smtp._tls.%s.         IN TXT "%s"`, d, tlsrptr.String()),
+			fmt.Sprintf(`_smtp._tls.%s.         TXT "%s"`, d, tlsrptr.String()),
 			"",
 		)
 	}
 
 	records = append(records,
 		"; Autoconfig is used by Thunderbird. Autodiscover is (in theory) used by Microsoft.",
-		fmt.Sprintf(`autoconfig.%s.         IN CNAME %s.`, d, h),
-		fmt.Sprintf(`_autodiscover._tcp.%s. IN SRV 0 1 443 autoconfig.%s.`, d, d),
+		fmt.Sprintf(`autoconfig.%s.         CNAME %s.`, d, h),
+		fmt.Sprintf(`_autodiscover._tcp.%s. SRV 0 1 443 autoconfig.%s.`, d, d),
 		"",
 
 		// ../rfc/6186:133 ../rfc/8314:692
 		"; For secure IMAP and submission autoconfig, point to mail host.",
-		fmt.Sprintf(`_imaps._tcp.%s.        IN SRV 0 1 993 %s.`, d, h),
-		fmt.Sprintf(`_submissions._tcp.%s.  IN SRV 0 1 465 %s.`, d, h),
+		fmt.Sprintf(`_imaps._tcp.%s.        SRV 0 1 993 %s.`, d, h),
+		fmt.Sprintf(`_submissions._tcp.%s.  SRV 0 1 465 %s.`, d, h),
 		"",
 		// ../rfc/6186:242
 		"; Next records specify POP3 and non-TLS ports are not to be used.",
 		"; These are optional and safe to leave out (e.g. if you have to click a lot in a",
 		"; DNS admin web interface).",
-		fmt.Sprintf(`_imap._tcp.%s.         IN SRV 0 1 143 .`, d),
-		fmt.Sprintf(`_submission._tcp.%s.   IN SRV 0 1 587 .`, d),
-		fmt.Sprintf(`_pop3._tcp.%s.         IN SRV 0 1 110 .`, d),
-		fmt.Sprintf(`_pop3s._tcp.%s.        IN SRV 0 1 995 .`, d),
+		fmt.Sprintf(`_imap._tcp.%s.         SRV 0 1 143 .`, d),
+		fmt.Sprintf(`_submission._tcp.%s.   SRV 0 1 587 .`, d),
+		fmt.Sprintf(`_pop3._tcp.%s.         SRV 0 1 110 .`, d),
+		fmt.Sprintf(`_pop3s._tcp.%s.        SRV 0 1 995 .`, d),
 		"",
 
 		"; Optional:",
 		"; You could mark Let's Encrypt as the only Certificate Authority allowed to",
 		"; sign TLS certificates for your domain.",
-		fmt.Sprintf("%s.                    IN CAA 0 issue \"letsencrypt.org\"", d),
+		fmt.Sprintf("%s.                    CAA 0 issue \"letsencrypt.org\"", d),
 	)
 	return records, nil
 }
diff --git a/testdata/integration/example.zone b/testdata/integration/example.zone
index 2af1c03..d7c2a57 100644
--- a/testdata/integration/example.zone
+++ b/testdata/integration/example.zone
@@ -5,14 +5,14 @@ $TTL 5m
 
 @ NS dns.example.
 
-moxacmepebble.mox1  IN A 172.28.1.10
-moxmail2.mox2       IN A 172.28.1.20
-dns                 IN A 172.28.1.30
-acmepebble          IN A 172.28.1.40
-test                IN A 172.28.1.50
-localserve.mox1     IN A 172.28.1.60
-postfixmail.postfix IN A 172.28.1.70
+moxacmepebble.mox1  A 172.28.1.10
+moxmail2.mox2       A 172.28.1.20
+dns                 A 172.28.1.30
+acmepebble          A 172.28.1.40
+test                A 172.28.1.50
+localserve.mox1     A 172.28.1.60
+postfixmail.postfix A 172.28.1.70
 
 postfix MX 10 postfixmail.postfix.example.
-postfixdkim0._domainkey.postfix IN TXT "v=DKIM1;h=sha256;t=s;k=ed25519;p=a4IsBTuMsSQjU+xVyx8KEd8eObis4FrCiV72OaEkvDY="
-postfix IN TXT "v=spf1 ip4:172.28.1.20 -all"
+postfixdkim0._domainkey.postfix TXT "v=DKIM1;h=sha256;t=s;k=ed25519;p=a4IsBTuMsSQjU+xVyx8KEd8eObis4FrCiV72OaEkvDY="
+postfix TXT "v=spf1 ip4:172.28.1.20 -all"
diff --git a/testdata/integration/moxacmepebble.sh b/testdata/integration/moxacmepebble.sh
index a51cfea..9772794 100755
--- a/testdata/integration/moxacmepebble.sh
+++ b/testdata/integration/moxacmepebble.sh
@@ -22,9 +22,9 @@ EOF
 
 (
 	cat /integration/example.zone;
-	sed -n '/^;/,/IN CAA/p' output.txt |
+	sed -n '/^;/,/CAA /p' output.txt |
 		# allow sending from postfix for mox1.example.
-		sed 's/mox1.example.  *IN TXT "v=spf1 mx ~all"/mox1.example. IN TXT "v=spf1 mx ip4:172.28.1.70 ~all"/'
+		sed 's/mox1.example.  *TXT "v=spf1 mx ~all"/mox1.example. TXT "v=spf1 mx ip4:172.28.1.70 ~all"/'
 ) >/integration/example-integration.zone
 unbound-control -s 172.28.1.30 reload # reload unbound with zone file changes
 
diff --git a/testdata/integration/moxmail2.sh b/testdata/integration/moxmail2.sh
index 06b6676..c4b2cfe 100755
--- a/testdata/integration/moxmail2.sh
+++ b/testdata/integration/moxmail2.sh
@@ -23,7 +23,7 @@ TLS:
 EOF
 
 # A fresh file was set up by moxacmepebble.
-sed -n '/^;/,/IN CAA/p' output.txt >>/integration/example-integration.zone
+sed -n '/^;/,/CAA /p' output.txt >>/integration/example-integration.zone
 unbound-control -s 172.28.1.30 reload # reload unbound with zone file changes
 
 mox -checkconsistency serve &
diff --git a/testdata/integration/reverse.zone b/testdata/integration/reverse.zone
index 0748009..067121a 100644
--- a/testdata/integration/reverse.zone
+++ b/testdata/integration/reverse.zone
@@ -3,10 +3,10 @@ $TTL 5m
 
 @ IN SOA dns.example. hostmaster.example. (1 0m 0m 0m 5m)
 
-10.1 IN PTR moxacmepebble.mox1.example.
-20.1 IN PTR moxmail2.mox2.example.
-30.1 IN PTR dns.example.
-40.1 IN PTR acmepebble.example.
-50.1 IN PTR test.example.
-60.1 IN PTR localserve.mox1.example.
-70.1 IN PTR postfixmail.postfix.example.
+10.1 PTR moxacmepebble.mox1.example.
+20.1 PTR moxmail2.mox2.example.
+30.1 PTR dns.example.
+40.1 PTR acmepebble.example.
+50.1 PTR test.example.
+60.1 PTR localserve.mox1.example.
+70.1 PTR postfixmail.postfix.example.
diff --git a/webadmin/admin.go b/webadmin/admin.go
index e9a807a..70b056f 100644
--- a/webadmin/admin.go
+++ b/webadmin/admin.go
@@ -976,10 +976,10 @@ EOF
 		if err != nil {
 			addf(&r.SPF.Errors, "Making SPF record for instructions: %s", err)
 		}
-		domainspf := fmt.Sprintf("%s IN TXT %s", domain.ASCII+".", mox.TXTStrings(dtxt))
+		domainspf := fmt.Sprintf("%s TXT %s", domain.ASCII+".", mox.TXTStrings(dtxt))
 
 		// Check SPF record for sending host. ../rfc/7208:2263 ../rfc/7208:2287
-		hostspf := fmt.Sprintf(`%s IN TXT "v=spf1 a -all"`, mox.Conf.Static.HostnameDomain.ASCII+".")
+		hostspf := fmt.Sprintf(`%s TXT "v=spf1 a -all"`, mox.Conf.Static.HostnameDomain.ASCII+".")
 
 		addf(&r.SPF.Instructions, "Ensure DNS TXT records like the following exists:\n\n\t%s\n\t%s\n\nIf you have an existing mail setup, with other hosts also sending mail for you domain, you should add those IPs as well. You could replace \"-all\" with \"~all\" to treat mail sent from unlisted IPs as \"softfail\", or with \"?all\" for \"neutral\".", domainspf, hostspf)
 	}()
@@ -1058,7 +1058,7 @@ EOF
 				addf(&r.DKIM.Errors, "Making DKIM record for instructions: %s", err)
 				continue
 			}
-			instr += fmt.Sprintf("\n\t%s._domainkey IN TXT %s\n", sel, mox.TXTStrings(txt))
+			instr += fmt.Sprintf("\n\t%s._domainkey TXT %s\n", sel, mox.TXTStrings(txt))
 		}
 		if instr != "" {
 			instr = "Ensure the following DNS record(s) exists, so mail servers receiving emails from this domain can verify the signatures in the mail headers:\n" + instr
@@ -1111,7 +1111,7 @@ EOF
 				} else if !accepts {
 					addf(&r.DMARC.Errors, "External destination does not accept reports (%s)", err)
 				}
-				extInstr = fmt.Sprintf("Ensure a DNS TXT record exists in the domain of the destination address to opt-in to receiving reports from this domain:\n\n\t%s._report._dmarc.%s. IN TXT \"v=DMARC1;\"\n\n", domain.ASCII, domConf.DMARC.DNSDomain.ASCII)
+				extInstr = fmt.Sprintf("Ensure a DNS TXT record exists in the domain of the destination address to opt-in to receiving reports from this domain:\n\n\t%s._report._dmarc.%s. TXT \"v=DMARC1;\"\n\n", domain.ASCII, domConf.DMARC.DNSDomain.ASCII)
 			}
 
 			uri := url.URL{
@@ -1124,7 +1124,7 @@ EOF
 		} else {
 			addf(&r.DMARC.Instructions, `Configure a DMARC destination in domain in config file.`)
 		}
-		instr := fmt.Sprintf("Ensure a DNS TXT record like the following exists:\n\n\t_dmarc IN TXT %s\n\nYou can start with testing mode by replacing p=reject with p=none. You can also request for the policy to be applied to a percentage of emails instead of all, by adding pct=X, with X between 0 and 100. Keep in mind that receiving mail servers will apply some anti-spam assessment regardless of the policy and whether it is applied to the message. The ruf= part requests daily aggregate reports to be sent to the specified address, which is automatically configured and reports automatically analyzed.", mox.TXTStrings(dmarcr.String()))
+		instr := fmt.Sprintf("Ensure a DNS TXT record like the following exists:\n\n\t_dmarc TXT %s\n\nYou can start with testing mode by replacing p=reject with p=none. You can also request for the policy to be applied to a percentage of emails instead of all, by adding pct=X, with X between 0 and 100. Keep in mind that receiving mail servers will apply some anti-spam assessment regardless of the policy and whether it is applied to the message. The ruf= part requests daily aggregate reports to be sent to the specified address, which is automatically configured and reports automatically analyzed.", mox.TXTStrings(dmarcr.String()))
 		addf(&r.DMARC.Instructions, instr)
 		if extInstr != "" {
 			addf(&r.DMARC.Instructions, extInstr)
@@ -1166,7 +1166,7 @@ EOF
 
 Ensure a DNS TXT record like the following exists:
 
-	_smtp._tls IN TXT %s
+	_smtp._tls TXT %s
 `, mox.TXTStrings(tlsrptr.String()))
 		} else {
 			addf(&r.TLSRPT.Errors, `Configure a TLSRPT destination in domain in config file.`)
@@ -1254,14 +1254,14 @@ When enabling MTA-STS, or updating a policy, always update the policy first (thr
 
 		addf(&r.MTASTS.Instructions, `Enable a policy through the configuration file. For new deployments, it is best to start with mode "testing" while enabling TLSRPT. Start with a short "max_age", so updates to your policy are picked up quickly. When confidence in the deployment is high enough, switch to "enforce" mode and a longer "max age". A max age in the order of weeks is recommended. If you foresee a change to your setup in the future, requiring different policies or MX records, you may want to dial back the "max age" ahead of time, similar to how you would handle TTL's in DNS record updates.`)
 
-		host := fmt.Sprintf("Ensure DNS CNAME/A/AAAA records exist that resolve mta-sts.%s to this mail server. For example:\n\n\t%s IN CNAME %s\n\n", domain.ASCII, "mta-sts."+domain.ASCII+".", mox.Conf.Static.HostnameDomain.ASCII+".")
+		host := fmt.Sprintf("Ensure DNS CNAME/A/AAAA records exist that resolve mta-sts.%s to this mail server. For example:\n\n\t%s CNAME %s\n\n", domain.ASCII, "mta-sts."+domain.ASCII+".", mox.Conf.Static.HostnameDomain.ASCII+".")
 		addf(&r.MTASTS.Instructions, host)
 
 		mtastsr := mtasts.Record{
 			Version: "STSv1",
 			ID:      time.Now().Format("20060102T150405"),
 		}
-		dns := fmt.Sprintf("Ensure a DNS TXT record like the following exists:\n\n\t_mta-sts IN TXT %s\n\nConfigure the ID in the configuration file, it must be of the form [a-zA-Z0-9]{1,31}. It represents the version of the policy. For each policy change, you must change the ID to a new unique value. You could use a timestamp like 20220621T123000. When this field exists, an SMTP server will fetch a policy at https://mta-sts.%s/.well-known/mta-sts.txt. This policy is served by mox.", mox.TXTStrings(mtastsr.String()), domain.Name())
+		dns := fmt.Sprintf("Ensure a DNS TXT record like the following exists:\n\n\t_mta-sts TXT %s\n\nConfigure the ID in the configuration file, it must be of the form [a-zA-Z0-9]{1,31}. It represents the version of the policy. For each policy change, you must change the ID to a new unique value. You could use a timestamp like 20220621T123000. When this field exists, an SMTP server will fetch a policy at https://mta-sts.%s/.well-known/mta-sts.txt. This policy is served by mox.", mox.TXTStrings(mtastsr.String()), domain.Name())
 		addf(&r.MTASTS.Instructions, dns)
 	}()
 
@@ -1318,7 +1318,7 @@ When enabling MTA-STS, or updating a policy, always update the policy first (thr
 		r.SRVConf.SRVs = map[string][]*net.SRV{}
 		for _, req := range reqs {
 			name := req.name + "_.tcp." + domain.ASCII
-			instr += fmt.Sprintf("\t%s._tcp.%-*s IN SRV 0 1 %d %s\n", req.name, len("_submissions")-len(req.name)+len(domain.ASCII+"."), domain.ASCII+".", req.port, req.host)
+			instr += fmt.Sprintf("\t%s._tcp.%-*s SRV 0 1 %d %s\n", req.name, len("_submissions")-len(req.name)+len(domain.ASCII+"."), domain.ASCII+".", req.port, req.host)
 			r.SRVConf.SRVs[req.name] = req.srvs
 			if err != nil {
 				addf(&r.SRVConf.Errors, "Looking up SRV record %q: %s", name, err)
@@ -1337,7 +1337,7 @@ When enabling MTA-STS, or updating a policy, always update the policy first (thr
 		defer logPanic(ctx)
 		defer wg.Done()
 
-		addf(&r.Autoconf.Instructions, "Ensure a DNS CNAME record like the following exists:\n\n\tautoconfig.%s IN CNAME %s\n\nNote: the trailing dot is relevant, it makes the host name absolute instead of relative to the domain name.", domain.ASCII+".", mox.Conf.Static.HostnameDomain.ASCII+".")
+		addf(&r.Autoconf.Instructions, "Ensure a DNS CNAME record like the following exists:\n\n\tautoconfig.%s CNAME %s\n\nNote: the trailing dot is relevant, it makes the host name absolute instead of relative to the domain name.", domain.ASCII+".", mox.Conf.Static.HostnameDomain.ASCII+".")
 
 		host := "autoconfig." + domain.ASCII + "."
 		ips, ourIPs, notOurIPs, err := lookupIPs(&r.Autoconf.Errors, host)
@@ -1364,7 +1364,7 @@ When enabling MTA-STS, or updating a policy, always update the policy first (thr
 		defer logPanic(ctx)
 		defer wg.Done()
 
-		addf(&r.Autodiscover.Instructions, "Ensure DNS records like the following exist:\n\n\t_autodiscover._tcp.%s IN SRV 0 1 443 autoconfig.%s\n\tautoconfig.%s IN CNAME %s\n\nNote: the trailing dots are relevant, it makes the host names absolute instead of relative to the domain name.", domain.ASCII+".", domain.ASCII+".", domain.ASCII+".", mox.Conf.Static.HostnameDomain.ASCII+".")
+		addf(&r.Autodiscover.Instructions, "Ensure DNS records like the following exist:\n\n\t_autodiscover._tcp.%s SRV 0 1 443 autoconfig.%s\n\tautoconfig.%s CNAME %s\n\nNote: the trailing dots are relevant, it makes the host names absolute instead of relative to the domain name.", domain.ASCII+".", domain.ASCII+".", domain.ASCII+".", mox.Conf.Static.HostnameDomain.ASCII+".")
 
 		_, srvs, _, err := resolver.LookupSRV(ctx, "autodiscover", "tcp", domain.ASCII+".")
 		if err != nil {