diff --git a/commands/getZones.go b/commands/getZones.go index 1d7e1dd6a..21384b774 100644 --- a/commands/getZones.go +++ b/commands/getZones.go @@ -38,17 +38,27 @@ ARGUMENTS: zone: One or more zones (domains) to download; or "all". FORMATS: - --format=js dnsconfig.js format (not perfect, but a decent first draft) + --format=js dnsconfig.js format (not perfect, just a decent first draft) + --format=djs js with disco commas --format=zone BIND Zonefile format --format=tsv TAB separated value (useful for AWK) --format=nameonly Just print the zone names +The columns in --format=tsv are: + FQDN (the label with the domain) + ShortName (just the label, "@" if it is the naked domain) + TTL + Record Type (A, AAAA, CNAME, etc.) + Target and arguments (quoted like in a zonefile) + +The --ttl flag only applies to zone/js/djs formats. + EXAMPLES: dnscontrol get-zones myr53 ROUTE53 example.com dnscontrol get-zones gmain GANDI_V5 example.comn other.com dnscontrol get-zones cfmain CLOUDFLAREAPI all dnscontrol get-zones -format=tsv bind BIND example.com - dnscontrol get-zones -format=js -out=draft.js glcoud GCLOUD example.com`, + dnscontrol get-zones -format=djs -out=draft.js glcoud GCLOUD example.com`, } }()) @@ -105,7 +115,7 @@ func (args *GetZoneArgs) flags() []cli.Flag { Name: "format", Destination: &args.OutputFormat, Value: "zone", - Usage: `Output format: js zone tsv nameonly`, + Usage: `Output format: js djs zone tsv nameonly`, }) flags = append(flags, &cli.StringFlag{ Name: "out", @@ -165,7 +175,7 @@ func GetZone(args GetZoneArgs) error { return nil } - // actually fetch all of the records + // fetch all of the records zoneRecs := make([]models.Records, len(zones)) for i, zone := range zones { recs, err := provider.GetZoneRecords(zone) @@ -175,14 +185,15 @@ func GetZone(args GetZoneArgs) error { zoneRecs[i] = recs } - // Write it out: + // Write the heading: - if args.OutputFormat == "js" { + if args.OutputFormat == "js" || args.OutputFormat == "djs" { fmt.Fprintf(w, `var %s = NewDnsProvider("%s", "%s");`+"\n", args.CredName, args.CredName, args.ProviderName) + fmt.Fprintf(w, `var REG_CHANGEME = NewRegistrar("ThirdParty", "NONE");`+"\n") } - // now print all zones + // print each zone for i, recs := range zoneRecs { zoneName := zones[i] @@ -194,19 +205,26 @@ func GetZone(args GetZoneArgs) error { prettyzone.WriteZoneFileRC(w, z.Records, zoneName, uint32(args.DefaultTTL), nil) fmt.Fprintln(w) - case "js": - fmt.Fprintf(w, `D("%s", REG_CHANGEME,`, zoneName) - fmt.Fprintf(w, "\n\tDnsProvider(%s)", args.CredName) + case "js", "djs": + sep := ",\n\t" // Commas at EOL + if args.OutputFormat == "djs" { + sep = "\n\t, " // Funky comma mode + } + fmt.Fprintf(w, `D("%s", REG_CHANGEME%s`, zoneName, sep) + var o []string + o = append(o, fmt.Sprintf("DnsProvider(%s)", args.CredName)) defaultTTL := uint32(args.DefaultTTL) if defaultTTL == 0 { defaultTTL = prettyzone.MostCommonTTL(recs) } if defaultTTL != models.DefaultTTL && defaultTTL != 0 { - fmt.Fprintf(w, "\n\tDefaultTTL(%d)", defaultTTL) + o = append(o, fmt.Sprintf("DefaultTTL(%d)", defaultTTL)) } for _, rec := range recs { - fmt.Fprint(w, formatDsl(zoneName, rec, defaultTTL)) + o = append(o, formatDsl(zoneName, rec, defaultTTL)) } + out := strings.Join(o, sep) + fmt.Fprint(w, strings.ReplaceAll(out, "\n\t, //", "\n\t//, ")) fmt.Fprint(w, "\n)\n") case "tsv": @@ -233,23 +251,47 @@ func formatDsl(zonename string, rec *models.RecordConfig, defaultTTL uint32) str } switch rec.Type { // #rtype_variations + case "CAA": + return makeCaa(rec, ttlop) case "MX": target = fmt.Sprintf("%d, '%s'", rec.MxPreference, rec.GetTargetField()) + case "SSHFP": + target = fmt.Sprintf("%d, %d, '%s'", rec.SshfpAlgorithm, rec.SshfpFingerprint, rec.GetTargetField()) case "SOA": + rec.Type = "//SOA" + target = fmt.Sprintf("'%s', '%s', %d, %d, %d, %d, %d", rec.GetTargetField(), rec.SoaMbox, rec.SoaSerial, rec.SoaRefresh, rec.SoaRetry, rec.SoaExpire, rec.SoaMinttl) + case "SRV": + target = fmt.Sprintf("%d, %d, %d, '%s'", rec.SrvPriority, rec.SrvWeight, rec.SrvPort, rec.GetTargetField()) + case "TLSA": + target = fmt.Sprintf("%d, %d, %d, '%s'", rec.TlsaUsage, rec.TlsaSelector, rec.TlsaMatchingType, rec.GetTargetField()) case "TXT": if len(rec.TxtStrings) == 1 { target = `'` + rec.TxtStrings[0] + `'` } else { target = `['` + strings.Join(rec.TxtStrings, `', '`) + `']` } + // TODO(tlim): If this is an SPF record, generate a SPF_BUILDER(). case "NS": // NS records at the apex should be NAMESERVER() records. if rec.Name == "@" { - return fmt.Sprintf(",\n\tNAMESERVER('%s')", target) + return fmt.Sprintf("NAMESERVER('%s')", target) } + target = "'" + target + "'" default: target = "'" + target + "'" } - return fmt.Sprintf(",\n\t%s('%s', %s%s)", rec.Type, rec.Name, target, ttlop) + return fmt.Sprintf("%s('%s', %s%s)", rec.Type, rec.Name, target, ttlop) +} + +func makeCaa(rec *models.RecordConfig, ttlop string) string { + var target string + if rec.CaaFlag == 128 { + target = fmt.Sprintf("'%s', '%s', CAA_CRITICAL", rec.CaaTag, rec.GetTargetField()) + } else { + target = fmt.Sprintf("'%s', '%s'", rec.CaaTag, rec.GetTargetField()) + } + return fmt.Sprintf("%s('%s', %s%s)", rec.Type, rec.Name, target, ttlop) + + // TODO(tlim): Generate a CAA_BUILDER() instead? } diff --git a/commands/gz_test.go b/commands/gz_test.go new file mode 100644 index 000000000..f395562bb --- /dev/null +++ b/commands/gz_test.go @@ -0,0 +1,80 @@ +package commands + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "testing" + + "github.com/andreyvit/diff" + + _ "github.com/StackExchange/dnscontrol/v2/providers/_all" +) + +func TestFormatTypes(t *testing.T) { + /* + Input: Converted to: Should match contents of: + test_data/$DOMAIN.zone js test_data/$DOMAIN.zone.js + test_data/$DOMAIN.zone tsv test_data/$DOMAIN.zone.tsv + test_data/$DOMAIN.zone zone test_data/$DOMAIN.zone.zone + */ + + for _, domain := range []string{"simple.com", "example.org"} { + t.Run(domain+"/js", func(t *testing.T) { testFormat(t, domain, "js") }) + t.Run(domain+"/tsv", func(t *testing.T) { testFormat(t, domain, "tsv") }) + t.Run(domain+"/zone", func(t *testing.T) { testFormat(t, domain, "zone") }) + } +} + +func testFormat(t *testing.T, domain, format string) { + t.Helper() + + expectedFilename := fmt.Sprintf("test_data/%s.zone.%s", domain, format) + outputFiletmpl := fmt.Sprintf("%s.zone.%s.*.txt", domain, format) + + outfile, err := ioutil.TempFile("", outputFiletmpl) + if err != nil { + log.Fatal(err) + } + defer os.Remove(outfile.Name()) + + // Convert test data to the experiment output. + gzargs := GetZoneArgs{ + ZoneNames: []string{domain}, + OutputFormat: format, + OutputFile: outfile.Name(), + CredName: "bind", + ProviderName: "BIND", + } + gzargs.CredsFile = "test_data/creds.json" + + // Read the zonefile and convert + err = GetZone(gzargs) + if err != nil { + log.Fatal(err) + } + + // Read the actual result: + got, err := ioutil.ReadFile(outfile.Name()) + if err != nil { + log.Fatal(err) + } + + // Read the expected result + want, err := ioutil.ReadFile(expectedFilename) + if err != nil { + log.Fatal(err) + } + + // // Update got -> want + // err = ioutil.WriteFile(expectedFilename, got, 0644) + // if err != nil { + // log.Fatal(err) + // } + + if w, g := string(want), string(got); w != g { + t.Errorf("testFormat mismatch (-got +want):\n%s", diff.LineDiff(g, w)) + } + +} diff --git a/commands/test_data/example.org.zone b/commands/test_data/example.org.zone new file mode 100644 index 000000000..8e22339df --- /dev/null +++ b/commands/test_data/example.org.zone @@ -0,0 +1,1150 @@ +; Copyright © 2006-2020 Phil Pennock +; No warranty: if it breaks, you get to cut your hands picking up the shards. +; Permission granted to duplicate in its entirety with authorship preserved, +; or redact as needed for test frameworks, or copy fragments for use in your +; own domains, or pretty much anything else except presenting it as your own +; creation. + +$TTL 7200 +$ORIGIN example.org. + +; This is adapted from a real zonefile. +; All IPs and DNS should be using address-spaces reserved for documentation or +; otherwise available for this usage. +; The original had a /28 routed to an onlink /32 in IPv4, and a /48 for IPv6. +; +; A couple of entries come from a different zone, to round out the examples. +; +; Some information has been redacted, eg Let's Encrypt account numbers. +; Some public keys are the same as they were in the original zone-file, so if +; you have tooling which parses ASN structure or whatever, they should still be +; good. +; If you have a DNS Observatory, you can probably identify the original zone +; through some of these. Good luck! +; +; Some of the IPv6 stuff shows a H:EX encoding scheme; that's real, not +; invented for the example.org redaction. I just changed the prefix. + +; {{{ SOA and NS records +example.org. 43200 SOA ns1.example.org. hostmaster.example.org. ( + 2020030700 ; serial + 7200 ; refresh period + 3600 ; retry interval + 10d ; expire time + 7200 ; default/negative TTL + ) + NS ns1.example.org. + NS ns2.example.org. + NS ns-a.example.net. + NS friend-dns.example.com. +; }}} SOA and NS records + +;;; ====================================================================== +; EMAIL FEDERATION {{{ +; These are not for clients within our domain. +; These are for sending and receiving email, and telling other mail-systems +; what can send email as us, or claim to be us in HELO, etc. +; +; For SPF: this affects "domains which aren't the domain in question"; we can +; ignore it for our own domains from systems under our control, as the +; mail-server we list here is the one which all mail is channeled through, so +; is what the outside world sees, so is the one listed. + + + MX 10 mx + TXT "v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all" +; CSA Priority/Weight/Port; +; Priority for CSA version, so 1 +; Weight: 1 unauthorized, 2 authorized, 3 unknown (bit-field) +; Port: assertions: 0 no assertion; 1 subdomains must use CSA +; Target: hostname for which sender IP must be in the RRsets of address records +; Top-level assertion: we list _all_ hostnames which send email out, so EHLO/HELO claiming to be us must be covered here. +; Because almost all mail routes through the mail-hub, we can make this claim; foo is exception +_client._smtp SRV 1 1 1 @ +_client._smtp.mx SRV 1 2 1 mx +_client._smtp.foo SRV 1 2 1 foo + +; }}} + +;;; ====================================================================== +; Authentication and directory services {{{ + +; _Service._Proto.Name TTL Class SRV Priority Weight Port Target +_kerberos._tcp SRV 10 1 88 kerb-service +_kerberos._udp SRV 10 1 88 kerb-service +_kpasswd._udp SRV 10 1 464 kerb-service +_kerberos-adm._tcp SRV 10 1 749 kerb-service +_kerberos TXT "EXAMPLE.ORG" + +; No LDAP service, and tell people that. +_ldap._tcp SRV 0 0 0 . +_ldap._udp SRV 0 0 0 . + +; PGP Universal & GnuPG use keys.$domain to find PGP keys, via LDAP + +; }}} + +;;; ====================================================================== +; Chat services {{{ + +; XMPP we divide up the hostnames for federation vs client access. +; Federation on xmpp-s2s, client access on xmpp. +; They are the same host, but we architect to be able to split. +_jabber._tcp SRV 10 2 5269 xmpp-s2s +_xmpp-server._tcp SRV 10 2 5269 xmpp-s2s +_xmpp-client._tcp SRV 10 2 5222 xmpp +; I don't think we need _im._xmpp and _pres._xmpp for a real present server, so +; skip them. If setting up a domain without XMPP, publish `0 0 0 .` for these too. +; _im._xmpp SRV ... +; _pres._xmpp SRV ... + +; RFC 3832 "Remote Discovery in SLP via DNS SRV" {exp}: _slpda.{_tcp,_udp} +; RFC 3861: _im. and _pres. for IM protocols; +; Instant Messaging SRV Protocol Label registry http://www.iana.org/assignments/im-srv-labels +; Presence SRV Protocol Label registry http://www.iana.org/assignments/pres-srv-labels/pres-srv-labels.xhtml +; So far, both only contain: _xmpp +; Thus _im._xmpp _pres._xmpp +; draft-loreto-simple-im-srv-label-02.txt adds _sip: _im._sip _pres._sip +; RFC 3921: _im._xmpp _pres._xmpp _xmpp._tcp +; SIP SRV: http://www.iana.org/assignments/sip-table +; _sip+d2t._tcp _sips+d2t._tcp _sip+d2u._udp _sip+d2s._sctp _sips+d2s._sctp +; RFC 4386: http://www.iana.org/assignments/pkix-parameters/pkix-parameters.xhtml +; _pkixrep. -> _ldap _http _ocsp +; Also possibly: _sip _sips _sipfederation _msrps + +_im._sip SRV 0 0 0 . +_pres._sip SRV 0 0 0 . +_sip+d2t._tcp SRV 0 0 0 . +_sips+d2t._tcp SRV 0 0 0 . +_sip+d2u._udp SRV 0 0 0 . +_sip+d2s._sctp SRV 0 0 0 . +_sips+d2s._sctp SRV 0 0 0 . + +; }}} + +;;; ====================================================================== +; Email service for use by clients within the domain. {{{ +; RFC 6186 (was: draft-daboo-srv-email-05.txt) +; RFC 8314 updates for submissions +; RFC 8314 updates for submissions (obsoletes all cleartext) +; RFC 8552 sets up a registry for all the underscore services and 8553 backfills it + +_submission._tcp SRV 10 10 587 smtp +_submissions._tcp SRV 10 10 465 smtp +_imap._tcp SRV 10 10 143 imap +_imaps._tcp SRV 10 10 993 imap +_pop3._tcp SRV 0 0 0 . +_pop3s._tcp SRV 0 0 0 . +_sieve._tcp SRV 10 10 4190 imap + +; }}} + +;;; ====================================================================== +; Other zone-top services {{{ + +; Where can people send questions or get more information +;;;@ RP hostmaster.example.org. dns-moreinfo.example.org. + +dns-moreinfo TXT ( + "Fred Bloggs, TZ=America/New_York" + "Chat-Service-X: @handle1" + "Chat-Service-Y: federated-handle@example.org" + ) + +; SKS withdrawn +_pgpkey-http._tcp SRV 0 0 0 . +_pgpkey-https._tcp SRV 0 0 0 . +_hkp._tcp SRV 0 0 0 . + +; WKD +; The SRV record should be disappearing because it was removed from the spec. +; In the meantime, point it to the host while being cognizant that GnuPG will +; use the hostname from the target of the SRV for the Host header. +_openpgpkey._tcp SRV 10 10 443 openpgpkey.example.org. + +; Not sure anything actually uses this, but ah well +; Avoid pointing at a CNAME, just use barbican directly +_finger._tcp SRV 10 10 79 barbican + +; https://wiki.libravatar.org/api/ +_avatars-sec._tcp SRV 10 10 443 avatars + +; For bare domain as a hostname, we try to avoid it as much as possible. +; Sometimes, it's necessary, eg finger. +; Once it exists in DNS though, HTTP requests will try to hit it when people +; don't type `www.` +; So we point the bare domain at a minimal jail with "not much" in it, and +; use packet filters to redirect the traffic to the correct places. +; For HTTP, we'll issue redirects to avoid this. For finger, we just respond. +@ A 192.0.2.1 +@ AAAA 2001:db8::1:1 + +; }}} + +;;; ====================================================================== +; Email cryptographic authentication {{{ + +; RFC5585 DomainKeys Identified Mail (DKIM) Service Overview +_adsp._domainkey TXT "dkim=all" ; RFC5617 unknown | all | discardable +; http://dmarc.org/draft-dmarc-base-00-01.html DMARC DKIM policy +; +; DMARC: beware that some validators require v/p with no intermediate values, +; which one reading of RFC7489 can support. So keep those together at the front. +_dmarc TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" + +; [This is a good place to have a link to documentation on local processes for +; updating DKIM keys] +; +; These are the real current (March 2020) values for the domain which this +; example was drawn from. +; Note that the domain dual-signs email with both RSA and Ed25519 keys. +; The domain also cycles DKIM keys every three months, but it's manual and the +; calendar reminder was missed in February. Oops. +; +d201911._domainkey TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4SmyE5Tz5/wPL8cb2AKuHnlFeLMOhAl1UX/NYaeDCKMWoBPTgZRT0jonKLmV2UscHdodXu5ZsLr/NAuLCp7HmPLReLz7kxKncP6ppveKxc1aq5SPTKeWe77p6BptlahHc35eiXsZRpTsEzrbEOainy1IWEd+w9p1gWbrSutwE22z0i4V88nQ9UBa1ks" "6cVGxXBZFovWC+i28aGs6Lc7cSfHG5+Mrg3ud5X4evYXTGFMPpunMcCsXrqmS5a+5gRSEMZhngha/cHjLwaJnWzKaywNWF5XOsCjL94QkS0joB7lnGOHMNSZBCcu542Y3Ht3SgHhlpkF9mIbIRfpzA9IoSQIDAQAB" +d201911e2._domainkey TXT "v=DKIM1; k=ed25519; p=GBt2k2L39KUb39fg5brOppXDHXvISy0+ECGgPld/bIo=" +; +d202003._domainkey TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/1tQvOEs7xtKNm7PbPgY4hQjwHVvqqkDb0+TeqZHYRSczQ3c0LFJrIDFiPIdwQe/7AuKrxvATSh/uXKZ3EP4ouMgROPZnUxVXENeetJj+pc3nfGwTKUBTTTth+SO74gdIWsntjvAfduzosC4ZkxbDwZ9c253qXARGvGu+LB/iAeq0ngEbm5fU13+Jo" "pv0d4dR6oGe9GvMEnGGLZzNrxWl1BPe2x5JZ5/X/3fW8vJx3OgRB5N6fqbAJ6HZ9kcbikDH4lPPl9RIoprFk7mmwno/nXLQYGhPobmqq8wLkDiXEkWtYa5lzujz3XI3Zkk8ZIOGvdbVVfAttT0IVPnYkOhQIDAQAB" +d202003e2._domainkey TXT "v=DKIM1; k=ed25519; p=DQI5d9sNMrr0SLDoAi071IFOyKnlbR29hAQdqVQecQg=" +; + +; http://tools.ietf.org/html/draft-ietf-marf-reporting-discovery-01 +_report TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +; These are for RFC8460 and the sender needs to support MTA-STS or DANE; if +; they only support one, we might get error complaints. +; SMTP TLS Reporting used `_smtp-tlsrpt` in +; but `_smtp._tls` by the time RFC 8460 was published. +_smtp._tls TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" + +; RFC 7489 § 7.1 +; Any domains which point their DMARC records to email in this domain need +; to be authorized by having a record here, saying "yes, it's known, go ahead +; and send their DMARC reports to us." +; +; This is not needed for our sub-domains, because they're the same +; "organizational domain" as us (organizational domain is the thing which is +; publicly registered, ie where the parent domain is in the public suffix +; list). +; +$ORIGIN _report._dmarc.example.org. +example.net TXT "v=DMARC1" +example.com TXT "v=DMARC1" +xn--2j5b.xn--9t4b11yi5a TXT "v=DMARC1" +special.test TXT "v=DMARC1" +xn--qck5b9a5eml3bze.xn--zckzah TXT "v=DMARC1" +$ORIGIN example.org. + +; https://datatracker.ietf.org/doc/draft-ietf-dane-smime/?include_text=1 +; Using Secure DNS to Associate Certificates with Domain Names For S/MIME +; draft-ietf-dane-smime-15 + +*._smimecert CNAME _ourca-smimea +; Whatever the LHS, it all uses the one set of CAs, mine, which is ECC. + +; }}} + +;;; ====================================================================== +; Zeroconf Delegation {{{ + +; Point clients at a sub-domain for Wide Area Bonjour +; "b" = browse domain +; "lb" = legacy browse domain (include domain in empty-string browses) +; "r" = registration domain +b._dns-sd._udp PTR field +lb._dns-sd._udp PTR field +r._dns-sd._udp PTR field + +field NS ns1.example.org. + NS ns2.example.org. +;;;field DS 50664 7 1 8AA19AF49BFBAE7103E3450FB19E7C4B88FA556A +;;;field DS 50664 7 2 D4EEDAE5EC46C3C1A3A6DC6BC4404F36BA00E4025562A9BC8F3261A9 D0F08F96 + +; }}} + +;;; ====================================================================== +; Hosts and SSH Fingerprints {{{ + +; SSH fingerprints +; RFC4255 Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints +; RFC6594 Use of the SHA-256 Algorithm with RSA, Digital Signature Algorithm (DSA), and Elliptic Curve DSA (ECDSA) in SSHFP Resource Records +; http://www.iana.org/assignments/dns-sshfp-rr-parameters/dns-sshfp-rr-parameters.xml +; +; On a host with a suitably up-to-date ssh-keygen, using the .pub files: +; ssh-keygen -r $hostname -f /etc/ssh/ssh_host_$type_key.pub +; +; Records are: SSHFP ${keytype_alg} ${hash_alg} ${hash_data} +; keytype_alg: 1/RSA 2/DSA 3/ECDSA 4/Ed25519 6/Ed448 +; hash_alg: 1/SHA-1 2/SHA-256 +; +;nb: SSH DSA currently constrained to 1024-bit, far too short, so dropping alg=2 from DNS +;nb: SHA-1 is dead, let's stop publishing it. + +; barbican: perimeter defense {{{ +; barbican runs perimeter general services, such as recursive DNS +; This is also the IP address used for NAT/RDR for private jails +; This is also now @ for example.org too. +; Deliberately no SSH here. +barbican A 192.0.2.1 +barbican AAAA 2001:db8::1:1 +barbican.ipv4 A 192.0.2.1 +barbican.ipv6 AAAA 2001:db8::1:1 +; }}} + +; megalomaniac: Onlink-address for main Colo box {{{ +megalomaniac A 198.51.100.254 + AAAA 2001:db8:ffef::254 +megalomaniac.ipv4 A 198.51.100.254 +megalomaniac.ipv6 AAAA 2001:db8:ffef::254 + +megalomaniac SSHFP 1 2 4e9ced94d3caf2ce915f85a63ce7279d5118a79ea03dac59cf4859b825d2f619 +megalomaniac SSHFP 3 2 d3556a3db83ab9ccec39dc6693dd2f3e28b178c9bba61880924821c426cc61eb +megalomaniac SSHFP 4 2 c60c9d9d4728668f5f46986ff0c5b416c5e913862c4970cbfe211a6f44a111b4 + +megalomaniac.ipv4 SSHFP 1 2 4e9ced94d3caf2ce915f85a63ce7279d5118a79ea03dac59cf4859b825d2f619 +megalomaniac.ipv4 SSHFP 3 2 d3556a3db83ab9ccec39dc6693dd2f3e28b178c9bba61880924821c426cc61eb +megalomaniac.ipv4 SSHFP 4 2 c60c9d9d4728668f5f46986ff0c5b416c5e913862c4970cbfe211a6f44a111b4 + +megalomaniac.ipv6 SSHFP 1 2 4e9ced94d3caf2ce915f85a63ce7279d5118a79ea03dac59cf4859b825d2f619 +megalomaniac.ipv6 SSHFP 3 2 d3556a3db83ab9ccec39dc6693dd2f3e28b178c9bba61880924821c426cc61eb +megalomaniac.ipv6 SSHFP 4 2 c60c9d9d4728668f5f46986ff0c5b416c5e913862c4970cbfe211a6f44a111b4 +; }}} + +; tower is the main login jail {{{ +tower A 192.0.2.42 +tower AAAA 2001:db8::1:42 +tower.ipv4 A 192.0.2.42 +tower.ipv6 AAAA 2001:db8::1:42 + +tower SSHFP 1 2 0f211d236e94768911a294f38653c4af6fa935a5b06c975d8162f59142571451 +tower SSHFP 3 2 88bf7b7401c11fa2e84871efb06cd73d8fc409154605b354db2dda0b82fe1160 +tower SSHFP 4 2 6d30900be0faaae73568fc007a87b4d076cf9a351ecacc1106aef726c34ad61d +tower.ipv4 SSHFP 1 2 0f211d236e94768911a294f38653c4af6fa935a5b06c975d8162f59142571451 +tower.ipv4 SSHFP 3 2 88bf7b7401c11fa2e84871efb06cd73d8fc409154605b354db2dda0b82fe1160 +tower.ipv4 SSHFP 4 2 6d30900be0faaae73568fc007a87b4d076cf9a351ecacc1106aef726c34ad61d +tower.ipv6 SSHFP 1 2 0f211d236e94768911a294f38653c4af6fa935a5b06c975d8162f59142571451 +tower.ipv6 SSHFP 3 2 88bf7b7401c11fa2e84871efb06cd73d8fc409154605b354db2dda0b82fe1160 +tower.ipv6 SSHFP 4 2 6d30900be0faaae73568fc007a87b4d076cf9a351ecacc1106aef726c34ad61d +; }}} + +; vcs for svn and git {{{ +vcs A 192.0.2.228 +vcs AAAA 2001:db8::48:4558:4456:4353 +vcs.ipv4 A 192.0.2.228 +vcs.ipv6 AAAA 2001:db8::48:4558:4456:4353 +git CNAME vcs +git.ipv4 CNAME vcs.ipv4 +git.ipv6 CNAME vcs.ipv6 +; svn is kerberized so has its own hostname and is only IPv6-accessible because +; we don't have IPv4 to spare for this. +svn AAAA 2001:db8::48:4558:73:766e + +vcs SSHFP 1 2 b518be390babdf43cb2d598aa6befa6ce6878546bf107b829d0cfc65253a97d4 +vcs SSHFP 3 2 e92545dc0bf501f72333ddeb7a37afc2c5b408ce39a3ad95fbc66236f0077323 +vcs SSHFP 4 2 02289441124a487095a6cda2e946c6a8ed9087faf3592ec4135536c3e615521c +vcs.ipv4 SSHFP 1 2 b518be390babdf43cb2d598aa6befa6ce6878546bf107b829d0cfc65253a97d4 +vcs.ipv4 SSHFP 3 2 e92545dc0bf501f72333ddeb7a37afc2c5b408ce39a3ad95fbc66236f0077323 +vcs.ipv4 SSHFP 4 2 02289441124a487095a6cda2e946c6a8ed9087faf3592ec4135536c3e615521c +vcs.ipv6 SSHFP 1 2 b518be390babdf43cb2d598aa6befa6ce6878546bf107b829d0cfc65253a97d4 +vcs.ipv6 SSHFP 3 2 e92545dc0bf501f72333ddeb7a37afc2c5b408ce39a3ad95fbc66236f0077323 +vcs.ipv6 SSHFP 4 2 02289441124a487095a6cda2e946c6a8ed9087faf3592ec4135536c3e615521c +; }}} + +; nsauth is the authoritative DNS server {{{ +nsauth A 192.0.2.53 +nsauth AAAA 2001:db8::53:1 +nsauth.ipv4 A 192.0.2.53 +nsauth.ipv6 AAAA 2001:db8::53:1 + +nsauth SSHFP 1 2 895804ae022fff643b2677563cb850607c5bb564d9919896c521098c8abc40f2 +nsauth SSHFP 3 2 28a65470badae611375747e1a803211c41e3d71e97741fa92ccbdf7b01f34e42 +nsauth SSHFP 4 2 6e10445c0649c03fa83e18b1873e5b89b3a20893ecb48d01e7cedb3dd563ecf0 +nsauth.ipv4 SSHFP 1 2 895804ae022fff643b2677563cb850607c5bb564d9919896c521098c8abc40f2 +nsauth.ipv4 SSHFP 3 2 28a65470badae611375747e1a803211c41e3d71e97741fa92ccbdf7b01f34e42 +nsauth.ipv4 SSHFP 4 2 6e10445c0649c03fa83e18b1873e5b89b3a20893ecb48d01e7cedb3dd563ecf0 +nsauth.ipv6 SSHFP 1 2 895804ae022fff643b2677563cb850607c5bb564d9919896c521098c8abc40f2 +nsauth.ipv6 SSHFP 3 2 28a65470badae611375747e1a803211c41e3d71e97741fa92ccbdf7b01f34e42 +nsauth.ipv6 SSHFP 4 2 6e10445c0649c03fa83e18b1873e5b89b3a20893ecb48d01e7cedb3dd563ecf0 + +; These are the entries which appear in glue +ns1 A 192.0.2.53 +ns1 AAAA 2001:db8::53:1 +ns2 A 203.0.113.53 +ns2 AAAA 2001:db8:113::53 + +; }}} + +; hermes for mail {{{ +; The raw IPv6 for this match the SMTP and IMAP _submission_ aliases +; for various reasons, but not the MX IPv6 (which also goes there). +hermes A 192.0.2.25 +hermes AAAA 2001:db8::48:4558:736d:7470 +hermes AAAA 2001:db8::48:4558:696d:6170 +hermes.ipv4 A 192.0.2.25 +hermes.ipv6 AAAA 2001:db8::48:4558:736d:7470 +hermes.ipv6 AAAA 2001:db8::48:4558:696d:6170 +hermes SSHFP 1 2 4472ff5bd0528cd49216af4503ba6a1c48f121d0292a31d6af193e5000af4966 +hermes SSHFP 3 2 eaba20c1565676a5229184ccfcf82d0ee408f91757a67d9fa51a0b6f3db4a33b +hermes SSHFP 4 2 a9d89920e599d04363c8b35a4ce66c1ed257ea1d16981f060b6aed080bbb7a7c +hermes.ipv4 SSHFP 1 2 4472ff5bd0528cd49216af4503ba6a1c48f121d0292a31d6af193e5000af4966 +hermes.ipv4 SSHFP 3 2 eaba20c1565676a5229184ccfcf82d0ee408f91757a67d9fa51a0b6f3db4a33b +hermes.ipv4 SSHFP 4 2 a9d89920e599d04363c8b35a4ce66c1ed257ea1d16981f060b6aed080bbb7a7c +hermes.ipv6 SSHFP 1 2 4472ff5bd0528cd49216af4503ba6a1c48f121d0292a31d6af193e5000af4966 +hermes.ipv6 SSHFP 3 2 eaba20c1565676a5229184ccfcf82d0ee408f91757a67d9fa51a0b6f3db4a33b +hermes.ipv6 SSHFP 4 2 a9d89920e599d04363c8b35a4ce66c1ed257ea1d16981f060b6aed080bbb7a7c +; }}} + +; other top-level base service hostnames (no SSH) {{{ +; other IPv4 and IPv6 is routed to unredoubted and then configured up locally +kerb-service A 192.0.2.88 +kerb-service AAAA 2001:db8::48:4558:6b65:7262 +security A 192.0.2.92 +security AAAA 2001:db8::48:4558:53:4543 +security.ipv4 A 192.0.2.92 +security.ipv6 AAAA 2001:db8::48:4558:53:4543 +services A 192.0.2.93 +services AAAA 2001:db8::48:4558:5345:5256 +services.ipv4 A 192.0.2.93 +services.ipv6 AAAA 2001:db8::48:4558:5345:5256 +; }}} + +; CNAMEs and things we wish were ALIAS or auto-made: {{{ + +; https://www.ietf.org/id/draft-koch-openpgp-webkey-service-07.txt +; (thru -06 it was SRV records); this is constructed from the email address, +; so we don't need .ipvX variants. +; I don't want CNAME as target of SRV; so grep-bait: CNAME security +openpgpkey A 192.0.2.92 +openpgpkey AAAA 2001:db8::48:4558:53:4543 + +finger CNAME barbican +finger.ipv4 CNAME barbican.ipv4 +finger.ipv6 CNAME barbican.ipv6 + +; avatars can't be a CNAME and the target of an SRV. +; greb-bait: CNAME services +avatars A 192.0.2.93 + AAAA 2001:db8::48:4558:5345:5256 +dict CNAME services +people CNAME services +people.ipv4 CNAME services.ipv4 +people.ipv6 CNAME services.ipv6 +wpad CNAME services +www CNAME services +www.ipv4 CNAME services.ipv4 +www.ipv6 CNAME services.ipv6 + +; }}} + +; Hosts and SSH Fingerprints }}} + +;;; ====================================================================== +; Zone Identity Services {{{ + +; CAA: originally:RFC 6844 ; currently: RFC 8659, with extensions in RFC 8657 +; +; issue/issuewild tags use values which are "rest of data" (not Strings, no 255 +; limit) and which are a domain name, followed optionally by parameters, each +; individual parameter preceded by a `;` semi-colon; parameters are defined +; per-Issuer. A missing domain-name is allowed, no parameters defined for that +; case. +; +; Even though RFC 8659 says "The semantics of parameters to the issue Property +; Tag are determined by the Issuer alone.", RFC 8657 proceeds to define +; semantics for some parameters. I think this is more "if folks do stick to +; one common schema then it's easier for others to build tools, but still each +; Issuer can set their own meanings and ignore this if they really want". +; This is backed by 8657 IANA Considerations. +; But then 8657 then imposes MUST requirements upon ACME-using systems. +; +; RFC 8657 adds: `accounturi`, `validationmethods`; can repeat the CAA record +; with the same domain but a different accounturi each time. + +; https://docs.aws.amazon.com/acm/latest/userguide/setup-caa.html +; Any of: "amazon.com" "amazontrust.com" "awstrust.com" "amazonaws.com" +; No info re RFC8657 as of 2019-12-31 +; +; https://letsencrypt.org/docs/caa/ +; "Let’s Encrypt’s identifying domain name for CAA is letsencrypt.org. This is officially documented in our Certification Practice Statement (CPS), section 4.2.1." +; https://letsencrypt.org/documents/isrg-cps-v2.0/ +; ISRG checks for relevant CAA records prior to issuing certificates. The CA acts in accordance with CAA records if present. The CA’s CAA identifying domain is ‘letsencrypt.org’. +; 2019-12-31: can find no docs re accounturi, but community forum posts +; showing that the account from the ACME client data works (after a bug was +; fixed re ACMEv2 URLs) +; +; https://www.digicert.com/dns-caa-rr-check.htm +; Equivalent: "digicert.com" "www.digicert.com" "digicert.ne.jp" "cybertrust.ne.jp" "symantec.com" "thawte.com" "geotrust.com" "rapidssl.com" +; 2019-12-31: no accounturi docs +; +; https://ccadb-public.secure.force.com/ccadb/AllCAAIdentifiersReport +; HTML page, all CAA identifiers known to the Common CA Database; + +; 2019-12-31: I am not using Amazon inside example.org domain; that's on the blog at bridge.example.com +; only lists LE. +; Beware that our LetsEncrypt automation tooling uses multiple email addresses +; for different purposes, but all hostnames within example.org are using the +; noc@example.org address, so that's the only one we need. + +; Let's Encrypt: +; Prod/1234567 == tower / noc@example.org +; Stag/23456789 = chat2 / noc+chat2@example.net +; Prod/76543210 = chat2 / noc+chat2@example.net + +@ CAA 0 issue "example.net" +@ CAA 0 issue "letsencrypt.org\; accounturi=https://acme-v01.api.letsencrypt.org/acme/reg/1234567" +@ CAA 0 issue "letsencrypt.org\; accounturi=https://acme-staging-v02.api.letsencrypt.org/acme/acct/23456789" +@ CAA 0 issue "letsencrypt.org\; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/76543210" +;@ CAA 0 issue "amazonaws.com" +@ CAA 0 issuewild ";" +@ CAA 0 iodef "mailto:security@example.org" + +; Zone Identity Services }}} + +;;; ====================================================================== +; TLSA Records {{{ + +; TLSA: Usage Selector Matching-Type CAdata +; Usage==2 : CA anchor, no PKIX requirement +; Selector==0 : match entire cert +; Selector==1 : match public key +; MT: 0=exact match, 1=sha256, 2=sha512 +; RFCs: 6698 6394 +; Naming from RFC 7218 (draft-ietf-dane-registry-acronyms-03.txt): +; * Usages: PKIX-TA(0) PKIX-EE(1) DANE-TA(2) DANE-EE(3) PrivCert(255) +; * Selectors: Cert(0) SPKI(1) PrivSel(255) +; * Matching Types: Full(0) SHA2-256(1) SHA2-512(2) PrivMatch(255) + +; We don't use -example-tlsa-full any more; that was our v3 CA, and in the meantime +; the client tooling for TLSA has matured and we can use better matching. +; +; _example-tlsa-full TLSA ( 2 0 0 30.... ) ; OurCA3 PKIX-less trust anchor +; +; openssl x509 -inform pem -outform der -in OurCA3.pem| perl -pe 's/(.)/sprintf "%02x", ord $1/aesg';echo +; openssl x509 -inform pem -outform der -in OurCA3.pem| perl -pe 's/(.)/sprintf "%02x", ord $1/aesg' | perl -pe 's/(.{72})/$1\n/g';echo + +; For our own CAs, we use Selector 0 to match the entire cert, because we +; control the horizontal and the vertical. +; For CAs we use, we use Selector 1, so that if they reissue their signing cert +; then our DNS records remain valid: as long the as public key is unchanged, +; the 2/1/1 will be unchanged too. +; +; We typically avoid 3/x/x for individual certs or public keys directly in DNS: +; we don't want to have to update a static zonefile for cert re-issuance, and +; even with a dynamic zonefile, any complicated pre-issuance schemes or timing +; dances to deal with DNS TTLs fall down when there's an emergency revocation +; or replacement. Just pin the issuing CA with 2/x/x and be done with it. + +; OurCA {{{ +_ourcaca4-tlsa TLSA ( ; OurCA4 PKIX-less trust anchor + 02 00 01 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 + ; TLSA DANE-TA CERT SHA2-256 + ) +; danetool --tlsa-rr --host=foo --ca --x509 --load-certificate=OurCA4.pem + +_ourcaca5-tlsa TLSA ( ; OurCA5 PKIX-less trust anchor + 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 + ; TLSA DANE-TA CERT SHA2-256 + ) +; danetool --tlsa-rr --host=foo --ca --x509 --load-certificate=OurCA5.pem + +; SMIMEA {{{ + +; Note that the SMIME email signatures include the CA usually; mutt certainly does, +; so folks getting signed email from me _have_ the CA. So no need to use 2/0/0 here. +; +; That _was_: +; openssl x509 -inform pem -outform der -in OurCA5.pem | perl -pe 's/(.)/sprintf "%02x", ord $1/aesg' | perl -pe 's/(.{72})/$1\n/g';echo +; +; but instead we can just use the signatures. +; Since it's just an X.509 cert and almost the same as a TLSA record, use danetool. +; Heck, copy the _ourcaca5-tlsa RRset and change the RRtypes. + +;;;_ourcaca5-smimea SMIMEA ( ; OurCA5 PKIX-less trust anchor +;;; 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +;;; ; SMIMEA DANE-TA CERT SHA2-256 +;;; ) +; danetool --tlsa-rr --host=foo --ca --x509 --load-certificate=OurCA5.pem + +; This one should be a _set_ of CAs, letting me add OurCA6 (Ed25519) when the time comes. +;;;_ourca-smimea SMIMEA ( ; OurCA5 PKIX-less trust anchor +;;; 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +;;; ; SMIMEA DANE-TA CERT SHA2-256 +;;; ) + +; OurCA }}} + +; SMIMEA }}} + +; Other CAs, standalone {{{ +_cacert-c3-tlsa TLSA ( ; CACert Class 3 PKIX-less trust anchor + 02 00 01 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 + ; TLSA DANE-TA CERT SHA2-256 + ) +; danetool --tlsa-rr --host=foo --ca --x509 --load-certificate=cacert-class3.pem + +; For Let's Encrypt, where they have multiple signing paths, we use public-key +; hashing, not certificate hashing. +; This avoids breakage given, eg, IdenTrust vs other authority paths +; +;_letsencrypt-tlsa TLSA ( 02 01 01 0b9fa5a59eed715c26c1020c711b4f6ec42d58b0015e14337a39dad301c5afc3 ) ; ISRG Root X1 +_letsencrypt-tlsa TLSA ( 02 01 01 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 ) ; X1 & X3 +_letsencrypt-tlsa TLSA ( 02 01 01 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b ) ; X2 & X4 + +; danetool --tlsa-rr --host=foo --ca --load-certificate=letsencrypt_rsa_authority_x1.pem + +; edit results of: for F in AmazonRootCA*; danetool --tlsa-rr --host=$F --ca --x509 --load-certificate=$F +_amazon-tlsa TLSA ( 02 00 01 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e ) ; AmazonRootCA1 +_amazon-tlsa TLSA ( 02 00 01 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 ) ; AmazonRootCA2 +_amazon-tlsa TLSA ( 02 00 01 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 ) ; AmazonRootCA3 +_amazon-tlsa TLSA ( 02 00 01 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 ) ; AmazonRootCA4 + +; Other CAs, standalone }}} + +; Combined CA TLSA records {{{ +; These let us migrate between CAs without any outage, or have contingency plans. + +; _ourca-tlsa is a pairing anchor, handling both CA4 and CA5. +_ourca-tlsa TLSA ( 02 00 01 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 ) ; OurCA4 +_ourca-tlsa TLSA ( 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 ) ; OurCA5 + +; Used for migrations CACert -> OurCA4 +_ourca-cacert-tlsa TLSA 02 00 01 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 ; OurCA4 +_ourca-cacert-tlsa TLSA 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 ; OurCA5 +_ourca-cacert-tlsa TLSA 02 00 01 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 ; cacert-c3 + +; This mostly means "stuff which used to be on our own CA but for which we now use Let's Encrypt because others might see it" +_ourca-le-tlsa TLSA ( 02 00 01 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 ) ; OurCA4 +_ourca-le-tlsa TLSA ( 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 ) ; OurCA5 +_ourca-le-tlsa TLSA ( 02 01 01 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 ) ; letsencrypt X1 & X3 +_ourca-le-tlsa TLSA ( 02 01 01 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b ) ; letsencrypt X2 & X4 + +; Don't ask +_ourca-cacert-le-tlsa TLSA ( 02 00 01 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 ) ; OurCA4 +_ourca-cacert-le-tlsa TLSA ( 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 ) ; OurCA5 +_ourca-cacert-le-tlsa TLSA ( 02 00 01 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 ) ; cacert-c3 +_ourca-cacert-le-tlsa TLSA ( 02 01 01 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 ) ; letsencrypt X1 & X3 +_ourca-cacert-le-tlsa TLSA ( 02 01 01 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b ) ; letsencrypt X2 & X4 + +; Stuff we migrate from CACert to Let's Encrypt; was never private CA, we +; always wanted others to be able to validate. +_cacert-le-tlsa TLSA ( 02 00 01 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 ) ; cacert-c3 +_cacert-le-tlsa TLSA ( 02 01 01 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 ) ; letsencrypt X1 & X3 +_cacert-le-tlsa TLSA ( 02 01 01 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b ) ; letsencrypt X2 & X4 + +; Some stuff we move between LE and AWS CloudFront in front of an S3 bucket +_le-amazon-tlsa TLSA ( 02 01 01 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 ) ; letsencrypt X1 & X3 +_le-amazon-tlsa TLSA ( 02 01 01 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b ) ; letsencrypt X2 & X4 +_le-amazon-tlsa TLSA ( 02 00 01 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e ) ; AmazonRootCA1 +_le-amazon-tlsa TLSA ( 02 00 01 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 ) ; AmazonRootCA2 +_le-amazon-tlsa TLSA ( 02 00 01 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 ) ; AmazonRootCA3 +_le-amazon-tlsa TLSA ( 02 00 01 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 ) ; AmazonRootCA4 + +; All The Current Bases +_ourca-le-amazon-tlsa TLSA ( 02 00 01 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 ) ; OurCA4 +_ourca-le-amazon-tlsa TLSA ( 02 00 01 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 ) ; OurCA5 +_ourca-le-amazon-tlsa TLSA ( 02 01 01 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 ) ; letsencrypt X1 & X3 +_ourca-le-amazon-tlsa TLSA ( 02 01 01 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b ) ; letsencrypt X2 & X4 +_ourca-le-amazon-tlsa TLSA ( 02 00 01 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e ) ; AmazonRootCA1 +_ourca-le-amazon-tlsa TLSA ( 02 00 01 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 ) ; AmazonRootCA2 +_ourca-le-amazon-tlsa TLSA ( 02 00 01 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 ) ; AmazonRootCA3 +_ourca-le-amazon-tlsa TLSA ( 02 00 01 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 ) ; AmazonRootCA4 + +; Combined CA TLSA records }}} + +; TLSA Records }}} + +;;; ====================================================================== +; TLSA Referencing {{{ + +; full: +; openssl x509 -inform pem -outform der -in OurCA3.pem| perl -pe 's/(.)/sprintf "%02x", ord $1/aesg' | perl -pe 's/(.{72})/$1\n/g';echo +; short: +; danetool --tlsa-rr --host=foo --ca --x509 --load-certificate=some-company.classN.server.shaN.ca.pem + +; Web and other HTTP Transport +_443._tcp.www CNAME _ourca-le-tlsa +_443._tcp.www.ipv4 CNAME _ourca-le-tlsa +_443._tcp.www.ipv6 CNAME _ourca-le-tlsa +_443._tcp.people CNAME _ourca-le-tlsa +_443._tcp.people.ipv4 CNAME _ourca-le-tlsa +_443._tcp.people.ipv6 CNAME _ourca-le-tlsa + +_443._tcp.git CNAME _ourca-le-tlsa +_443._tcp.svn CNAME _ourca-le-tlsa + +; XMPP / Jabber +_5222._tcp.xmpp CNAME _ourca-le-tlsa +_5223._tcp.xmpp CNAME _ourca-le-tlsa +_5269._tcp.xmpp-s2s CNAME _ourca-le-tlsa + +; Email +_25._tcp.mx CNAME _ourca-le-tlsa +_26._tcp.mx CNAME _ourca-le-tlsa +_27._tcp.mx CNAME _ourca-le-tlsa +; the ....46 are the SRV results, the non-46 may be manually configured +; the 1xxx are for potential debugging +; there's too much historical cruft here, from when I had one IPv4 address and +; a bazillion IPv6 addresses. +_465._tcp.smtp46 CNAME _ourca-le-tlsa +_587._tcp.smtp46 CNAME _ourca-le-tlsa +_1465._tcp.smtp46 CNAME _ourca-le-tlsa +_1587._tcp.smtp46 CNAME _ourca-le-tlsa +_465._tcp.smtp CNAME _ourca-le-tlsa +_587._tcp.smtp CNAME _ourca-le-tlsa +_1465._tcp.smtp CNAME _ourca-le-tlsa +_1587._tcp.smtp CNAME _ourca-le-tlsa +_143._tcp.imap46 CNAME _ourca-le-tlsa +_993._tcp.imap46 CNAME _ourca-le-tlsa +_143._tcp.imap CNAME _ourca-le-tlsa +_993._tcp.imap CNAME _ourca-le-tlsa +; except for sieve, where there's only IPv6 +_4190._tcp.imap CNAME _ourca-le-tlsa +; + +www.security CNAME security +www.security.ipv4 CNAME security.ipv4 +www.security.ipv6 CNAME security.ipv6 +_443._tcp.www.security CNAME _ourca-le-tlsa +_443._tcp.www.security.ipv4 CNAME _ourca-le-tlsa +_443._tcp.www.security.ipv6 CNAME _ourca-le-tlsa +_443._tcp.security CNAME _ourca-le-tlsa +_443._tcp.security.ipv4 CNAME _ourca-le-tlsa +_443._tcp.security.ipv6 CNAME _ourca-le-tlsa + +; _acme-challenge.www.security.example.org. 120 TXT "..." +; _acme-challenge.security.example.org. 120 TXT "..." +; beware SAN : one each +; rather low TTL because some things might be using this as their backoff determination, buggily. +; +; This does require that the ACME client correctly remap domains to update in +; DNS before asking for a validation of the unmapped domain. The tool I used +; in 2020-02 claimed such support but it was buggy and I had to abandon this +; approach. Instead, we have the nginx vhost which handles a bare +; "example.org" handle /.well-known/acme-challenge/ by trying local files +; first, else HTTP proxying to the chat server. +; I'd like to get rid of that proxying and switch back to DNS updating; +; "d.example.net" is delegated to a commercial service which has an API with +; near-instant live updates. + +_acme-challenge 15 CNAME _acme-challenge.chat-acme.d.example.net. +_acme-challenge.xmpp 15 CNAME _acme-challenge.xmpp.chat-acme.d.example.net. +_acme-challenge.chat 15 CNAME _acme-challenge.chat.chat-acme.d.example.net. +_acme-challenge.conference 15 CNAME _acme-challenge.conference.chat-acme.d.example.net. +_acme-challenge.proxy-chatfiles 15 CNAME _acme-challenge.proxy-chatfiles.chat-acme.d.example.net. +_acme-challenge.pubsub.xmpp 15 CNAME _acme-challenge.pubsub.xmpp.chat-acme.d.example.net. + +; TLSA Referencing }}} + +;;; ====================================================================== +; Mail server hostnames {{{ + +; Willing to sacrifice Kerberos portability and rely upon disabling reverse DNS +; hostname canonicalization. Sucking it up. Still no reliable IPv6 at home. +; :( +; For /etc/krb5.conf: [libdefaults] dns_canonicalize_hostname = false +; +imap AAAA 2001:db8::48:4558:696d:6170 + A 192.0.2.25 +smtp AAAA 2001:db8::48:4558:736d:7470 + A 192.0.2.25 +; +smtp46 A 192.0.2.25 ; old alias pre-dating IPv4 in smtp + AAAA 2001:db8::48:4558:736d:7470 +imap46 A 192.0.2.25 ; old alias pre-dating IPv4 in imap + AAAA 2001:db8::48:4558:696d:6170 + +; If changing this, then also update the SPF record; it hard-codes these for +; efficiency of remote systems, saving them some lookups. +; Really, we should be constructing the SPF record via a macro of some kind. +; I'm trying to avoid using M4 to make this zonefile, but sometimes with some +; good whiskey, that looks mighty tempting. +mx A 192.0.2.25 + AAAA 2001:db8::48:4558:736d:7470 +mx.ipv4 A 192.0.2.25 +mx.ipv6 AAAA 2001:db8::48:4558:736d:7470 +; +; RFC 7208 section 10.1.3 +; HELO hostnames need to have SPF to allow processing for bounces +; In short: there is no envelope sender for bounces, so the only thing which +; can be checked in the HELO name. +; +; This needs to be tied to the exim.conf setup for outbound `helo_data` on +; transports. At this time, that's "mx.example.org"; this needs to also +; match the `interface` IP address list. +; We do not need to allow other email service providers to claim to be this +; host, so we use "a" (for A/AAAA matching), a catchall for our IPs, and a +; *hard* reject on any other IP. +; Because we never send email from mx.example.org and it's only used by HELO +; checks, we can be firm and not have to worry about forwarding or other +; legitimate shenanigans. +mx TXT "v=spf1 a include:_spflarge.example.net -all" + +; RFC 8461 SMTP MTA Strict Transport Security (MTA-STS) +; Final: `_mta-sts` TXT record, `v=STSv1`, with host records on `mta-sts`: +; https://mta-sts.example.com/.well-known/mta-sts.txt +; +;; History: +;;; https://tools.ietf.org/html/draft-ietf-uta-mta-sts-02 +;;; SMTP MTA Strict Transport Security (MTA-STS) +;;; Draft -03 renames to "mta-sts", to match the hostname which has to resolve anyway, +;;; which makes sense: NXDOMAIN can skip a second lookup. +;;; https://tools.ietf.org/html/draft-ietf-uta-mta-sts-03 +;;; NB: draft -15 in now in last call, and by this point: +;;; + it's .txt, not .json +;;; + the allowed fields for the mode have changed (`report` -> `testing`) +;;; + DNS is back to _mta-sts +; +; IP addresses should match `services`; this is ALIAS record stuff, can't use +; CNAME because need TXT record too, for the draft, but can switch back to CNAME +; if ignoring the draft. +; +; If updating version, don't forget to check globnix.org DNS too! +_mta-sts TXT "v=STSv1; id=20191231r1;" +mta-sts TXT "v=STSv1; id=20191231r1;" +mta-sts A 192.0.2.93 +mta-sts AAAA 2001:db8::48:4558:5345:5256 + +; Mail server hostnames }}} + +;;; ====================================================================== +; Chat server hostnames {{{ + +; $HostingProvider chat2.example.net +; ipv4: 203.0.113.175 +; ipv6: 2001:db8::f0ab:cdef:1234:f00f +; +xmpp.ipv6 AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp-s2s.ipv6 AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp A 203.0.113.175 + AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp-s2s A 203.0.113.175 + AAAA 2001:db8::f0ab:cdef:1234:f00f +proxy-chatfiles CNAME xmpp +fileproxy.xmpp CNAME xmpp +; Federated services which are offered under their own hostnames (even if on +; the same server instance and port) federate with the same mechanisms, so you +; need an SRV or just an address record if on the default ports. +; Which remote servers will actually use SRV is more open to question. +conference CNAME xmpp-s2s +_xmpp-server._tcp.conference SRV 10 2 5269 xmpp-s2s +; Services which are modules and need their own hostnames, but are not +; federated within XMPP, don't need the SRV record +pubsub.xmpp CNAME xmpp-s2s + +chat A 203.0.113.175 + AAAA 2001:db8::f0ab:cdef:1234:f00f +proxy-chatfiles.chat CNAME chat +fileproxy.chat CNAME chat +conference.chat CNAME chat +pubsub.chat CNAME chat +_xmpp-server._tcp.conference SRV 10 2 5269 chat + +; Chat server hostnames }}} + +;;; ====================================================================== +; Random other hostnames {{{ + +auth AAAA 2001:db8::48:4558:6175:7468 +kpeople AAAA 2001:db8::48:4558:6b70:706c +ocsp.security AAAA 2001:db8::48:4558:6f63:7370 +webauth AAAA 2001:db8::48:4558:7765:6261 + +news-feed A 192.0.2.93 + AAAA 2001:db8::48:4558:6e6e:7470 + +; This is for Go package downloads, keeping the canonical names of modules +; in domains under our control, so that we're not locked into one code-hosting +; provider. +go CNAME abcdefghijklmn.cloudfront.net. +; Do *not* deploy TLSA until cloudfront.net is signed +; (or we use ALIAS-type records) +;_443._tcp.go CNAME _amazon-tlsa + +foo A 192.0.2.200 + +; Random other hostnames }}} + +;;; ====================================================================== +; Per-person email sub-domains {{{ +; Household dedicated mail domains where *@person.example.org can receive email + +; Gladys receives email here but never sends from this address +; (Gladys has probably forgotten this exists and will roll her eyes at it.) +gladys MX 10 mx +$ORIGIN gladys.example.org. +_adsp._domainkey TXT "dkim=all" +_dmarc TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_report TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +$ORIGIN example.org. + +; Fred sends and receives email in this domain +fred MX 10 mx +$ORIGIN fred.example.org. +; Also have a web-server for some old OpenID stuff +@ A 192.0.2.93 ; services +@ AAAA 2001:db8::48:4558:5345:5256 ; services + TXT "v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all" +; +d201911._domainkey TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8/OMUa3PnWh9LqXFVwlAgYDdTtbq3zTtTOSBmJq5yWauzXYcUuSmhW7CsV0QQlacCsQgJlwg9Nl1vO1TosAj5EKUCLTeSqjlWrM7KXKPx8FT71Q9H9wXX4MHUyGrqHFo0OPzcmtHwqcd8AD6MIvJHSRoAfiPPBp8Euc0wGnJZdGS75Hk+wA3MQ2/Tlz" "P2eenyiFyqmUTAGOYsGC/tREsWPiegR/OVxNGlzTY6quHsuVK7UYtIyFnYx9PGWdl3b3p7VjQ5V0Rp+2CLtVrCuS6Zs+/3NhZdM7mdD0a9Jgxakwa1le5YmB5lHTGF7T8quy6TlKe9lMUIRNjqTHfSFz/MwIDAQAB" +d201911e2._domainkey TXT "v=DKIM1; k=ed25519; p=rQNsV9YcPJn/WYI1EDLjNbN/VuX1Hqq/oe4htbnhv+A=" +; +d202003._domainkey TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvpnx7tnRxAnE/poIRbVb2i+f1uQCXWnBHzHurgEyZX0CmGaiJuCbr8SWOW2PoXq9YX8gIv2TS3uzwGv/4yA2yX9Z9zar1LeWUfGgMWLdCol9xfmWrI+6MUzxuwhw/mXwzigbI4bHoakh3ez/i3J9KPS85GfrOODqA1emR13f2pG8EzAcje+rwW2PtYj" "c0h+FMDpeLuPYyYszFbNlrkVUneesxnoz+o4x/s6P14ZoRqz5CR7u6G02HwnNaHads5Eto6FYYErUUTtFmgWuYabHxgLVGRdRQs6B5OBYT/3L2q/lAgmEgdy/QL+c0Psfj99/XQmO8fcM0scBzw2ukQzcUwIDAQAB" +d202003e2._domainkey TXT "v=DKIM1; k=ed25519; p=0DAPp/IRLYFI/Z4YSgJRi4gr7xcu1/EfJ5mjVn10aAw=" +; +_adsp._domainkey TXT "dkim=all" +_dmarc TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_report TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +$ORIGIN example.org. + +; Per-person email sub-domains }}} + +;;; ====================================================================== +; {{{PGP-Keys-And-Fingerprints + +; RFC4398 Storing Certificates in the Domain Name System (DNS) +; type tag algorithm +; type 3 = PGP +; type 6 = IPGP +; IPGP fingerprint length, fingerprint, URL; either FP or URL may be empty +; gnupg comes with tools/make-dns-cert.c +; +; There's a lack of clarity about what clients do given a dot in the email LHS, +; so I solve it by publishing both with the dot escaped to be part of the label, +; and the dot introducing DNS hierarchy. +; +;;;fred CERT 6 0 0 FKy7QyQ5Ot41Fdot2k0ekA4UwcwEaHR0cHM6Ly93d3cuc2VjdXJpdHkuc3BvZGh1aXMub3JnL1BHUC9rZXlzLzB4NEQxRTkwMEUxNEMxQ0MwNC5hc2M= +;;;fred.bloggs CERT 6 0 0 FKy7QyQ5Ot41Fdot2k0ekA4UwcwEaHR0cHM6Ly93d3cuc2VjdXJpdHkuc3BvZGh1aXMub3JnL1BHUC9rZXlzLzB4NEQxRTkwMEUxNEMxQ0MwNC5hc2M= +;;;fred\.bloggs CERT 6 0 0 FKy7QyQ5Ot41Fdot2k0ekA4UwcwEaHR0cHM6Ly93d3cuc2VjdXJpdHkuc3BvZGh1aXMub3JnL1BHUC9rZXlzLzB4NEQxRTkwMEUxNEMxQ0MwNC5hc2M= + +; +; I used: +; perl -MMIME::Base64 -le '$_ = $ARGV[0]; s/\s+//g; s/(..)/chr(hex($1))/eg; +; $_=chr(length($_)) . $_ . $ARGV[1]; print encode_base64($_, "")' \ +; "$(gpg --with-colons --fingerprint $gpg_key | perl -F: -lane 'print $F[9] if $F[0] eq "fpr"')" \ +; https://www.security.example.org/PGP/keys/0x0123456789ABCDEF.asc +; +; Note that my first attempt omitted the length. Oops. The first octet of +; the base64 data is the length; following octets, for that length, is the +; fingerprint, in raw binary form; any remaining octets are the URL. +; With the URL in, we might have with key 0x0123456789ABCDEF : +; FKy7QyQ5Ot41Fdot2k0ekA4UwcwEaHR0cHM6Ly93d3cuc2VjdXJpdHkuc3BvZGh1aXMub3JnL1BHUC9rZXlzLzB4NEQxRTkwMEUxNEMxQ0MwNC5hc2M= + +;; WKD is https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/?include_text=1 +;; and included above with SRV records + +;; https://datatracker.ietf.org/doc/draft-ietf-dane-openpgpkey/?include_text=1 +;; rev 00: so draft-ietf-dane-openpgpkey-00.txt +;; NOW: RFC 7929 (experimental) + +; Generated by my gnupg-exportsof-mykey tooling; this is from output file +; dns-examplecom-pgp.openpgpkey.db +; This is the 2020 Ed25519 key. It's smaller, so it's marginally less insane +; to have it in DNS. +; +; For example.org redaction: leaving this intact, original PGP key, there +; are email addresses buried in the key, and human names. But changing +; the DNS labels and comments for zone consistency. +; You can get the original name just by looking up the fingerprint below +; the ORIGIN: +$ORIGIN _openpgpkey.example.org. +; 4833892924C60A7AE666D32A1DA3E68F41CEECAC +; Fred Bloggs +;;;6555f290b6e653629d9d582660dd2fe730320e43e252f8e2e5ee1bdd TYPE61 \# 1239 ( +;;; 9833045e2504f516092b06010401da470f0101074043c292df50b30dcaadd549 +;;; cf48fd9cbc06e43c3a9ca1cb788da0bd3c00ffc750b4245068696c2050656e6e +;;; 6f636b203c7068696c4070656e6e6f636b2d746563682e636f6d3e888e041316 +;;; 080036021b03030b090703150a080416030201021e0102178002190116210448 +;;; 33892924c60a7ae666d32a1da3e68f41ceecac05025e250723000a09101da3e6 +;;; 8f41ceecac2b46010081e1c09a255705b3fdda16a5485e6ec25edeabac78c92d +;;; aa5f6c1af2b71cc93800fe364bd0438dd5120585a034327e69757aa58ca0890a +;;; d7691cba343c55cf957806b41c5068696c2050656e6e6f636b203c7064704067 +;;; 6e7570672e6e65743e888b041316080033021b03030b090703150a0804160302 +;;; 01021e010217801621044833892924c60a7ae666d32a1da3e68f41ceecac0502 +;;; 5e250723000a09101da3e68f41ceecac4faf00ff4bc1c3a4389cb5bb9cb90026 +;;; c416f2ad99be2a6e0fe2ceea0bfa8e175e02c0eb0100eb8a11ec88b21ffa08c3 +;;; 0a71afd04dc43d093fabcb9c5ae2de0b9c627e88670eb4285068696c2050656e +;;; 6e6f636b203c7068696c2e70656e6e6f636b4073706f64687569732e6f72673e +;;; 888b041316080033021b03030b090703150a080416030201021e010217801621 +;;; 044833892924c60a7ae666d32a1da3e68f41ceecac05025e250724000a09101d +;;; a3e68f41ceecacc40800ff6702abcf89ddfe3ed0670953ca6a0a33ff4d6d83bf +;;; 8b4496cc299abedfb4947401008ab97f09df18813ad998de3fe3c55085b49f56 +;;; ee1f68cac298be4be6d4f34906b41b5068696c2050656e6e6f636b203c706470 +;;; 406578696d2e6f72673e888b041316080033021b03030b090703150a08041603 +;;; 0201021e010217801621044833892924c60a7ae666d32a1da3e68f41ceecac05 +;;; 025e250724000a09101da3e68f41ceecac9dbf0100f3eac82b9ef48d4cabc522 +;;; 44cbd52ed9392a2ad4111d8f6b0aa33f8859e956c301008653862e15970d7601 +;;; 57764064209c2ca8a9a616b1441e0e670b96458fd1a90eb838045e2504f5120a +;;; 2b0601040197550105010107401ea56299a543466023db5f4d4f1452450a393a +;;; fcb9039ada0c27e2dc7f59752f03010807887e04181608002616210448338929 +;;; 24c60a7ae666d32a1da3e68f41ceecac05025e2504f5021b0c05090966018000 +;;; 0a09101da3e68f41ceecac624d00fe30c7b6e6bbe930e899c270f4f17de5bdae +;;; 55612f9d69cb7490f6a4a4d04f261600ff58e26ae9fc3324de9bd51a77ff65d4 +;;; 2af60294f55a03fe0f1ec316e4f5b8e70bb833045e25054016092b06010401da +;;; 470f010107406df5a87cd9b51890f84f7e597ab17e549f1ba093844178ea61ce +;;; ac484b4a58e988ef0418160800201621044833892924c60a7ae666d32a1da3e6 +;;; 8f41ceecac05025e250540021b02008109101da3e68f41ceecac762004191608 +;;; 001d16210436bea421261c40a54fc9261c2e7665110f8a56ff05025e25054000 +;;; 0a09102e7665110f8a56ff0bb500ff4aee429d5659915336511a3b744c4c25fd +;;; d09fb8af2962c57e279b1906ad5e040100a8616e0d4def13d7910c30a595e7fc +;;; 92308de87404b96fa17325b6ef6cd7e9029f260100c2fe3edf9bbfce3c42317c +;;; a93bdbc4e52dd98bd2f782eb708edca1fbd7c345e50100dc69c0cc5a2481dc79 +;;; 08162d7c6d185c8866498eb87d159e0eb3877d68de2b02 +;;; ) + +; {{{PGP-PKA-Records + +; Phil notes, since the actual records below are "as output by gpg" for easier maintenance. +; TYPE37 == CERT +; During early GnuPG 2.1.x, GnuPG switched from v1 PKA to v2; details at: +; +; => local-part is zooko's base32 of LHS (as seen from src), type is CERT/IPGP, no URL +; so _very_ similar to the records already above where I chose to use CERT/IPGP ages ago. +; +; pip install zbase32 ; python # zbase32 is python2-only +; import hashlib, zbase32 +; lhs = 'fob' # fred o bloggs +; print(zbase32.zbase32.b2a(hashlib.sha1(lhs.lower()).digest())) +; That gets us the left-hand-side. +; +; Skimming code, it looks as though the PKA lookup really does just re-use the older IPGP +; code, which can return a URL; `gpg2.1.15 --auto-key-locate pka` got a fingerprint but then +; errored on getting the key. So instead, let's experiment by grabbing our own CERT from above, +; shoving it into this namespace and changing the LHS to use the zb32 encoded variants +; (which also handles `.` vs `\.` issues). +; +; Discovery from log.dirmngr : tries to access +; +; Okay, so change the hostname to `sks.example.org` and we should be good, as the PGP keyserver will get it +; % perl -MMIME::Base64 -le '$_ = $ARGV[0]; s/\s+//g; s/(..)/chr(hex($1))/eg; +; $_=chr(length($_)) . $_ . $ARGV[1]; print encode_base64($_, "")' \ +; "$(gpg --with-colons --fingerprint $pgp_key_main | perl -F: -lane 'print $F[9] if $F[0] eq "fpr"')" \ +; https://sks.example.org +; FKy7QyQ5Ot41Fdot2k0ekA4UwcwEaHR0cHM6Ly9za3Muc3BvZGh1aXMub3Jn +; +; 2018-07-14 after turning down sks.example.org: +; % perl -MMIME::Base64 -le '$_ = $ARGV[0]; s/\s+//g; s/(..)/chr(hex($1))/eg; $_=chr(length($_)) . $_ . $ARGV[1]; print encode_base64($_, "")' \ +; "$(gpg --with-colons --fingerprint $pgp_key_main | perl -F: -lane 'if ($F[0] eq "fpr") {print $F[9]; exit}')" \ +; http://ha.pool.sks-keyservers.net +; FKy7QyQ5Ot41Fdot2k0ekA4UwcwEaHR0cDovL2hhLnBvb2wuc2tzLWtleXNlcnZlcnMubmV0 +; ... + +; But that's not working with GnuPG. Just junk all of the above. +; Use my pka.py script and drop the keyserver entirely. + +; gpg2.1 --export-options export-minimal,export-pka --export 0x0123456789ABCDEF +$ORIGIN _pka.example.org. + +; Fingerprint: 0123456789ABCDEF0123456789ABCDEF01234567 +; +;;;kwj7zzgek39st5d5q81hq517e3iuzbr4 CERT 6 0 0 FKy7QyQ5Ot41Fdot2k0ekA4UwcwE + +; Fingerprint: 0123456789ABCDEF0123456789ABCDEF01234567 +; +;;;ajh79yijhx3xmeadhc79zrje7ret3qzr CERT 6 0 0 FKy7QyQ5Ot41Fdot2k0ekA4UwcwE + +$ORIGIN example.org. +; }}}PGP-PKA-Records + + +; }}}PGP-Keys-And-Fingerprints + +; Removed localhost entries: cookie stealing etc + +;;; ====================================================================== +; Sub-domains with email and other services {{{ + +mailtest MX 10 mx +$ORIGIN mailtest.example.org. +; +d201911._domainkey TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo9xHnjHyhm1weA6FjOqM8LKVsklFt26HXWoe/0XCdmBG4i/UzQ7RiSgWO4kv7anPK6qf6rtL1xYsHufaRXG8yLsZxz+BbUP99eZvxZX78tMg4cGf+yU6uFxulCbOzsMy+8Cc3bbQTtIWYjyWBwnHdRRrCkQxjZ5KAd+x7ZB5qzqg2/eLJ7fCuNsr/xn" "0XTY6XYgug95e3h4CEW3Y+bkG81AMeJmT/hoVTcXvT/Gm6ZOUmx6faQWIHSW7qOR3VS6S75HOuclEUk0gt9r7OQHKl01sXh8g02SHRk8SUMEoNVayqplYZTFFF01Z192m7enmpp+St+HHUIT6jW/CAMCO3wIDAQAB" +d201911e2._domainkey TXT "v=DKIM1; k=ed25519; p=afulDDnhaTzdqKQN0jtWV04eOhAcyBk3NCyVheOf53Y=" +; +d202003._domainkey TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2BTVZaVLvL3qZBPaF7tRR0SdOKe+hjcpQ5fqO48lEuYiyTb6lkn8DPjDK11gTN3au0Bm+y8KC7ITKSJosuJXytxt3wqc61Pwtmb/Cy7GzmOF1AuegydB3/88VbgHT5DZucHrh6+ValZk4Trkx+/1K26Uo+h2KL2n/Ldb1y91ATHujp8DqxAOhiZ7KN" "aS1okNRRB4/14jPufAbeiN8/iBPiY5Hl80KHmpjM+7vvjb5jiecZ1ZrVDj7eTES4pmVh2v1c106mZLieoqDPYaf/HVbCM4E4n1B6kjbboSOpANADIcqXxGJQ7Be7/Sk9f7KwRusrsMHXmBHgm4wPmwGVZ3QIDAQAB" +d202003e2._domainkey TXT "v=DKIM1; k=ed25519; p=iqwH/hhozFdeo1xnuldr8KUi7O7g+DzmC+f0SYMKVDc=" +; +_adsp._domainkey TXT "dkim=all" +_dmarc TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_report TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +$ORIGIN example.org. + +; Stub to show that SKS has been withdrawn; we no longer have email in this +; domain (it was used for PGP exchange with two non-SKS keyservers). +$ORIGIN sks.example.org. +_pgpkey-http._tcp SRV 0 0 0 . +_pgpkey-https._tcp SRV 0 0 0 . +_hkp._tcp SRV 0 0 0 . + +$ORIGIN sks-peer.example.org. +_pgpkey-http._tcp SRV 0 0 0 . +_pgpkey-https._tcp SRV 0 0 0 . +_hkp._tcp SRV 0 0 0 . + +; This is used for a dynamic DNS entry for my home router; at one point the +; router I was using supported various HTTP-based updates but not Dynamic DNS +; updates, so I switched to HE and have never switched that one back. +$ORIGIN yoyo.example.org. +@ NS ns5.he.net. +@ NS ns4.he.net. +@ NS ns3.he.net. +@ NS ns2.he.net. +@ NS ns1.he.net. + +$ORIGIN example.org. + +; khard: kubernetes the hard way, Google, `xyz-2` project +; gcloud dns managed-zones create khard --description="Kubernetes The Hard Way HD2" --dns-name="khard.example.org." +; gcloud dns managed-zones describe khard +khard IN NS ns-cloud-d1.googledomains.com. +khard IN NS ns-cloud-d2.googledomains.com. +khard IN NS ns-cloud-d3.googledomains.com. +khard IN NS ns-cloud-d4.googledomains.com. +;;;khard IN DS 15247 8 2 C7CAF8A6EFF0E6E7E811D68CDD87AD2EC135CB501A959FACBA3BC13F93F59864 + +; Sub-domains with email and other services }}} + +;;; ====================================================================== +; Stubs to reject email to some hosts {{{ +; Hosts which should not appear as senders but are abused by spammers because +; of parsing message-id headers: +realhost MX 0 . +realhost TXT "v=spf1 -all" +_25._tcp.realhost TLSA 03 00 00 0000000000000000000000000000000000000000000000000000000000000000 +; Stubs to reject email to some hosts }}} + +;;; ====================================================================== +; Domain verifications for some tools {{{ + +; +; "Optionally, once the "Verified" badge is visible on your organization's +; profile page, you can delete the TXT entry from the DNS record at your domain +; hosting service." +;_github-challenge-ExampleOrg TXT "" ; dropped value when commented out + +; +; "ACM automatically renews your certificate for as long as the certificate is +; in use and the CNAME record that ACM created for you remains in place in your +; DNS database. You can stop automatic renewal by removing the certificate from +; the AWS service with which it is associated or by deleting the CNAME record." +_fedcba9876543210fedcba9876543210.go.example.org. CNAME _45678901234abcdef45678901234abcd.ggedgsdned.acm-validations.aws. +; -- added 2019-10-09; leave in place, no scheduled removal. + +; Google Webmaster +; added 2020-01-26 login fred@example.net +; to allow usage in oauth consent screens. +; Must be left in place. +opqrstuvwxyz CNAME gv-abcdefghijklmn.dv.googlehosted.com. +; postmaster.google.com (CNAME alternative to TXT on top-level domain): +zyxwvutsrqpo CNAME gv-nmlkjihgfedcba.dv.googlehosted.com. + +; Bing (I didn't record the policy back when I added this) +0123456789abcdef0123456789abcdef CNAME verify.bing.com. + +; Domain verifications for some tools }}} + +; vim: set filetype=bindzone foldmethod=marker : diff --git a/commands/test_data/example.org.zone.js b/commands/test_data/example.org.zone.js new file mode 100644 index 000000000..a75d969de --- /dev/null +++ b/commands/test_data/example.org.zone.js @@ -0,0 +1,355 @@ +var bind = NewDnsProvider("bind", "BIND"); +var REG_CHANGEME = NewRegistrar("ThirdParty", "NONE"); +D("example.org", REG_CHANGEME, + DnsProvider(bind), + DefaultTTL(7200), + //SOA('@', 'ns1.example.org.', 'hostmaster.example.org.', 2020030700, 7200, 3600, 864000, 7200, TTL(43200)), + NAMESERVER('ns1.example.org.'), + NAMESERVER('ns2.example.org.'), + NAMESERVER('ns-a.example.net.'), + NAMESERVER('friend-dns.example.com.'), + MX('@', 10, 'mx.example.org.'), + TXT('@', 'v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all'), + SRV('_client._smtp', 1, 1, 1, 'example.org.'), + SRV('_client._smtp.mx', 1, 2, 1, 'mx.example.org.'), + SRV('_client._smtp.foo', 1, 2, 1, 'foo.example.org.'), + SRV('_kerberos._tcp', 10, 1, 88, 'kerb-service.example.org.'), + SRV('_kerberos._udp', 10, 1, 88, 'kerb-service.example.org.'), + SRV('_kpasswd._udp', 10, 1, 464, 'kerb-service.example.org.'), + SRV('_kerberos-adm._tcp', 10, 1, 749, 'kerb-service.example.org.'), + TXT('_kerberos', 'EXAMPLE.ORG'), + SRV('_ldap._tcp', 0, 0, 0, '.'), + SRV('_ldap._udp', 0, 0, 0, '.'), + SRV('_jabber._tcp', 10, 2, 5269, 'xmpp-s2s.example.org.'), + SRV('_xmpp-server._tcp', 10, 2, 5269, 'xmpp-s2s.example.org.'), + SRV('_xmpp-client._tcp', 10, 2, 5222, 'xmpp.example.org.'), + SRV('_im._sip', 0, 0, 0, '.'), + SRV('_pres._sip', 0, 0, 0, '.'), + SRV('_sip+d2t._tcp', 0, 0, 0, '.'), + SRV('_sips+d2t._tcp', 0, 0, 0, '.'), + SRV('_sip+d2u._udp', 0, 0, 0, '.'), + SRV('_sip+d2s._sctp', 0, 0, 0, '.'), + SRV('_sips+d2s._sctp', 0, 0, 0, '.'), + SRV('_submission._tcp', 10, 10, 587, 'smtp.example.org.'), + SRV('_submissions._tcp', 10, 10, 465, 'smtp.example.org.'), + SRV('_imap._tcp', 10, 10, 143, 'imap.example.org.'), + SRV('_imaps._tcp', 10, 10, 993, 'imap.example.org.'), + SRV('_pop3._tcp', 0, 0, 0, '.'), + SRV('_pop3s._tcp', 0, 0, 0, '.'), + SRV('_sieve._tcp', 10, 10, 4190, 'imap.example.org.'), + TXT('dns-moreinfo', ['Fred Bloggs, TZ=America/New_York', 'Chat-Service-X: @handle1', 'Chat-Service-Y: federated-handle@example.org']), + SRV('_pgpkey-http._tcp', 0, 0, 0, '.'), + SRV('_pgpkey-https._tcp', 0, 0, 0, '.'), + SRV('_hkp._tcp', 0, 0, 0, '.'), + SRV('_openpgpkey._tcp', 10, 10, 443, 'openpgpkey.example.org.'), + SRV('_finger._tcp', 10, 10, 79, 'barbican.example.org.'), + SRV('_avatars-sec._tcp', 10, 10, 443, 'avatars.example.org.'), + A('@', '192.0.2.1'), + AAAA('@', '2001:db8::1:1'), + TXT('_adsp._domainkey', 'dkim=all'), + TXT('_dmarc', 'v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s'), + TXT('d201911._domainkey', ['v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4SmyE5Tz5/wPL8cb2AKuHnlFeLMOhAl1UX/NYaeDCKMWoBPTgZRT0jonKLmV2UscHdodXu5ZsLr/NAuLCp7HmPLReLz7kxKncP6ppveKxc1aq5SPTKeWe77p6BptlahHc35eiXsZRpTsEzrbEOainy1IWEd+w9p1gWbrSutwE22z0i4V88nQ9UBa1ks', '6cVGxXBZFovWC+i28aGs6Lc7cSfHG5+Mrg3ud5X4evYXTGFMPpunMcCsXrqmS5a+5gRSEMZhngha/cHjLwaJnWzKaywNWF5XOsCjL94QkS0joB7lnGOHMNSZBCcu542Y3Ht3SgHhlpkF9mIbIRfpzA9IoSQIDAQAB']), + TXT('d201911e2._domainkey', 'v=DKIM1; k=ed25519; p=GBt2k2L39KUb39fg5brOppXDHXvISy0+ECGgPld/bIo='), + TXT('d202003._domainkey', ['v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/1tQvOEs7xtKNm7PbPgY4hQjwHVvqqkDb0+TeqZHYRSczQ3c0LFJrIDFiPIdwQe/7AuKrxvATSh/uXKZ3EP4ouMgROPZnUxVXENeetJj+pc3nfGwTKUBTTTth+SO74gdIWsntjvAfduzosC4ZkxbDwZ9c253qXARGvGu+LB/iAeq0ngEbm5fU13+Jo', 'pv0d4dR6oGe9GvMEnGGLZzNrxWl1BPe2x5JZ5/X/3fW8vJx3OgRB5N6fqbAJ6HZ9kcbikDH4lPPl9RIoprFk7mmwno/nXLQYGhPobmqq8wLkDiXEkWtYa5lzujz3XI3Zkk8ZIOGvdbVVfAttT0IVPnYkOhQIDAQAB']), + TXT('d202003e2._domainkey', 'v=DKIM1; k=ed25519; p=DQI5d9sNMrr0SLDoAi071IFOyKnlbR29hAQdqVQecQg='), + TXT('_report', 'r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;'), + TXT('_smtp._tls', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + TXT('_smtp-tlsrpt', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + TXT('example.net._report._dmarc', 'v=DMARC1'), + TXT('example.com._report._dmarc', 'v=DMARC1'), + TXT('xn--2j5b.xn--9t4b11yi5a._report._dmarc', 'v=DMARC1'), + TXT('special.test._report._dmarc', 'v=DMARC1'), + TXT('xn--qck5b9a5eml3bze.xn--zckzah._report._dmarc', 'v=DMARC1'), + CNAME('*._smimecert', '_ourca-smimea.example.org.'), + PTR('b._dns-sd._udp', 'field.example.org.'), + PTR('lb._dns-sd._udp', 'field.example.org.'), + PTR('r._dns-sd._udp', 'field.example.org.'), + NS('field', 'ns1.example.org.'), + NS('field', 'ns2.example.org.'), + A('barbican', '192.0.2.1'), + AAAA('barbican', '2001:db8::1:1'), + A('barbican.ipv4', '192.0.2.1'), + AAAA('barbican.ipv6', '2001:db8::1:1'), + A('megalomaniac', '198.51.100.254'), + AAAA('megalomaniac', '2001:db8:ffef::254'), + A('megalomaniac.ipv4', '198.51.100.254'), + AAAA('megalomaniac.ipv6', '2001:db8:ffef::254'), + SSHFP('megalomaniac', 1, 2, '4e9ced94d3caf2ce915f85a63ce7279d5118a79ea03dac59cf4859b825d2f619'), + SSHFP('megalomaniac', 3, 2, 'd3556a3db83ab9ccec39dc6693dd2f3e28b178c9bba61880924821c426cc61eb'), + SSHFP('megalomaniac', 4, 2, 'c60c9d9d4728668f5f46986ff0c5b416c5e913862c4970cbfe211a6f44a111b4'), + SSHFP('megalomaniac.ipv4', 1, 2, '4e9ced94d3caf2ce915f85a63ce7279d5118a79ea03dac59cf4859b825d2f619'), + SSHFP('megalomaniac.ipv4', 3, 2, 'd3556a3db83ab9ccec39dc6693dd2f3e28b178c9bba61880924821c426cc61eb'), + SSHFP('megalomaniac.ipv4', 4, 2, 'c60c9d9d4728668f5f46986ff0c5b416c5e913862c4970cbfe211a6f44a111b4'), + SSHFP('megalomaniac.ipv6', 1, 2, '4e9ced94d3caf2ce915f85a63ce7279d5118a79ea03dac59cf4859b825d2f619'), + SSHFP('megalomaniac.ipv6', 3, 2, 'd3556a3db83ab9ccec39dc6693dd2f3e28b178c9bba61880924821c426cc61eb'), + SSHFP('megalomaniac.ipv6', 4, 2, 'c60c9d9d4728668f5f46986ff0c5b416c5e913862c4970cbfe211a6f44a111b4'), + A('tower', '192.0.2.42'), + AAAA('tower', '2001:db8::1:42'), + A('tower.ipv4', '192.0.2.42'), + AAAA('tower.ipv6', '2001:db8::1:42'), + SSHFP('tower', 1, 2, '0f211d236e94768911a294f38653c4af6fa935a5b06c975d8162f59142571451'), + SSHFP('tower', 3, 2, '88bf7b7401c11fa2e84871efb06cd73d8fc409154605b354db2dda0b82fe1160'), + SSHFP('tower', 4, 2, '6d30900be0faaae73568fc007a87b4d076cf9a351ecacc1106aef726c34ad61d'), + SSHFP('tower.ipv4', 1, 2, '0f211d236e94768911a294f38653c4af6fa935a5b06c975d8162f59142571451'), + SSHFP('tower.ipv4', 3, 2, '88bf7b7401c11fa2e84871efb06cd73d8fc409154605b354db2dda0b82fe1160'), + SSHFP('tower.ipv4', 4, 2, '6d30900be0faaae73568fc007a87b4d076cf9a351ecacc1106aef726c34ad61d'), + SSHFP('tower.ipv6', 1, 2, '0f211d236e94768911a294f38653c4af6fa935a5b06c975d8162f59142571451'), + SSHFP('tower.ipv6', 3, 2, '88bf7b7401c11fa2e84871efb06cd73d8fc409154605b354db2dda0b82fe1160'), + SSHFP('tower.ipv6', 4, 2, '6d30900be0faaae73568fc007a87b4d076cf9a351ecacc1106aef726c34ad61d'), + A('vcs', '192.0.2.228'), + AAAA('vcs', '2001:db8::48:4558:4456:4353'), + A('vcs.ipv4', '192.0.2.228'), + AAAA('vcs.ipv6', '2001:db8::48:4558:4456:4353'), + CNAME('git', 'vcs.example.org.'), + CNAME('git.ipv4', 'vcs.ipv4.example.org.'), + CNAME('git.ipv6', 'vcs.ipv6.example.org.'), + AAAA('svn', '2001:db8::48:4558:73:766e'), + SSHFP('vcs', 1, 2, 'b518be390babdf43cb2d598aa6befa6ce6878546bf107b829d0cfc65253a97d4'), + SSHFP('vcs', 3, 2, 'e92545dc0bf501f72333ddeb7a37afc2c5b408ce39a3ad95fbc66236f0077323'), + SSHFP('vcs', 4, 2, '02289441124a487095a6cda2e946c6a8ed9087faf3592ec4135536c3e615521c'), + SSHFP('vcs.ipv4', 1, 2, 'b518be390babdf43cb2d598aa6befa6ce6878546bf107b829d0cfc65253a97d4'), + SSHFP('vcs.ipv4', 3, 2, 'e92545dc0bf501f72333ddeb7a37afc2c5b408ce39a3ad95fbc66236f0077323'), + SSHFP('vcs.ipv4', 4, 2, '02289441124a487095a6cda2e946c6a8ed9087faf3592ec4135536c3e615521c'), + SSHFP('vcs.ipv6', 1, 2, 'b518be390babdf43cb2d598aa6befa6ce6878546bf107b829d0cfc65253a97d4'), + SSHFP('vcs.ipv6', 3, 2, 'e92545dc0bf501f72333ddeb7a37afc2c5b408ce39a3ad95fbc66236f0077323'), + SSHFP('vcs.ipv6', 4, 2, '02289441124a487095a6cda2e946c6a8ed9087faf3592ec4135536c3e615521c'), + A('nsauth', '192.0.2.53'), + AAAA('nsauth', '2001:db8::53:1'), + A('nsauth.ipv4', '192.0.2.53'), + AAAA('nsauth.ipv6', '2001:db8::53:1'), + SSHFP('nsauth', 1, 2, '895804ae022fff643b2677563cb850607c5bb564d9919896c521098c8abc40f2'), + SSHFP('nsauth', 3, 2, '28a65470badae611375747e1a803211c41e3d71e97741fa92ccbdf7b01f34e42'), + SSHFP('nsauth', 4, 2, '6e10445c0649c03fa83e18b1873e5b89b3a20893ecb48d01e7cedb3dd563ecf0'), + SSHFP('nsauth.ipv4', 1, 2, '895804ae022fff643b2677563cb850607c5bb564d9919896c521098c8abc40f2'), + SSHFP('nsauth.ipv4', 3, 2, '28a65470badae611375747e1a803211c41e3d71e97741fa92ccbdf7b01f34e42'), + SSHFP('nsauth.ipv4', 4, 2, '6e10445c0649c03fa83e18b1873e5b89b3a20893ecb48d01e7cedb3dd563ecf0'), + SSHFP('nsauth.ipv6', 1, 2, '895804ae022fff643b2677563cb850607c5bb564d9919896c521098c8abc40f2'), + SSHFP('nsauth.ipv6', 3, 2, '28a65470badae611375747e1a803211c41e3d71e97741fa92ccbdf7b01f34e42'), + SSHFP('nsauth.ipv6', 4, 2, '6e10445c0649c03fa83e18b1873e5b89b3a20893ecb48d01e7cedb3dd563ecf0'), + A('ns1', '192.0.2.53'), + AAAA('ns1', '2001:db8::53:1'), + A('ns2', '203.0.113.53'), + AAAA('ns2', '2001:db8:113::53'), + A('hermes', '192.0.2.25'), + AAAA('hermes', '2001:db8::48:4558:736d:7470'), + AAAA('hermes', '2001:db8::48:4558:696d:6170'), + A('hermes.ipv4', '192.0.2.25'), + AAAA('hermes.ipv6', '2001:db8::48:4558:736d:7470'), + AAAA('hermes.ipv6', '2001:db8::48:4558:696d:6170'), + SSHFP('hermes', 1, 2, '4472ff5bd0528cd49216af4503ba6a1c48f121d0292a31d6af193e5000af4966'), + SSHFP('hermes', 3, 2, 'eaba20c1565676a5229184ccfcf82d0ee408f91757a67d9fa51a0b6f3db4a33b'), + SSHFP('hermes', 4, 2, 'a9d89920e599d04363c8b35a4ce66c1ed257ea1d16981f060b6aed080bbb7a7c'), + SSHFP('hermes.ipv4', 1, 2, '4472ff5bd0528cd49216af4503ba6a1c48f121d0292a31d6af193e5000af4966'), + SSHFP('hermes.ipv4', 3, 2, 'eaba20c1565676a5229184ccfcf82d0ee408f91757a67d9fa51a0b6f3db4a33b'), + SSHFP('hermes.ipv4', 4, 2, 'a9d89920e599d04363c8b35a4ce66c1ed257ea1d16981f060b6aed080bbb7a7c'), + SSHFP('hermes.ipv6', 1, 2, '4472ff5bd0528cd49216af4503ba6a1c48f121d0292a31d6af193e5000af4966'), + SSHFP('hermes.ipv6', 3, 2, 'eaba20c1565676a5229184ccfcf82d0ee408f91757a67d9fa51a0b6f3db4a33b'), + SSHFP('hermes.ipv6', 4, 2, 'a9d89920e599d04363c8b35a4ce66c1ed257ea1d16981f060b6aed080bbb7a7c'), + A('kerb-service', '192.0.2.88'), + AAAA('kerb-service', '2001:db8::48:4558:6b65:7262'), + A('security', '192.0.2.92'), + AAAA('security', '2001:db8::48:4558:53:4543'), + A('security.ipv4', '192.0.2.92'), + AAAA('security.ipv6', '2001:db8::48:4558:53:4543'), + A('services', '192.0.2.93'), + AAAA('services', '2001:db8::48:4558:5345:5256'), + A('services.ipv4', '192.0.2.93'), + AAAA('services.ipv6', '2001:db8::48:4558:5345:5256'), + A('openpgpkey', '192.0.2.92'), + AAAA('openpgpkey', '2001:db8::48:4558:53:4543'), + CNAME('finger', 'barbican.example.org.'), + CNAME('finger.ipv4', 'barbican.ipv4.example.org.'), + CNAME('finger.ipv6', 'barbican.ipv6.example.org.'), + A('avatars', '192.0.2.93'), + AAAA('avatars', '2001:db8::48:4558:5345:5256'), + CNAME('dict', 'services.example.org.'), + CNAME('people', 'services.example.org.'), + CNAME('people.ipv4', 'services.ipv4.example.org.'), + CNAME('people.ipv6', 'services.ipv6.example.org.'), + CNAME('wpad', 'services.example.org.'), + CNAME('www', 'services.example.org.'), + CNAME('www.ipv4', 'services.ipv4.example.org.'), + CNAME('www.ipv6', 'services.ipv6.example.org.'), + CAA('@', 'issue', 'example.net'), + CAA('@', 'issue', 'letsencrypt.org\; accounturi=https://acme-v01.api.letsencrypt.org/acme/reg/1234567'), + CAA('@', 'issue', 'letsencrypt.org\; accounturi=https://acme-staging-v02.api.letsencrypt.org/acme/acct/23456789'), + CAA('@', 'issue', 'letsencrypt.org\; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/76543210'), + CAA('@', 'issuewild', ';'), + CAA('@', 'iodef', 'mailto:security@example.org'), + TLSA('_ourcaca4-tlsa', 2, 0, 1, 'ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488'), + TLSA('_ourcaca5-tlsa', 2, 0, 1, '11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1'), + TLSA('_cacert-c3-tlsa', 2, 0, 1, '4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8'), + TLSA('_letsencrypt-tlsa', 2, 1, 1, '60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18'), + TLSA('_letsencrypt-tlsa', 2, 1, 1, 'b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b'), + TLSA('_amazon-tlsa', 2, 0, 1, '8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e'), + TLSA('_amazon-tlsa', 2, 0, 1, '1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4'), + TLSA('_amazon-tlsa', 2, 0, 1, '18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4'), + TLSA('_amazon-tlsa', 2, 0, 1, 'e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092'), + TLSA('_ourca-tlsa', 2, 0, 1, 'ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488'), + TLSA('_ourca-tlsa', 2, 0, 1, '11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1'), + TLSA('_ourca-cacert-tlsa', 2, 0, 1, 'ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488'), + TLSA('_ourca-cacert-tlsa', 2, 0, 1, '11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1'), + TLSA('_ourca-cacert-tlsa', 2, 0, 1, '4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8'), + TLSA('_ourca-le-tlsa', 2, 0, 1, 'ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488'), + TLSA('_ourca-le-tlsa', 2, 0, 1, '11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1'), + TLSA('_ourca-le-tlsa', 2, 1, 1, '60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18'), + TLSA('_ourca-le-tlsa', 2, 1, 1, 'b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b'), + TLSA('_ourca-cacert-le-tlsa', 2, 0, 1, 'ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488'), + TLSA('_ourca-cacert-le-tlsa', 2, 0, 1, '11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1'), + TLSA('_ourca-cacert-le-tlsa', 2, 0, 1, '4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8'), + TLSA('_ourca-cacert-le-tlsa', 2, 1, 1, '60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18'), + TLSA('_ourca-cacert-le-tlsa', 2, 1, 1, 'b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b'), + TLSA('_cacert-le-tlsa', 2, 0, 1, '4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8'), + TLSA('_cacert-le-tlsa', 2, 1, 1, '60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18'), + TLSA('_cacert-le-tlsa', 2, 1, 1, 'b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b'), + TLSA('_le-amazon-tlsa', 2, 1, 1, '60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18'), + TLSA('_le-amazon-tlsa', 2, 1, 1, 'b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b'), + TLSA('_le-amazon-tlsa', 2, 0, 1, '8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e'), + TLSA('_le-amazon-tlsa', 2, 0, 1, '1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4'), + TLSA('_le-amazon-tlsa', 2, 0, 1, '18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4'), + TLSA('_le-amazon-tlsa', 2, 0, 1, 'e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092'), + TLSA('_ourca-le-amazon-tlsa', 2, 0, 1, 'ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488'), + TLSA('_ourca-le-amazon-tlsa', 2, 0, 1, '11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1'), + TLSA('_ourca-le-amazon-tlsa', 2, 1, 1, '60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18'), + TLSA('_ourca-le-amazon-tlsa', 2, 1, 1, 'b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b'), + TLSA('_ourca-le-amazon-tlsa', 2, 0, 1, '8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e'), + TLSA('_ourca-le-amazon-tlsa', 2, 0, 1, '1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4'), + TLSA('_ourca-le-amazon-tlsa', 2, 0, 1, '18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4'), + TLSA('_ourca-le-amazon-tlsa', 2, 0, 1, 'e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092'), + CNAME('_443._tcp.www', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.www.ipv4', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.www.ipv6', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.people', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.people.ipv4', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.people.ipv6', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.git', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.svn', '_ourca-le-tlsa.example.org.'), + CNAME('_5222._tcp.xmpp', '_ourca-le-tlsa.example.org.'), + CNAME('_5223._tcp.xmpp', '_ourca-le-tlsa.example.org.'), + CNAME('_5269._tcp.xmpp-s2s', '_ourca-le-tlsa.example.org.'), + CNAME('_25._tcp.mx', '_ourca-le-tlsa.example.org.'), + CNAME('_26._tcp.mx', '_ourca-le-tlsa.example.org.'), + CNAME('_27._tcp.mx', '_ourca-le-tlsa.example.org.'), + CNAME('_465._tcp.smtp46', '_ourca-le-tlsa.example.org.'), + CNAME('_587._tcp.smtp46', '_ourca-le-tlsa.example.org.'), + CNAME('_1465._tcp.smtp46', '_ourca-le-tlsa.example.org.'), + CNAME('_1587._tcp.smtp46', '_ourca-le-tlsa.example.org.'), + CNAME('_465._tcp.smtp', '_ourca-le-tlsa.example.org.'), + CNAME('_587._tcp.smtp', '_ourca-le-tlsa.example.org.'), + CNAME('_1465._tcp.smtp', '_ourca-le-tlsa.example.org.'), + CNAME('_1587._tcp.smtp', '_ourca-le-tlsa.example.org.'), + CNAME('_143._tcp.imap46', '_ourca-le-tlsa.example.org.'), + CNAME('_993._tcp.imap46', '_ourca-le-tlsa.example.org.'), + CNAME('_143._tcp.imap', '_ourca-le-tlsa.example.org.'), + CNAME('_993._tcp.imap', '_ourca-le-tlsa.example.org.'), + CNAME('_4190._tcp.imap', '_ourca-le-tlsa.example.org.'), + CNAME('www.security', 'security.example.org.'), + CNAME('www.security.ipv4', 'security.ipv4.example.org.'), + CNAME('www.security.ipv6', 'security.ipv6.example.org.'), + CNAME('_443._tcp.www.security', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.www.security.ipv4', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.www.security.ipv6', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.security', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.security.ipv4', '_ourca-le-tlsa.example.org.'), + CNAME('_443._tcp.security.ipv6', '_ourca-le-tlsa.example.org.'), + CNAME('_acme-challenge', '_acme-challenge.chat-acme.d.example.net.', TTL(15)), + CNAME('_acme-challenge.xmpp', '_acme-challenge.xmpp.chat-acme.d.example.net.', TTL(15)), + CNAME('_acme-challenge.chat', '_acme-challenge.chat.chat-acme.d.example.net.', TTL(15)), + CNAME('_acme-challenge.conference', '_acme-challenge.conference.chat-acme.d.example.net.', TTL(15)), + CNAME('_acme-challenge.proxy-chatfiles', '_acme-challenge.proxy-chatfiles.chat-acme.d.example.net.', TTL(15)), + CNAME('_acme-challenge.pubsub.xmpp', '_acme-challenge.pubsub.xmpp.chat-acme.d.example.net.', TTL(15)), + AAAA('imap', '2001:db8::48:4558:696d:6170'), + A('imap', '192.0.2.25'), + AAAA('smtp', '2001:db8::48:4558:736d:7470'), + A('smtp', '192.0.2.25'), + A('smtp46', '192.0.2.25'), + AAAA('smtp46', '2001:db8::48:4558:736d:7470'), + A('imap46', '192.0.2.25'), + AAAA('imap46', '2001:db8::48:4558:696d:6170'), + A('mx', '192.0.2.25'), + AAAA('mx', '2001:db8::48:4558:736d:7470'), + A('mx.ipv4', '192.0.2.25'), + AAAA('mx.ipv6', '2001:db8::48:4558:736d:7470'), + TXT('mx', 'v=spf1 a include:_spflarge.example.net -all'), + TXT('_mta-sts', 'v=STSv1; id=20191231r1;'), + TXT('mta-sts', 'v=STSv1; id=20191231r1;'), + A('mta-sts', '192.0.2.93'), + AAAA('mta-sts', '2001:db8::48:4558:5345:5256'), + AAAA('xmpp.ipv6', '2001:db8::f0ab:cdef:1234:f00f'), + AAAA('xmpp-s2s.ipv6', '2001:db8::f0ab:cdef:1234:f00f'), + A('xmpp', '203.0.113.175'), + AAAA('xmpp', '2001:db8::f0ab:cdef:1234:f00f'), + A('xmpp-s2s', '203.0.113.175'), + AAAA('xmpp-s2s', '2001:db8::f0ab:cdef:1234:f00f'), + CNAME('proxy-chatfiles', 'xmpp.example.org.'), + CNAME('fileproxy.xmpp', 'xmpp.example.org.'), + CNAME('conference', 'xmpp-s2s.example.org.'), + SRV('_xmpp-server._tcp.conference', 10, 2, 5269, 'xmpp-s2s.example.org.'), + CNAME('pubsub.xmpp', 'xmpp-s2s.example.org.'), + A('chat', '203.0.113.175'), + AAAA('chat', '2001:db8::f0ab:cdef:1234:f00f'), + CNAME('proxy-chatfiles.chat', 'chat.example.org.'), + CNAME('fileproxy.chat', 'chat.example.org.'), + CNAME('conference.chat', 'chat.example.org.'), + CNAME('pubsub.chat', 'chat.example.org.'), + SRV('_xmpp-server._tcp.conference', 10, 2, 5269, 'chat.example.org.'), + AAAA('auth', '2001:db8::48:4558:6175:7468'), + AAAA('kpeople', '2001:db8::48:4558:6b70:706c'), + AAAA('ocsp.security', '2001:db8::48:4558:6f63:7370'), + AAAA('webauth', '2001:db8::48:4558:7765:6261'), + A('news-feed', '192.0.2.93'), + AAAA('news-feed', '2001:db8::48:4558:6e6e:7470'), + CNAME('go', 'abcdefghijklmn.cloudfront.net.'), + A('foo', '192.0.2.200'), + MX('gladys', 10, 'mx.example.org.'), + TXT('_adsp._domainkey.gladys', 'dkim=all'), + TXT('_dmarc.gladys', 'v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s'), + TXT('_report.gladys', 'r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;'), + TXT('_smtp._tls.gladys', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + TXT('_smtp-tlsrpt.gladys', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + MX('fred', 10, 'mx.example.org.'), + A('fred', '192.0.2.93'), + AAAA('fred', '2001:db8::48:4558:5345:5256'), + TXT('fred', 'v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all'), + TXT('d201911._domainkey.fred', ['v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8/OMUa3PnWh9LqXFVwlAgYDdTtbq3zTtTOSBmJq5yWauzXYcUuSmhW7CsV0QQlacCsQgJlwg9Nl1vO1TosAj5EKUCLTeSqjlWrM7KXKPx8FT71Q9H9wXX4MHUyGrqHFo0OPzcmtHwqcd8AD6MIvJHSRoAfiPPBp8Euc0wGnJZdGS75Hk+wA3MQ2/Tlz', 'P2eenyiFyqmUTAGOYsGC/tREsWPiegR/OVxNGlzTY6quHsuVK7UYtIyFnYx9PGWdl3b3p7VjQ5V0Rp+2CLtVrCuS6Zs+/3NhZdM7mdD0a9Jgxakwa1le5YmB5lHTGF7T8quy6TlKe9lMUIRNjqTHfSFz/MwIDAQAB']), + TXT('d201911e2._domainkey.fred', 'v=DKIM1; k=ed25519; p=rQNsV9YcPJn/WYI1EDLjNbN/VuX1Hqq/oe4htbnhv+A='), + TXT('d202003._domainkey.fred', ['v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvpnx7tnRxAnE/poIRbVb2i+f1uQCXWnBHzHurgEyZX0CmGaiJuCbr8SWOW2PoXq9YX8gIv2TS3uzwGv/4yA2yX9Z9zar1LeWUfGgMWLdCol9xfmWrI+6MUzxuwhw/mXwzigbI4bHoakh3ez/i3J9KPS85GfrOODqA1emR13f2pG8EzAcje+rwW2PtYj', 'c0h+FMDpeLuPYyYszFbNlrkVUneesxnoz+o4x/s6P14ZoRqz5CR7u6G02HwnNaHads5Eto6FYYErUUTtFmgWuYabHxgLVGRdRQs6B5OBYT/3L2q/lAgmEgdy/QL+c0Psfj99/XQmO8fcM0scBzw2ukQzcUwIDAQAB']), + TXT('d202003e2._domainkey.fred', 'v=DKIM1; k=ed25519; p=0DAPp/IRLYFI/Z4YSgJRi4gr7xcu1/EfJ5mjVn10aAw='), + TXT('_adsp._domainkey.fred', 'dkim=all'), + TXT('_dmarc.fred', 'v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s'), + TXT('_report.fred', 'r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;'), + TXT('_smtp._tls.fred', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + TXT('_smtp-tlsrpt.fred', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + MX('mailtest', 10, 'mx.example.org.'), + TXT('d201911._domainkey.mailtest', ['v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo9xHnjHyhm1weA6FjOqM8LKVsklFt26HXWoe/0XCdmBG4i/UzQ7RiSgWO4kv7anPK6qf6rtL1xYsHufaRXG8yLsZxz+BbUP99eZvxZX78tMg4cGf+yU6uFxulCbOzsMy+8Cc3bbQTtIWYjyWBwnHdRRrCkQxjZ5KAd+x7ZB5qzqg2/eLJ7fCuNsr/xn', '0XTY6XYgug95e3h4CEW3Y+bkG81AMeJmT/hoVTcXvT/Gm6ZOUmx6faQWIHSW7qOR3VS6S75HOuclEUk0gt9r7OQHKl01sXh8g02SHRk8SUMEoNVayqplYZTFFF01Z192m7enmpp+St+HHUIT6jW/CAMCO3wIDAQAB']), + TXT('d201911e2._domainkey.mailtest', 'v=DKIM1; k=ed25519; p=afulDDnhaTzdqKQN0jtWV04eOhAcyBk3NCyVheOf53Y='), + TXT('d202003._domainkey.mailtest', ['v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2BTVZaVLvL3qZBPaF7tRR0SdOKe+hjcpQ5fqO48lEuYiyTb6lkn8DPjDK11gTN3au0Bm+y8KC7ITKSJosuJXytxt3wqc61Pwtmb/Cy7GzmOF1AuegydB3/88VbgHT5DZucHrh6+ValZk4Trkx+/1K26Uo+h2KL2n/Ldb1y91ATHujp8DqxAOhiZ7KN', 'aS1okNRRB4/14jPufAbeiN8/iBPiY5Hl80KHmpjM+7vvjb5jiecZ1ZrVDj7eTES4pmVh2v1c106mZLieoqDPYaf/HVbCM4E4n1B6kjbboSOpANADIcqXxGJQ7Be7/Sk9f7KwRusrsMHXmBHgm4wPmwGVZ3QIDAQAB']), + TXT('d202003e2._domainkey.mailtest', 'v=DKIM1; k=ed25519; p=iqwH/hhozFdeo1xnuldr8KUi7O7g+DzmC+f0SYMKVDc='), + TXT('_adsp._domainkey.mailtest', 'dkim=all'), + TXT('_dmarc.mailtest', 'v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s'), + TXT('_report.mailtest', 'r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;'), + TXT('_smtp._tls.mailtest', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + TXT('_smtp-tlsrpt.mailtest', 'v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org'), + SRV('_pgpkey-http._tcp.sks', 0, 0, 0, '.'), + SRV('_pgpkey-https._tcp.sks', 0, 0, 0, '.'), + SRV('_hkp._tcp.sks', 0, 0, 0, '.'), + SRV('_pgpkey-http._tcp.sks-peer', 0, 0, 0, '.'), + SRV('_pgpkey-https._tcp.sks-peer', 0, 0, 0, '.'), + SRV('_hkp._tcp.sks-peer', 0, 0, 0, '.'), + NS('yoyo', 'ns5.he.net.'), + NS('yoyo', 'ns4.he.net.'), + NS('yoyo', 'ns3.he.net.'), + NS('yoyo', 'ns2.he.net.'), + NS('yoyo', 'ns1.he.net.'), + NS('khard', 'ns-cloud-d1.googledomains.com.'), + NS('khard', 'ns-cloud-d2.googledomains.com.'), + NS('khard', 'ns-cloud-d3.googledomains.com.'), + NS('khard', 'ns-cloud-d4.googledomains.com.'), + MX('realhost', 0, '.'), + TXT('realhost', 'v=spf1 -all'), + TLSA('_25._tcp.realhost', 3, 0, 0, '0000000000000000000000000000000000000000000000000000000000000000'), + CNAME('_fedcba9876543210fedcba9876543210.go', '_45678901234abcdef45678901234abcd.ggedgsdned.acm-validations.aws.'), + CNAME('opqrstuvwxyz', 'gv-abcdefghijklmn.dv.googlehosted.com.'), + CNAME('zyxwvutsrqpo', 'gv-nmlkjihgfedcba.dv.googlehosted.com.'), + CNAME('0123456789abcdef0123456789abcdef', 'verify.bing.com.') +) diff --git a/commands/test_data/example.org.zone.tsv b/commands/test_data/example.org.zone.tsv new file mode 100644 index 000000000..ca15d2fc8 --- /dev/null +++ b/commands/test_data/example.org.zone.tsv @@ -0,0 +1,349 @@ +example.org @ 43200 IN SOA ns1.example.org. hostmaster.example.org. 2020030700 7200 3600 864000 7200 +example.org @ 7200 IN NS ns1.example.org. +example.org @ 7200 IN NS ns2.example.org. +example.org @ 7200 IN NS ns-a.example.net. +example.org @ 7200 IN NS friend-dns.example.com. +example.org @ 7200 IN MX 10 mx.example.org. +example.org @ 7200 IN TXT "v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all" +_client._smtp.example.org _client._smtp 7200 IN SRV 1 1 1 example.org. +_client._smtp.mx.example.org _client._smtp.mx 7200 IN SRV 1 2 1 mx.example.org. +_client._smtp.foo.example.org _client._smtp.foo 7200 IN SRV 1 2 1 foo.example.org. +_kerberos._tcp.example.org _kerberos._tcp 7200 IN SRV 10 1 88 kerb-service.example.org. +_kerberos._udp.example.org _kerberos._udp 7200 IN SRV 10 1 88 kerb-service.example.org. +_kpasswd._udp.example.org _kpasswd._udp 7200 IN SRV 10 1 464 kerb-service.example.org. +_kerberos-adm._tcp.example.org _kerberos-adm._tcp 7200 IN SRV 10 1 749 kerb-service.example.org. +_kerberos.example.org _kerberos 7200 IN TXT "EXAMPLE.ORG" +_ldap._tcp.example.org _ldap._tcp 7200 IN SRV 0 0 0 . +_ldap._udp.example.org _ldap._udp 7200 IN SRV 0 0 0 . +_jabber._tcp.example.org _jabber._tcp 7200 IN SRV 10 2 5269 xmpp-s2s.example.org. +_xmpp-server._tcp.example.org _xmpp-server._tcp 7200 IN SRV 10 2 5269 xmpp-s2s.example.org. +_xmpp-client._tcp.example.org _xmpp-client._tcp 7200 IN SRV 10 2 5222 xmpp.example.org. +_im._sip.example.org _im._sip 7200 IN SRV 0 0 0 . +_pres._sip.example.org _pres._sip 7200 IN SRV 0 0 0 . +_sip+d2t._tcp.example.org _sip+d2t._tcp 7200 IN SRV 0 0 0 . +_sips+d2t._tcp.example.org _sips+d2t._tcp 7200 IN SRV 0 0 0 . +_sip+d2u._udp.example.org _sip+d2u._udp 7200 IN SRV 0 0 0 . +_sip+d2s._sctp.example.org _sip+d2s._sctp 7200 IN SRV 0 0 0 . +_sips+d2s._sctp.example.org _sips+d2s._sctp 7200 IN SRV 0 0 0 . +_submission._tcp.example.org _submission._tcp 7200 IN SRV 10 10 587 smtp.example.org. +_submissions._tcp.example.org _submissions._tcp 7200 IN SRV 10 10 465 smtp.example.org. +_imap._tcp.example.org _imap._tcp 7200 IN SRV 10 10 143 imap.example.org. +_imaps._tcp.example.org _imaps._tcp 7200 IN SRV 10 10 993 imap.example.org. +_pop3._tcp.example.org _pop3._tcp 7200 IN SRV 0 0 0 . +_pop3s._tcp.example.org _pop3s._tcp 7200 IN SRV 0 0 0 . +_sieve._tcp.example.org _sieve._tcp 7200 IN SRV 10 10 4190 imap.example.org. +dns-moreinfo.example.org dns-moreinfo 7200 IN TXT "Fred Bloggs, TZ=America/New_York" "Chat-Service-X: @handle1" "Chat-Service-Y: federated-handle@example.org" +_pgpkey-http._tcp.example.org _pgpkey-http._tcp 7200 IN SRV 0 0 0 . +_pgpkey-https._tcp.example.org _pgpkey-https._tcp 7200 IN SRV 0 0 0 . +_hkp._tcp.example.org _hkp._tcp 7200 IN SRV 0 0 0 . +_openpgpkey._tcp.example.org _openpgpkey._tcp 7200 IN SRV 10 10 443 openpgpkey.example.org. +_finger._tcp.example.org _finger._tcp 7200 IN SRV 10 10 79 barbican.example.org. +_avatars-sec._tcp.example.org _avatars-sec._tcp 7200 IN SRV 10 10 443 avatars.example.org. +example.org @ 7200 IN A 192.0.2.1 +example.org @ 7200 IN AAAA 2001:db8::1:1 +_adsp._domainkey.example.org _adsp._domainkey 7200 IN TXT "dkim=all" +_dmarc.example.org _dmarc 7200 IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +d201911._domainkey.example.org d201911._domainkey 7200 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4SmyE5Tz5/wPL8cb2AKuHnlFeLMOhAl1UX/NYaeDCKMWoBPTgZRT0jonKLmV2UscHdodXu5ZsLr/NAuLCp7HmPLReLz7kxKncP6ppveKxc1aq5SPTKeWe77p6BptlahHc35eiXsZRpTsEzrbEOainy1IWEd+w9p1gWbrSutwE22z0i4V88nQ9UBa1ks" "6cVGxXBZFovWC+i28aGs6Lc7cSfHG5+Mrg3ud5X4evYXTGFMPpunMcCsXrqmS5a+5gRSEMZhngha/cHjLwaJnWzKaywNWF5XOsCjL94QkS0joB7lnGOHMNSZBCcu542Y3Ht3SgHhlpkF9mIbIRfpzA9IoSQIDAQAB" +d201911e2._domainkey.example.org d201911e2._domainkey 7200 IN TXT "v=DKIM1; k=ed25519; p=GBt2k2L39KUb39fg5brOppXDHXvISy0+ECGgPld/bIo=" +d202003._domainkey.example.org d202003._domainkey 7200 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/1tQvOEs7xtKNm7PbPgY4hQjwHVvqqkDb0+TeqZHYRSczQ3c0LFJrIDFiPIdwQe/7AuKrxvATSh/uXKZ3EP4ouMgROPZnUxVXENeetJj+pc3nfGwTKUBTTTth+SO74gdIWsntjvAfduzosC4ZkxbDwZ9c253qXARGvGu+LB/iAeq0ngEbm5fU13+Jo" "pv0d4dR6oGe9GvMEnGGLZzNrxWl1BPe2x5JZ5/X/3fW8vJx3OgRB5N6fqbAJ6HZ9kcbikDH4lPPl9RIoprFk7mmwno/nXLQYGhPobmqq8wLkDiXEkWtYa5lzujz3XI3Zkk8ZIOGvdbVVfAttT0IVPnYkOhQIDAQAB" +d202003e2._domainkey.example.org d202003e2._domainkey 7200 IN TXT "v=DKIM1; k=ed25519; p=DQI5d9sNMrr0SLDoAi071IFOyKnlbR29hAQdqVQecQg=" +_report.example.org _report 7200 IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls.example.org _smtp._tls 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt.example.org _smtp-tlsrpt 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +example.net._report._dmarc.example.org example.net._report._dmarc 7200 IN TXT "v=DMARC1" +example.com._report._dmarc.example.org example.com._report._dmarc 7200 IN TXT "v=DMARC1" +xn--2j5b.xn--9t4b11yi5a._report._dmarc.example.org xn--2j5b.xn--9t4b11yi5a._report._dmarc 7200 IN TXT "v=DMARC1" +special.test._report._dmarc.example.org special.test._report._dmarc 7200 IN TXT "v=DMARC1" +xn--qck5b9a5eml3bze.xn--zckzah._report._dmarc.example.org xn--qck5b9a5eml3bze.xn--zckzah._report._dmarc 7200 IN TXT "v=DMARC1" +*._smimecert.example.org *._smimecert 7200 IN CNAME _ourca-smimea.example.org. +b._dns-sd._udp.example.org b._dns-sd._udp 7200 IN PTR field.example.org. +lb._dns-sd._udp.example.org lb._dns-sd._udp 7200 IN PTR field.example.org. +r._dns-sd._udp.example.org r._dns-sd._udp 7200 IN PTR field.example.org. +field.example.org field 7200 IN NS ns1.example.org. +field.example.org field 7200 IN NS ns2.example.org. +barbican.example.org barbican 7200 IN A 192.0.2.1 +barbican.example.org barbican 7200 IN AAAA 2001:db8::1:1 +barbican.ipv4.example.org barbican.ipv4 7200 IN A 192.0.2.1 +barbican.ipv6.example.org barbican.ipv6 7200 IN AAAA 2001:db8::1:1 +megalomaniac.example.org megalomaniac 7200 IN A 198.51.100.254 +megalomaniac.example.org megalomaniac 7200 IN AAAA 2001:db8:ffef::254 +megalomaniac.ipv4.example.org megalomaniac.ipv4 7200 IN A 198.51.100.254 +megalomaniac.ipv6.example.org megalomaniac.ipv6 7200 IN AAAA 2001:db8:ffef::254 +megalomaniac.example.org megalomaniac 7200 IN SSHFP 1 2 4E9CED94D3CAF2CE915F85A63CE7279D5118A79EA03DAC59CF4859B825D2F619 +megalomaniac.example.org megalomaniac 7200 IN SSHFP 3 2 D3556A3DB83AB9CCEC39DC6693DD2F3E28B178C9BBA61880924821C426CC61EB +megalomaniac.example.org megalomaniac 7200 IN SSHFP 4 2 C60C9D9D4728668F5F46986FF0C5B416C5E913862C4970CBFE211A6F44A111B4 +megalomaniac.ipv4.example.org megalomaniac.ipv4 7200 IN SSHFP 1 2 4E9CED94D3CAF2CE915F85A63CE7279D5118A79EA03DAC59CF4859B825D2F619 +megalomaniac.ipv4.example.org megalomaniac.ipv4 7200 IN SSHFP 3 2 D3556A3DB83AB9CCEC39DC6693DD2F3E28B178C9BBA61880924821C426CC61EB +megalomaniac.ipv4.example.org megalomaniac.ipv4 7200 IN SSHFP 4 2 C60C9D9D4728668F5F46986FF0C5B416C5E913862C4970CBFE211A6F44A111B4 +megalomaniac.ipv6.example.org megalomaniac.ipv6 7200 IN SSHFP 1 2 4E9CED94D3CAF2CE915F85A63CE7279D5118A79EA03DAC59CF4859B825D2F619 +megalomaniac.ipv6.example.org megalomaniac.ipv6 7200 IN SSHFP 3 2 D3556A3DB83AB9CCEC39DC6693DD2F3E28B178C9BBA61880924821C426CC61EB +megalomaniac.ipv6.example.org megalomaniac.ipv6 7200 IN SSHFP 4 2 C60C9D9D4728668F5F46986FF0C5B416C5E913862C4970CBFE211A6F44A111B4 +tower.example.org tower 7200 IN A 192.0.2.42 +tower.example.org tower 7200 IN AAAA 2001:db8::1:42 +tower.ipv4.example.org tower.ipv4 7200 IN A 192.0.2.42 +tower.ipv6.example.org tower.ipv6 7200 IN AAAA 2001:db8::1:42 +tower.example.org tower 7200 IN SSHFP 1 2 0F211D236E94768911A294F38653C4AF6FA935A5B06C975D8162F59142571451 +tower.example.org tower 7200 IN SSHFP 3 2 88BF7B7401C11FA2E84871EFB06CD73D8FC409154605B354DB2DDA0B82FE1160 +tower.example.org tower 7200 IN SSHFP 4 2 6D30900BE0FAAAE73568FC007A87B4D076CF9A351ECACC1106AEF726C34AD61D +tower.ipv4.example.org tower.ipv4 7200 IN SSHFP 1 2 0F211D236E94768911A294F38653C4AF6FA935A5B06C975D8162F59142571451 +tower.ipv4.example.org tower.ipv4 7200 IN SSHFP 3 2 88BF7B7401C11FA2E84871EFB06CD73D8FC409154605B354DB2DDA0B82FE1160 +tower.ipv4.example.org tower.ipv4 7200 IN SSHFP 4 2 6D30900BE0FAAAE73568FC007A87B4D076CF9A351ECACC1106AEF726C34AD61D +tower.ipv6.example.org tower.ipv6 7200 IN SSHFP 1 2 0F211D236E94768911A294F38653C4AF6FA935A5B06C975D8162F59142571451 +tower.ipv6.example.org tower.ipv6 7200 IN SSHFP 3 2 88BF7B7401C11FA2E84871EFB06CD73D8FC409154605B354DB2DDA0B82FE1160 +tower.ipv6.example.org tower.ipv6 7200 IN SSHFP 4 2 6D30900BE0FAAAE73568FC007A87B4D076CF9A351ECACC1106AEF726C34AD61D +vcs.example.org vcs 7200 IN A 192.0.2.228 +vcs.example.org vcs 7200 IN AAAA 2001:db8::48:4558:4456:4353 +vcs.ipv4.example.org vcs.ipv4 7200 IN A 192.0.2.228 +vcs.ipv6.example.org vcs.ipv6 7200 IN AAAA 2001:db8::48:4558:4456:4353 +git.example.org git 7200 IN CNAME vcs.example.org. +git.ipv4.example.org git.ipv4 7200 IN CNAME vcs.ipv4.example.org. +git.ipv6.example.org git.ipv6 7200 IN CNAME vcs.ipv6.example.org. +svn.example.org svn 7200 IN AAAA 2001:db8::48:4558:73:766e +vcs.example.org vcs 7200 IN SSHFP 1 2 B518BE390BABDF43CB2D598AA6BEFA6CE6878546BF107B829D0CFC65253A97D4 +vcs.example.org vcs 7200 IN SSHFP 3 2 E92545DC0BF501F72333DDEB7A37AFC2C5B408CE39A3AD95FBC66236F0077323 +vcs.example.org vcs 7200 IN SSHFP 4 2 02289441124A487095A6CDA2E946C6A8ED9087FAF3592EC4135536C3E615521C +vcs.ipv4.example.org vcs.ipv4 7200 IN SSHFP 1 2 B518BE390BABDF43CB2D598AA6BEFA6CE6878546BF107B829D0CFC65253A97D4 +vcs.ipv4.example.org vcs.ipv4 7200 IN SSHFP 3 2 E92545DC0BF501F72333DDEB7A37AFC2C5B408CE39A3AD95FBC66236F0077323 +vcs.ipv4.example.org vcs.ipv4 7200 IN SSHFP 4 2 02289441124A487095A6CDA2E946C6A8ED9087FAF3592EC4135536C3E615521C +vcs.ipv6.example.org vcs.ipv6 7200 IN SSHFP 1 2 B518BE390BABDF43CB2D598AA6BEFA6CE6878546BF107B829D0CFC65253A97D4 +vcs.ipv6.example.org vcs.ipv6 7200 IN SSHFP 3 2 E92545DC0BF501F72333DDEB7A37AFC2C5B408CE39A3AD95FBC66236F0077323 +vcs.ipv6.example.org vcs.ipv6 7200 IN SSHFP 4 2 02289441124A487095A6CDA2E946C6A8ED9087FAF3592EC4135536C3E615521C +nsauth.example.org nsauth 7200 IN A 192.0.2.53 +nsauth.example.org nsauth 7200 IN AAAA 2001:db8::53:1 +nsauth.ipv4.example.org nsauth.ipv4 7200 IN A 192.0.2.53 +nsauth.ipv6.example.org nsauth.ipv6 7200 IN AAAA 2001:db8::53:1 +nsauth.example.org nsauth 7200 IN SSHFP 1 2 895804AE022FFF643B2677563CB850607C5BB564D9919896C521098C8ABC40F2 +nsauth.example.org nsauth 7200 IN SSHFP 3 2 28A65470BADAE611375747E1A803211C41E3D71E97741FA92CCBDF7B01F34E42 +nsauth.example.org nsauth 7200 IN SSHFP 4 2 6E10445C0649C03FA83E18B1873E5B89B3A20893ECB48D01E7CEDB3DD563ECF0 +nsauth.ipv4.example.org nsauth.ipv4 7200 IN SSHFP 1 2 895804AE022FFF643B2677563CB850607C5BB564D9919896C521098C8ABC40F2 +nsauth.ipv4.example.org nsauth.ipv4 7200 IN SSHFP 3 2 28A65470BADAE611375747E1A803211C41E3D71E97741FA92CCBDF7B01F34E42 +nsauth.ipv4.example.org nsauth.ipv4 7200 IN SSHFP 4 2 6E10445C0649C03FA83E18B1873E5B89B3A20893ECB48D01E7CEDB3DD563ECF0 +nsauth.ipv6.example.org nsauth.ipv6 7200 IN SSHFP 1 2 895804AE022FFF643B2677563CB850607C5BB564D9919896C521098C8ABC40F2 +nsauth.ipv6.example.org nsauth.ipv6 7200 IN SSHFP 3 2 28A65470BADAE611375747E1A803211C41E3D71E97741FA92CCBDF7B01F34E42 +nsauth.ipv6.example.org nsauth.ipv6 7200 IN SSHFP 4 2 6E10445C0649C03FA83E18B1873E5B89B3A20893ECB48D01E7CEDB3DD563ECF0 +ns1.example.org ns1 7200 IN A 192.0.2.53 +ns1.example.org ns1 7200 IN AAAA 2001:db8::53:1 +ns2.example.org ns2 7200 IN A 203.0.113.53 +ns2.example.org ns2 7200 IN AAAA 2001:db8:113::53 +hermes.example.org hermes 7200 IN A 192.0.2.25 +hermes.example.org hermes 7200 IN AAAA 2001:db8::48:4558:736d:7470 +hermes.example.org hermes 7200 IN AAAA 2001:db8::48:4558:696d:6170 +hermes.ipv4.example.org hermes.ipv4 7200 IN A 192.0.2.25 +hermes.ipv6.example.org hermes.ipv6 7200 IN AAAA 2001:db8::48:4558:736d:7470 +hermes.ipv6.example.org hermes.ipv6 7200 IN AAAA 2001:db8::48:4558:696d:6170 +hermes.example.org hermes 7200 IN SSHFP 1 2 4472FF5BD0528CD49216AF4503BA6A1C48F121D0292A31D6AF193E5000AF4966 +hermes.example.org hermes 7200 IN SSHFP 3 2 EABA20C1565676A5229184CCFCF82D0EE408F91757A67D9FA51A0B6F3DB4A33B +hermes.example.org hermes 7200 IN SSHFP 4 2 A9D89920E599D04363C8B35A4CE66C1ED257EA1D16981F060B6AED080BBB7A7C +hermes.ipv4.example.org hermes.ipv4 7200 IN SSHFP 1 2 4472FF5BD0528CD49216AF4503BA6A1C48F121D0292A31D6AF193E5000AF4966 +hermes.ipv4.example.org hermes.ipv4 7200 IN SSHFP 3 2 EABA20C1565676A5229184CCFCF82D0EE408F91757A67D9FA51A0B6F3DB4A33B +hermes.ipv4.example.org hermes.ipv4 7200 IN SSHFP 4 2 A9D89920E599D04363C8B35A4CE66C1ED257EA1D16981F060B6AED080BBB7A7C +hermes.ipv6.example.org hermes.ipv6 7200 IN SSHFP 1 2 4472FF5BD0528CD49216AF4503BA6A1C48F121D0292A31D6AF193E5000AF4966 +hermes.ipv6.example.org hermes.ipv6 7200 IN SSHFP 3 2 EABA20C1565676A5229184CCFCF82D0EE408F91757A67D9FA51A0B6F3DB4A33B +hermes.ipv6.example.org hermes.ipv6 7200 IN SSHFP 4 2 A9D89920E599D04363C8B35A4CE66C1ED257EA1D16981F060B6AED080BBB7A7C +kerb-service.example.org kerb-service 7200 IN A 192.0.2.88 +kerb-service.example.org kerb-service 7200 IN AAAA 2001:db8::48:4558:6b65:7262 +security.example.org security 7200 IN A 192.0.2.92 +security.example.org security 7200 IN AAAA 2001:db8::48:4558:53:4543 +security.ipv4.example.org security.ipv4 7200 IN A 192.0.2.92 +security.ipv6.example.org security.ipv6 7200 IN AAAA 2001:db8::48:4558:53:4543 +services.example.org services 7200 IN A 192.0.2.93 +services.example.org services 7200 IN AAAA 2001:db8::48:4558:5345:5256 +services.ipv4.example.org services.ipv4 7200 IN A 192.0.2.93 +services.ipv6.example.org services.ipv6 7200 IN AAAA 2001:db8::48:4558:5345:5256 +openpgpkey.example.org openpgpkey 7200 IN A 192.0.2.92 +openpgpkey.example.org openpgpkey 7200 IN AAAA 2001:db8::48:4558:53:4543 +finger.example.org finger 7200 IN CNAME barbican.example.org. +finger.ipv4.example.org finger.ipv4 7200 IN CNAME barbican.ipv4.example.org. +finger.ipv6.example.org finger.ipv6 7200 IN CNAME barbican.ipv6.example.org. +avatars.example.org avatars 7200 IN A 192.0.2.93 +avatars.example.org avatars 7200 IN AAAA 2001:db8::48:4558:5345:5256 +dict.example.org dict 7200 IN CNAME services.example.org. +people.example.org people 7200 IN CNAME services.example.org. +people.ipv4.example.org people.ipv4 7200 IN CNAME services.ipv4.example.org. +people.ipv6.example.org people.ipv6 7200 IN CNAME services.ipv6.example.org. +wpad.example.org wpad 7200 IN CNAME services.example.org. +www.example.org www 7200 IN CNAME services.example.org. +www.ipv4.example.org www.ipv4 7200 IN CNAME services.ipv4.example.org. +www.ipv6.example.org www.ipv6 7200 IN CNAME services.ipv6.example.org. +example.org @ 7200 IN CAA 0 issue "example.net" +example.org @ 7200 IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v01.api.letsencrypt.org/acme/reg/1234567" +example.org @ 7200 IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-staging-v02.api.letsencrypt.org/acme/acct/23456789" +example.org @ 7200 IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/76543210" +example.org @ 7200 IN CAA 0 issuewild ";" +example.org @ 7200 IN CAA 0 iodef "mailto:security@example.org" +_ourcaca4-tlsa.example.org _ourcaca4-tlsa 7200 IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourcaca5-tlsa.example.org _ourcaca5-tlsa 7200 IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_cacert-c3-tlsa.example.org _cacert-c3-tlsa 7200 IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 +_letsencrypt-tlsa.example.org _letsencrypt-tlsa 7200 IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 +_letsencrypt-tlsa.example.org _letsencrypt-tlsa 7200 IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_amazon-tlsa.example.org _amazon-tlsa 7200 IN TLSA 2 0 1 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e +_amazon-tlsa.example.org _amazon-tlsa 7200 IN TLSA 2 0 1 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 +_amazon-tlsa.example.org _amazon-tlsa 7200 IN TLSA 2 0 1 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 +_amazon-tlsa.example.org _amazon-tlsa 7200 IN TLSA 2 0 1 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 +_ourca-tlsa.example.org _ourca-tlsa 7200 IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourca-tlsa.example.org _ourca-tlsa 7200 IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_ourca-cacert-tlsa.example.org _ourca-cacert-tlsa 7200 IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourca-cacert-tlsa.example.org _ourca-cacert-tlsa 7200 IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_ourca-cacert-tlsa.example.org _ourca-cacert-tlsa 7200 IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 +_ourca-le-tlsa.example.org _ourca-le-tlsa 7200 IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourca-le-tlsa.example.org _ourca-le-tlsa 7200 IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_ourca-le-tlsa.example.org _ourca-le-tlsa 7200 IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 +_ourca-le-tlsa.example.org _ourca-le-tlsa 7200 IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_ourca-cacert-le-tlsa.example.org _ourca-cacert-le-tlsa 7200 IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourca-cacert-le-tlsa.example.org _ourca-cacert-le-tlsa 7200 IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_ourca-cacert-le-tlsa.example.org _ourca-cacert-le-tlsa 7200 IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 +_ourca-cacert-le-tlsa.example.org _ourca-cacert-le-tlsa 7200 IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 +_ourca-cacert-le-tlsa.example.org _ourca-cacert-le-tlsa 7200 IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_cacert-le-tlsa.example.org _cacert-le-tlsa 7200 IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 +_cacert-le-tlsa.example.org _cacert-le-tlsa 7200 IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 +_cacert-le-tlsa.example.org _cacert-le-tlsa 7200 IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_le-amazon-tlsa.example.org _le-amazon-tlsa 7200 IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 +_le-amazon-tlsa.example.org _le-amazon-tlsa 7200 IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_le-amazon-tlsa.example.org _le-amazon-tlsa 7200 IN TLSA 2 0 1 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e +_le-amazon-tlsa.example.org _le-amazon-tlsa 7200 IN TLSA 2 0 1 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 +_le-amazon-tlsa.example.org _le-amazon-tlsa 7200 IN TLSA 2 0 1 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 +_le-amazon-tlsa.example.org _le-amazon-tlsa 7200 IN TLSA 2 0 1 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 0 1 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 0 1 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 0 1 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 +_ourca-le-amazon-tlsa.example.org _ourca-le-amazon-tlsa 7200 IN TLSA 2 0 1 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 +_443._tcp.www.example.org _443._tcp.www 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.www.ipv4.example.org _443._tcp.www.ipv4 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.www.ipv6.example.org _443._tcp.www.ipv6 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.people.example.org _443._tcp.people 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.people.ipv4.example.org _443._tcp.people.ipv4 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.people.ipv6.example.org _443._tcp.people.ipv6 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.git.example.org _443._tcp.git 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.svn.example.org _443._tcp.svn 7200 IN CNAME _ourca-le-tlsa.example.org. +_5222._tcp.xmpp.example.org _5222._tcp.xmpp 7200 IN CNAME _ourca-le-tlsa.example.org. +_5223._tcp.xmpp.example.org _5223._tcp.xmpp 7200 IN CNAME _ourca-le-tlsa.example.org. +_5269._tcp.xmpp-s2s.example.org _5269._tcp.xmpp-s2s 7200 IN CNAME _ourca-le-tlsa.example.org. +_25._tcp.mx.example.org _25._tcp.mx 7200 IN CNAME _ourca-le-tlsa.example.org. +_26._tcp.mx.example.org _26._tcp.mx 7200 IN CNAME _ourca-le-tlsa.example.org. +_27._tcp.mx.example.org _27._tcp.mx 7200 IN CNAME _ourca-le-tlsa.example.org. +_465._tcp.smtp46.example.org _465._tcp.smtp46 7200 IN CNAME _ourca-le-tlsa.example.org. +_587._tcp.smtp46.example.org _587._tcp.smtp46 7200 IN CNAME _ourca-le-tlsa.example.org. +_1465._tcp.smtp46.example.org _1465._tcp.smtp46 7200 IN CNAME _ourca-le-tlsa.example.org. +_1587._tcp.smtp46.example.org _1587._tcp.smtp46 7200 IN CNAME _ourca-le-tlsa.example.org. +_465._tcp.smtp.example.org _465._tcp.smtp 7200 IN CNAME _ourca-le-tlsa.example.org. +_587._tcp.smtp.example.org _587._tcp.smtp 7200 IN CNAME _ourca-le-tlsa.example.org. +_1465._tcp.smtp.example.org _1465._tcp.smtp 7200 IN CNAME _ourca-le-tlsa.example.org. +_1587._tcp.smtp.example.org _1587._tcp.smtp 7200 IN CNAME _ourca-le-tlsa.example.org. +_143._tcp.imap46.example.org _143._tcp.imap46 7200 IN CNAME _ourca-le-tlsa.example.org. +_993._tcp.imap46.example.org _993._tcp.imap46 7200 IN CNAME _ourca-le-tlsa.example.org. +_143._tcp.imap.example.org _143._tcp.imap 7200 IN CNAME _ourca-le-tlsa.example.org. +_993._tcp.imap.example.org _993._tcp.imap 7200 IN CNAME _ourca-le-tlsa.example.org. +_4190._tcp.imap.example.org _4190._tcp.imap 7200 IN CNAME _ourca-le-tlsa.example.org. +www.security.example.org www.security 7200 IN CNAME security.example.org. +www.security.ipv4.example.org www.security.ipv4 7200 IN CNAME security.ipv4.example.org. +www.security.ipv6.example.org www.security.ipv6 7200 IN CNAME security.ipv6.example.org. +_443._tcp.www.security.example.org _443._tcp.www.security 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.www.security.ipv4.example.org _443._tcp.www.security.ipv4 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.www.security.ipv6.example.org _443._tcp.www.security.ipv6 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.security.example.org _443._tcp.security 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.security.ipv4.example.org _443._tcp.security.ipv4 7200 IN CNAME _ourca-le-tlsa.example.org. +_443._tcp.security.ipv6.example.org _443._tcp.security.ipv6 7200 IN CNAME _ourca-le-tlsa.example.org. +_acme-challenge.example.org _acme-challenge 15 IN CNAME _acme-challenge.chat-acme.d.example.net. +_acme-challenge.xmpp.example.org _acme-challenge.xmpp 15 IN CNAME _acme-challenge.xmpp.chat-acme.d.example.net. +_acme-challenge.chat.example.org _acme-challenge.chat 15 IN CNAME _acme-challenge.chat.chat-acme.d.example.net. +_acme-challenge.conference.example.org _acme-challenge.conference 15 IN CNAME _acme-challenge.conference.chat-acme.d.example.net. +_acme-challenge.proxy-chatfiles.example.org _acme-challenge.proxy-chatfiles 15 IN CNAME _acme-challenge.proxy-chatfiles.chat-acme.d.example.net. +_acme-challenge.pubsub.xmpp.example.org _acme-challenge.pubsub.xmpp 15 IN CNAME _acme-challenge.pubsub.xmpp.chat-acme.d.example.net. +imap.example.org imap 7200 IN AAAA 2001:db8::48:4558:696d:6170 +imap.example.org imap 7200 IN A 192.0.2.25 +smtp.example.org smtp 7200 IN AAAA 2001:db8::48:4558:736d:7470 +smtp.example.org smtp 7200 IN A 192.0.2.25 +smtp46.example.org smtp46 7200 IN A 192.0.2.25 +smtp46.example.org smtp46 7200 IN AAAA 2001:db8::48:4558:736d:7470 +imap46.example.org imap46 7200 IN A 192.0.2.25 +imap46.example.org imap46 7200 IN AAAA 2001:db8::48:4558:696d:6170 +mx.example.org mx 7200 IN A 192.0.2.25 +mx.example.org mx 7200 IN AAAA 2001:db8::48:4558:736d:7470 +mx.ipv4.example.org mx.ipv4 7200 IN A 192.0.2.25 +mx.ipv6.example.org mx.ipv6 7200 IN AAAA 2001:db8::48:4558:736d:7470 +mx.example.org mx 7200 IN TXT "v=spf1 a include:_spflarge.example.net -all" +_mta-sts.example.org _mta-sts 7200 IN TXT "v=STSv1; id=20191231r1;" +mta-sts.example.org mta-sts 7200 IN TXT "v=STSv1; id=20191231r1;" +mta-sts.example.org mta-sts 7200 IN A 192.0.2.93 +mta-sts.example.org mta-sts 7200 IN AAAA 2001:db8::48:4558:5345:5256 +xmpp.ipv6.example.org xmpp.ipv6 7200 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp-s2s.ipv6.example.org xmpp-s2s.ipv6 7200 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp.example.org xmpp 7200 IN A 203.0.113.175 +xmpp.example.org xmpp 7200 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp-s2s.example.org xmpp-s2s 7200 IN A 203.0.113.175 +xmpp-s2s.example.org xmpp-s2s 7200 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +proxy-chatfiles.example.org proxy-chatfiles 7200 IN CNAME xmpp.example.org. +fileproxy.xmpp.example.org fileproxy.xmpp 7200 IN CNAME xmpp.example.org. +conference.example.org conference 7200 IN CNAME xmpp-s2s.example.org. +_xmpp-server._tcp.conference.example.org _xmpp-server._tcp.conference 7200 IN SRV 10 2 5269 xmpp-s2s.example.org. +pubsub.xmpp.example.org pubsub.xmpp 7200 IN CNAME xmpp-s2s.example.org. +chat.example.org chat 7200 IN A 203.0.113.175 +chat.example.org chat 7200 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +proxy-chatfiles.chat.example.org proxy-chatfiles.chat 7200 IN CNAME chat.example.org. +fileproxy.chat.example.org fileproxy.chat 7200 IN CNAME chat.example.org. +conference.chat.example.org conference.chat 7200 IN CNAME chat.example.org. +pubsub.chat.example.org pubsub.chat 7200 IN CNAME chat.example.org. +_xmpp-server._tcp.conference.example.org _xmpp-server._tcp.conference 7200 IN SRV 10 2 5269 chat.example.org. +auth.example.org auth 7200 IN AAAA 2001:db8::48:4558:6175:7468 +kpeople.example.org kpeople 7200 IN AAAA 2001:db8::48:4558:6b70:706c +ocsp.security.example.org ocsp.security 7200 IN AAAA 2001:db8::48:4558:6f63:7370 +webauth.example.org webauth 7200 IN AAAA 2001:db8::48:4558:7765:6261 +news-feed.example.org news-feed 7200 IN A 192.0.2.93 +news-feed.example.org news-feed 7200 IN AAAA 2001:db8::48:4558:6e6e:7470 +go.example.org go 7200 IN CNAME abcdefghijklmn.cloudfront.net. +foo.example.org foo 7200 IN A 192.0.2.200 +gladys.example.org gladys 7200 IN MX 10 mx.example.org. +_adsp._domainkey.gladys.example.org _adsp._domainkey.gladys 7200 IN TXT "dkim=all" +_dmarc.gladys.example.org _dmarc.gladys 7200 IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_report.gladys.example.org _report.gladys 7200 IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls.gladys.example.org _smtp._tls.gladys 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt.gladys.example.org _smtp-tlsrpt.gladys 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +fred.example.org fred 7200 IN MX 10 mx.example.org. +fred.example.org fred 7200 IN A 192.0.2.93 +fred.example.org fred 7200 IN AAAA 2001:db8::48:4558:5345:5256 +fred.example.org fred 7200 IN TXT "v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all" +d201911._domainkey.fred.example.org d201911._domainkey.fred 7200 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8/OMUa3PnWh9LqXFVwlAgYDdTtbq3zTtTOSBmJq5yWauzXYcUuSmhW7CsV0QQlacCsQgJlwg9Nl1vO1TosAj5EKUCLTeSqjlWrM7KXKPx8FT71Q9H9wXX4MHUyGrqHFo0OPzcmtHwqcd8AD6MIvJHSRoAfiPPBp8Euc0wGnJZdGS75Hk+wA3MQ2/Tlz" "P2eenyiFyqmUTAGOYsGC/tREsWPiegR/OVxNGlzTY6quHsuVK7UYtIyFnYx9PGWdl3b3p7VjQ5V0Rp+2CLtVrCuS6Zs+/3NhZdM7mdD0a9Jgxakwa1le5YmB5lHTGF7T8quy6TlKe9lMUIRNjqTHfSFz/MwIDAQAB" +d201911e2._domainkey.fred.example.org d201911e2._domainkey.fred 7200 IN TXT "v=DKIM1; k=ed25519; p=rQNsV9YcPJn/WYI1EDLjNbN/VuX1Hqq/oe4htbnhv+A=" +d202003._domainkey.fred.example.org d202003._domainkey.fred 7200 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvpnx7tnRxAnE/poIRbVb2i+f1uQCXWnBHzHurgEyZX0CmGaiJuCbr8SWOW2PoXq9YX8gIv2TS3uzwGv/4yA2yX9Z9zar1LeWUfGgMWLdCol9xfmWrI+6MUzxuwhw/mXwzigbI4bHoakh3ez/i3J9KPS85GfrOODqA1emR13f2pG8EzAcje+rwW2PtYj" "c0h+FMDpeLuPYyYszFbNlrkVUneesxnoz+o4x/s6P14ZoRqz5CR7u6G02HwnNaHads5Eto6FYYErUUTtFmgWuYabHxgLVGRdRQs6B5OBYT/3L2q/lAgmEgdy/QL+c0Psfj99/XQmO8fcM0scBzw2ukQzcUwIDAQAB" +d202003e2._domainkey.fred.example.org d202003e2._domainkey.fred 7200 IN TXT "v=DKIM1; k=ed25519; p=0DAPp/IRLYFI/Z4YSgJRi4gr7xcu1/EfJ5mjVn10aAw=" +_adsp._domainkey.fred.example.org _adsp._domainkey.fred 7200 IN TXT "dkim=all" +_dmarc.fred.example.org _dmarc.fred 7200 IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_report.fred.example.org _report.fred 7200 IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls.fred.example.org _smtp._tls.fred 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt.fred.example.org _smtp-tlsrpt.fred 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +mailtest.example.org mailtest 7200 IN MX 10 mx.example.org. +d201911._domainkey.mailtest.example.org d201911._domainkey.mailtest 7200 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo9xHnjHyhm1weA6FjOqM8LKVsklFt26HXWoe/0XCdmBG4i/UzQ7RiSgWO4kv7anPK6qf6rtL1xYsHufaRXG8yLsZxz+BbUP99eZvxZX78tMg4cGf+yU6uFxulCbOzsMy+8Cc3bbQTtIWYjyWBwnHdRRrCkQxjZ5KAd+x7ZB5qzqg2/eLJ7fCuNsr/xn" "0XTY6XYgug95e3h4CEW3Y+bkG81AMeJmT/hoVTcXvT/Gm6ZOUmx6faQWIHSW7qOR3VS6S75HOuclEUk0gt9r7OQHKl01sXh8g02SHRk8SUMEoNVayqplYZTFFF01Z192m7enmpp+St+HHUIT6jW/CAMCO3wIDAQAB" +d201911e2._domainkey.mailtest.example.org d201911e2._domainkey.mailtest 7200 IN TXT "v=DKIM1; k=ed25519; p=afulDDnhaTzdqKQN0jtWV04eOhAcyBk3NCyVheOf53Y=" +d202003._domainkey.mailtest.example.org d202003._domainkey.mailtest 7200 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2BTVZaVLvL3qZBPaF7tRR0SdOKe+hjcpQ5fqO48lEuYiyTb6lkn8DPjDK11gTN3au0Bm+y8KC7ITKSJosuJXytxt3wqc61Pwtmb/Cy7GzmOF1AuegydB3/88VbgHT5DZucHrh6+ValZk4Trkx+/1K26Uo+h2KL2n/Ldb1y91ATHujp8DqxAOhiZ7KN" "aS1okNRRB4/14jPufAbeiN8/iBPiY5Hl80KHmpjM+7vvjb5jiecZ1ZrVDj7eTES4pmVh2v1c106mZLieoqDPYaf/HVbCM4E4n1B6kjbboSOpANADIcqXxGJQ7Be7/Sk9f7KwRusrsMHXmBHgm4wPmwGVZ3QIDAQAB" +d202003e2._domainkey.mailtest.example.org d202003e2._domainkey.mailtest 7200 IN TXT "v=DKIM1; k=ed25519; p=iqwH/hhozFdeo1xnuldr8KUi7O7g+DzmC+f0SYMKVDc=" +_adsp._domainkey.mailtest.example.org _adsp._domainkey.mailtest 7200 IN TXT "dkim=all" +_dmarc.mailtest.example.org _dmarc.mailtest 7200 IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_report.mailtest.example.org _report.mailtest 7200 IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp._tls.mailtest.example.org _smtp._tls.mailtest 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp-tlsrpt.mailtest.example.org _smtp-tlsrpt.mailtest 7200 IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_pgpkey-http._tcp.sks.example.org _pgpkey-http._tcp.sks 7200 IN SRV 0 0 0 . +_pgpkey-https._tcp.sks.example.org _pgpkey-https._tcp.sks 7200 IN SRV 0 0 0 . +_hkp._tcp.sks.example.org _hkp._tcp.sks 7200 IN SRV 0 0 0 . +_pgpkey-http._tcp.sks-peer.example.org _pgpkey-http._tcp.sks-peer 7200 IN SRV 0 0 0 . +_pgpkey-https._tcp.sks-peer.example.org _pgpkey-https._tcp.sks-peer 7200 IN SRV 0 0 0 . +_hkp._tcp.sks-peer.example.org _hkp._tcp.sks-peer 7200 IN SRV 0 0 0 . +yoyo.example.org yoyo 7200 IN NS ns5.he.net. +yoyo.example.org yoyo 7200 IN NS ns4.he.net. +yoyo.example.org yoyo 7200 IN NS ns3.he.net. +yoyo.example.org yoyo 7200 IN NS ns2.he.net. +yoyo.example.org yoyo 7200 IN NS ns1.he.net. +khard.example.org khard 7200 IN NS ns-cloud-d1.googledomains.com. +khard.example.org khard 7200 IN NS ns-cloud-d2.googledomains.com. +khard.example.org khard 7200 IN NS ns-cloud-d3.googledomains.com. +khard.example.org khard 7200 IN NS ns-cloud-d4.googledomains.com. +realhost.example.org realhost 7200 IN MX 0 . +realhost.example.org realhost 7200 IN TXT "v=spf1 -all" +_25._tcp.realhost.example.org _25._tcp.realhost 7200 IN TLSA 3 0 0 0000000000000000000000000000000000000000000000000000000000000000 +_fedcba9876543210fedcba9876543210.go.example.org _fedcba9876543210fedcba9876543210.go 7200 IN CNAME _45678901234abcdef45678901234abcd.ggedgsdned.acm-validations.aws. +opqrstuvwxyz.example.org opqrstuvwxyz 7200 IN CNAME gv-abcdefghijklmn.dv.googlehosted.com. +zyxwvutsrqpo.example.org zyxwvutsrqpo 7200 IN CNAME gv-nmlkjihgfedcba.dv.googlehosted.com. +0123456789abcdef0123456789abcdef.example.org 0123456789abcdef0123456789abcdef 7200 IN CNAME verify.bing.com. diff --git a/commands/test_data/example.org.zone.zone b/commands/test_data/example.org.zone.zone new file mode 100644 index 000000000..0c4848119 --- /dev/null +++ b/commands/test_data/example.org.zone.zone @@ -0,0 +1,352 @@ +$ORIGIN example.org. +$TTL 7200 +@ 43200 IN SOA ns1.example.org. hostmaster.example.org. 2020030700 7200 3600 864000 7200 + IN NS friend-dns.example.com. + IN NS ns-a.example.net. + IN NS ns1.example.org. + IN NS ns2.example.org. + IN A 192.0.2.1 + IN AAAA 2001:db8::1:1 + IN MX 10 mx.example.org. + IN TXT "v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all" + IN CAA 0 iodef "mailto:security@example.org" + IN CAA 0 issue "example.net" + IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-staging-v02.api.letsencrypt.org/acme/acct/23456789" + IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v01.api.letsencrypt.org/acme/reg/1234567" + IN CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/76543210" + IN CAA 0 issuewild ";" +0123456789abcdef0123456789abcdef IN CNAME verify.bing.com. +_acme-challenge 15 IN CNAME _acme-challenge.chat-acme.d.example.net. +_amazon-tlsa IN TLSA 2 0 1 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 + IN TLSA 2 0 1 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 + IN TLSA 2 0 1 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e + IN TLSA 2 0 1 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 +_cacert-c3-tlsa IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 +_cacert-le-tlsa IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 + IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 + IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_dmarc IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +example.com._report._dmarc IN TXT "v=DMARC1" +example.net._report._dmarc IN TXT "v=DMARC1" +special.test._report._dmarc IN TXT "v=DMARC1" +xn--2j5b.xn--9t4b11yi5a._report._dmarc IN TXT "v=DMARC1" +xn--qck5b9a5eml3bze.xn--zckzah._report._dmarc IN TXT "v=DMARC1" +_adsp._domainkey IN TXT "dkim=all" +d201911._domainkey IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4SmyE5Tz5/wPL8cb2AKuHnlFeLMOhAl1UX/NYaeDCKMWoBPTgZRT0jonKLmV2UscHdodXu5ZsLr/NAuLCp7HmPLReLz7kxKncP6ppveKxc1aq5SPTKeWe77p6BptlahHc35eiXsZRpTsEzrbEOainy1IWEd+w9p1gWbrSutwE22z0i4V88nQ9UBa1ks" "6cVGxXBZFovWC+i28aGs6Lc7cSfHG5+Mrg3ud5X4evYXTGFMPpunMcCsXrqmS5a+5gRSEMZhngha/cHjLwaJnWzKaywNWF5XOsCjL94QkS0joB7lnGOHMNSZBCcu542Y3Ht3SgHhlpkF9mIbIRfpzA9IoSQIDAQAB" +d201911e2._domainkey IN TXT "v=DKIM1; k=ed25519; p=GBt2k2L39KUb39fg5brOppXDHXvISy0+ECGgPld/bIo=" +d202003._domainkey IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/1tQvOEs7xtKNm7PbPgY4hQjwHVvqqkDb0+TeqZHYRSczQ3c0LFJrIDFiPIdwQe/7AuKrxvATSh/uXKZ3EP4ouMgROPZnUxVXENeetJj+pc3nfGwTKUBTTTth+SO74gdIWsntjvAfduzosC4ZkxbDwZ9c253qXARGvGu+LB/iAeq0ngEbm5fU13+Jo" "pv0d4dR6oGe9GvMEnGGLZzNrxWl1BPe2x5JZ5/X/3fW8vJx3OgRB5N6fqbAJ6HZ9kcbikDH4lPPl9RIoprFk7mmwno/nXLQYGhPobmqq8wLkDiXEkWtYa5lzujz3XI3Zkk8ZIOGvdbVVfAttT0IVPnYkOhQIDAQAB" +d202003e2._domainkey IN TXT "v=DKIM1; k=ed25519; p=DQI5d9sNMrr0SLDoAi071IFOyKnlbR29hAQdqVQecQg=" +_kerberos IN TXT "EXAMPLE.ORG" +_le-amazon-tlsa IN TLSA 2 0 1 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 + IN TLSA 2 0 1 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 + IN TLSA 2 0 1 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e + IN TLSA 2 0 1 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 + IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 + IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_letsencrypt-tlsa IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 + IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_mta-sts IN TXT "v=STSv1; id=20191231r1;" +_ourca-cacert-le-tlsa IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 + IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 + IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 + IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 + IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_ourca-cacert-tlsa IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 + IN TLSA 2 0 1 4edde9e55ca453b388887caa25d5c5c5bccf2891d73b87495808293d5fac83c8 + IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourca-le-amazon-tlsa IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 + IN TLSA 2 0 1 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 + IN TLSA 2 0 1 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 + IN TLSA 2 0 1 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e + IN TLSA 2 0 1 e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 + IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 + IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 + IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_ourca-le-tlsa IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 + IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 + IN TLSA 2 1 1 60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18 + IN TLSA 2 1 1 b111dd8a1c2091a89bd4fd60c57f0716cce50feeff8137cdbee0326e02cf362b +_ourca-tlsa IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 + IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourcaca4-tlsa IN TLSA 2 0 1 ea99063a0a3bda9727032cf82da238698b90ba729300703d3956943635f96488 +_ourcaca5-tlsa IN TLSA 2 0 1 11f058f61f97b8adc66ef4801f918c71b10e5c1e3d39afde10408b3026647ef1 +_report IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_sip+d2s._sctp IN SRV 0 0 0 . +_sips+d2s._sctp IN SRV 0 0 0 . +_im._sip IN SRV 0 0 0 . +_pres._sip IN SRV 0 0 0 . +*._smimecert IN CNAME _ourca-smimea.example.org. +_client._smtp IN SRV 1 1 1 example.org. +_smtp-tlsrpt IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_avatars-sec._tcp IN SRV 10 10 443 avatars.example.org. +_finger._tcp IN SRV 10 10 79 barbican.example.org. +_hkp._tcp IN SRV 0 0 0 . +_imap._tcp IN SRV 10 10 143 imap.example.org. +_imaps._tcp IN SRV 10 10 993 imap.example.org. +_jabber._tcp IN SRV 10 2 5269 xmpp-s2s.example.org. +_kerberos._tcp IN SRV 10 1 88 kerb-service.example.org. +_kerberos-adm._tcp IN SRV 10 1 749 kerb-service.example.org. +_ldap._tcp IN SRV 0 0 0 . +_openpgpkey._tcp IN SRV 10 10 443 openpgpkey.example.org. +_pgpkey-http._tcp IN SRV 0 0 0 . +_pgpkey-https._tcp IN SRV 0 0 0 . +_pop3._tcp IN SRV 0 0 0 . +_pop3s._tcp IN SRV 0 0 0 . +_sieve._tcp IN SRV 10 10 4190 imap.example.org. +_sip+d2t._tcp IN SRV 0 0 0 . +_sips+d2t._tcp IN SRV 0 0 0 . +_submission._tcp IN SRV 10 10 587 smtp.example.org. +_submissions._tcp IN SRV 10 10 465 smtp.example.org. +_xmpp-client._tcp IN SRV 10 2 5222 xmpp.example.org. +_xmpp-server._tcp IN SRV 10 2 5269 xmpp-s2s.example.org. +_smtp._tls IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +b._dns-sd._udp IN PTR field.example.org. +lb._dns-sd._udp IN PTR field.example.org. +r._dns-sd._udp IN PTR field.example.org. +_kerberos._udp IN SRV 10 1 88 kerb-service.example.org. +_kpasswd._udp IN SRV 10 1 464 kerb-service.example.org. +_ldap._udp IN SRV 0 0 0 . +_sip+d2u._udp IN SRV 0 0 0 . +auth IN AAAA 2001:db8::48:4558:6175:7468 +avatars IN A 192.0.2.93 + IN AAAA 2001:db8::48:4558:5345:5256 +barbican IN A 192.0.2.1 + IN AAAA 2001:db8::1:1 +chat IN A 203.0.113.175 + IN AAAA 2001:db8::f0ab:cdef:1234:f00f +_acme-challenge.chat 15 IN CNAME _acme-challenge.chat.chat-acme.d.example.net. +conference.chat IN CNAME chat.example.org. +fileproxy.chat IN CNAME chat.example.org. +proxy-chatfiles.chat IN CNAME chat.example.org. +pubsub.chat IN CNAME chat.example.org. +conference IN CNAME xmpp-s2s.example.org. +_acme-challenge.conference 15 IN CNAME _acme-challenge.conference.chat-acme.d.example.net. +_xmpp-server._tcp.conference IN SRV 10 2 5269 chat.example.org. + IN SRV 10 2 5269 xmpp-s2s.example.org. +dict IN CNAME services.example.org. +dns-moreinfo IN TXT "Fred Bloggs, TZ=America/New_York" "Chat-Service-X: @handle1" "Chat-Service-Y: federated-handle@example.org" +field IN NS ns1.example.org. + IN NS ns2.example.org. +finger IN CNAME barbican.example.org. +foo IN A 192.0.2.200 +_client._smtp.foo IN SRV 1 2 1 foo.example.org. +fred IN A 192.0.2.93 + IN AAAA 2001:db8::48:4558:5345:5256 + IN MX 10 mx.example.org. + IN TXT "v=spf1 ip4:192.0.2.25 ip6:2001:db8::1:25 mx include:_spf.example.com ~all" +_dmarc.fred IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_adsp._domainkey.fred IN TXT "dkim=all" +d201911._domainkey.fred IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8/OMUa3PnWh9LqXFVwlAgYDdTtbq3zTtTOSBmJq5yWauzXYcUuSmhW7CsV0QQlacCsQgJlwg9Nl1vO1TosAj5EKUCLTeSqjlWrM7KXKPx8FT71Q9H9wXX4MHUyGrqHFo0OPzcmtHwqcd8AD6MIvJHSRoAfiPPBp8Euc0wGnJZdGS75Hk+wA3MQ2/Tlz" "P2eenyiFyqmUTAGOYsGC/tREsWPiegR/OVxNGlzTY6quHsuVK7UYtIyFnYx9PGWdl3b3p7VjQ5V0Rp+2CLtVrCuS6Zs+/3NhZdM7mdD0a9Jgxakwa1le5YmB5lHTGF7T8quy6TlKe9lMUIRNjqTHfSFz/MwIDAQAB" +d201911e2._domainkey.fred IN TXT "v=DKIM1; k=ed25519; p=rQNsV9YcPJn/WYI1EDLjNbN/VuX1Hqq/oe4htbnhv+A=" +d202003._domainkey.fred IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvpnx7tnRxAnE/poIRbVb2i+f1uQCXWnBHzHurgEyZX0CmGaiJuCbr8SWOW2PoXq9YX8gIv2TS3uzwGv/4yA2yX9Z9zar1LeWUfGgMWLdCol9xfmWrI+6MUzxuwhw/mXwzigbI4bHoakh3ez/i3J9KPS85GfrOODqA1emR13f2pG8EzAcje+rwW2PtYj" "c0h+FMDpeLuPYyYszFbNlrkVUneesxnoz+o4x/s6P14ZoRqz5CR7u6G02HwnNaHads5Eto6FYYErUUTtFmgWuYabHxgLVGRdRQs6B5OBYT/3L2q/lAgmEgdy/QL+c0Psfj99/XQmO8fcM0scBzw2ukQzcUwIDAQAB" +d202003e2._domainkey.fred IN TXT "v=DKIM1; k=ed25519; p=0DAPp/IRLYFI/Z4YSgJRi4gr7xcu1/EfJ5mjVn10aAw=" +_report.fred IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp-tlsrpt.fred IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp._tls.fred IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +git IN CNAME vcs.example.org. +_443._tcp.git IN CNAME _ourca-le-tlsa.example.org. +gladys IN MX 10 mx.example.org. +_dmarc.gladys IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_adsp._domainkey.gladys IN TXT "dkim=all" +_report.gladys IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp-tlsrpt.gladys IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp._tls.gladys IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +go IN CNAME abcdefghijklmn.cloudfront.net. +_fedcba9876543210fedcba9876543210.go IN CNAME _45678901234abcdef45678901234abcd.ggedgsdned.acm-validations.aws. +hermes IN A 192.0.2.25 + IN AAAA 2001:db8::48:4558:696d:6170 + IN AAAA 2001:db8::48:4558:736d:7470 + IN SSHFP 1 2 4472FF5BD0528CD49216AF4503BA6A1C48F121D0292A31D6AF193E5000AF4966 + IN SSHFP 3 2 EABA20C1565676A5229184CCFCF82D0EE408F91757A67D9FA51A0B6F3DB4A33B + IN SSHFP 4 2 A9D89920E599D04363C8B35A4CE66C1ED257EA1D16981F060B6AED080BBB7A7C +imap IN A 192.0.2.25 + IN AAAA 2001:db8::48:4558:696d:6170 +_143._tcp.imap IN CNAME _ourca-le-tlsa.example.org. +_4190._tcp.imap IN CNAME _ourca-le-tlsa.example.org. +_993._tcp.imap IN CNAME _ourca-le-tlsa.example.org. +imap46 IN A 192.0.2.25 + IN AAAA 2001:db8::48:4558:696d:6170 +_143._tcp.imap46 IN CNAME _ourca-le-tlsa.example.org. +_993._tcp.imap46 IN CNAME _ourca-le-tlsa.example.org. +barbican.ipv4 IN A 192.0.2.1 +finger.ipv4 IN CNAME barbican.ipv4.example.org. +git.ipv4 IN CNAME vcs.ipv4.example.org. +hermes.ipv4 IN A 192.0.2.25 + IN SSHFP 1 2 4472FF5BD0528CD49216AF4503BA6A1C48F121D0292A31D6AF193E5000AF4966 + IN SSHFP 3 2 EABA20C1565676A5229184CCFCF82D0EE408F91757A67D9FA51A0B6F3DB4A33B + IN SSHFP 4 2 A9D89920E599D04363C8B35A4CE66C1ED257EA1D16981F060B6AED080BBB7A7C +megalomaniac.ipv4 IN A 198.51.100.254 + IN SSHFP 1 2 4E9CED94D3CAF2CE915F85A63CE7279D5118A79EA03DAC59CF4859B825D2F619 + IN SSHFP 3 2 D3556A3DB83AB9CCEC39DC6693DD2F3E28B178C9BBA61880924821C426CC61EB + IN SSHFP 4 2 C60C9D9D4728668F5F46986FF0C5B416C5E913862C4970CBFE211A6F44A111B4 +mx.ipv4 IN A 192.0.2.25 +nsauth.ipv4 IN A 192.0.2.53 + IN SSHFP 1 2 895804AE022FFF643B2677563CB850607C5BB564D9919896C521098C8ABC40F2 + IN SSHFP 3 2 28A65470BADAE611375747E1A803211C41E3D71E97741FA92CCBDF7B01F34E42 + IN SSHFP 4 2 6E10445C0649C03FA83E18B1873E5B89B3A20893ECB48D01E7CEDB3DD563ECF0 +people.ipv4 IN CNAME services.ipv4.example.org. +_443._tcp.people.ipv4 IN CNAME _ourca-le-tlsa.example.org. +security.ipv4 IN A 192.0.2.92 +_443._tcp.security.ipv4 IN CNAME _ourca-le-tlsa.example.org. +www.security.ipv4 IN CNAME security.ipv4.example.org. +_443._tcp.www.security.ipv4 IN CNAME _ourca-le-tlsa.example.org. +services.ipv4 IN A 192.0.2.93 +tower.ipv4 IN A 192.0.2.42 + IN SSHFP 1 2 0F211D236E94768911A294F38653C4AF6FA935A5B06C975D8162F59142571451 + IN SSHFP 3 2 88BF7B7401C11FA2E84871EFB06CD73D8FC409154605B354DB2DDA0B82FE1160 + IN SSHFP 4 2 6D30900BE0FAAAE73568FC007A87B4D076CF9A351ECACC1106AEF726C34AD61D +vcs.ipv4 IN A 192.0.2.228 + IN SSHFP 1 2 B518BE390BABDF43CB2D598AA6BEFA6CE6878546BF107B829D0CFC65253A97D4 + IN SSHFP 3 2 E92545DC0BF501F72333DDEB7A37AFC2C5B408CE39A3AD95FBC66236F0077323 + IN SSHFP 4 2 02289441124A487095A6CDA2E946C6A8ED9087FAF3592EC4135536C3E615521C +www.ipv4 IN CNAME services.ipv4.example.org. +_443._tcp.www.ipv4 IN CNAME _ourca-le-tlsa.example.org. +barbican.ipv6 IN AAAA 2001:db8::1:1 +finger.ipv6 IN CNAME barbican.ipv6.example.org. +git.ipv6 IN CNAME vcs.ipv6.example.org. +hermes.ipv6 IN AAAA 2001:db8::48:4558:696d:6170 + IN AAAA 2001:db8::48:4558:736d:7470 + IN SSHFP 1 2 4472FF5BD0528CD49216AF4503BA6A1C48F121D0292A31D6AF193E5000AF4966 + IN SSHFP 3 2 EABA20C1565676A5229184CCFCF82D0EE408F91757A67D9FA51A0B6F3DB4A33B + IN SSHFP 4 2 A9D89920E599D04363C8B35A4CE66C1ED257EA1D16981F060B6AED080BBB7A7C +megalomaniac.ipv6 IN AAAA 2001:db8:ffef::254 + IN SSHFP 1 2 4E9CED94D3CAF2CE915F85A63CE7279D5118A79EA03DAC59CF4859B825D2F619 + IN SSHFP 3 2 D3556A3DB83AB9CCEC39DC6693DD2F3E28B178C9BBA61880924821C426CC61EB + IN SSHFP 4 2 C60C9D9D4728668F5F46986FF0C5B416C5E913862C4970CBFE211A6F44A111B4 +mx.ipv6 IN AAAA 2001:db8::48:4558:736d:7470 +nsauth.ipv6 IN AAAA 2001:db8::53:1 + IN SSHFP 1 2 895804AE022FFF643B2677563CB850607C5BB564D9919896C521098C8ABC40F2 + IN SSHFP 3 2 28A65470BADAE611375747E1A803211C41E3D71E97741FA92CCBDF7B01F34E42 + IN SSHFP 4 2 6E10445C0649C03FA83E18B1873E5B89B3A20893ECB48D01E7CEDB3DD563ECF0 +people.ipv6 IN CNAME services.ipv6.example.org. +_443._tcp.people.ipv6 IN CNAME _ourca-le-tlsa.example.org. +security.ipv6 IN AAAA 2001:db8::48:4558:53:4543 +_443._tcp.security.ipv6 IN CNAME _ourca-le-tlsa.example.org. +www.security.ipv6 IN CNAME security.ipv6.example.org. +_443._tcp.www.security.ipv6 IN CNAME _ourca-le-tlsa.example.org. +services.ipv6 IN AAAA 2001:db8::48:4558:5345:5256 +tower.ipv6 IN AAAA 2001:db8::1:42 + IN SSHFP 1 2 0F211D236E94768911A294F38653C4AF6FA935A5B06C975D8162F59142571451 + IN SSHFP 3 2 88BF7B7401C11FA2E84871EFB06CD73D8FC409154605B354DB2DDA0B82FE1160 + IN SSHFP 4 2 6D30900BE0FAAAE73568FC007A87B4D076CF9A351ECACC1106AEF726C34AD61D +vcs.ipv6 IN AAAA 2001:db8::48:4558:4456:4353 + IN SSHFP 1 2 B518BE390BABDF43CB2D598AA6BEFA6CE6878546BF107B829D0CFC65253A97D4 + IN SSHFP 3 2 E92545DC0BF501F72333DDEB7A37AFC2C5B408CE39A3AD95FBC66236F0077323 + IN SSHFP 4 2 02289441124A487095A6CDA2E946C6A8ED9087FAF3592EC4135536C3E615521C +www.ipv6 IN CNAME services.ipv6.example.org. +_443._tcp.www.ipv6 IN CNAME _ourca-le-tlsa.example.org. +xmpp.ipv6 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +xmpp-s2s.ipv6 IN AAAA 2001:db8::f0ab:cdef:1234:f00f +kerb-service IN A 192.0.2.88 + IN AAAA 2001:db8::48:4558:6b65:7262 +khard IN NS ns-cloud-d1.googledomains.com. + IN NS ns-cloud-d2.googledomains.com. + IN NS ns-cloud-d3.googledomains.com. + IN NS ns-cloud-d4.googledomains.com. +kpeople IN AAAA 2001:db8::48:4558:6b70:706c +mailtest IN MX 10 mx.example.org. +_dmarc.mailtest IN TXT "v=DMARC1; p=none; sp=none; rua=mailto:dmarc-notify@example.org; ruf=mailto:dmarc-notify@example.org; adkim=s" +_adsp._domainkey.mailtest IN TXT "dkim=all" +d201911._domainkey.mailtest IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo9xHnjHyhm1weA6FjOqM8LKVsklFt26HXWoe/0XCdmBG4i/UzQ7RiSgWO4kv7anPK6qf6rtL1xYsHufaRXG8yLsZxz+BbUP99eZvxZX78tMg4cGf+yU6uFxulCbOzsMy+8Cc3bbQTtIWYjyWBwnHdRRrCkQxjZ5KAd+x7ZB5qzqg2/eLJ7fCuNsr/xn" "0XTY6XYgug95e3h4CEW3Y+bkG81AMeJmT/hoVTcXvT/Gm6ZOUmx6faQWIHSW7qOR3VS6S75HOuclEUk0gt9r7OQHKl01sXh8g02SHRk8SUMEoNVayqplYZTFFF01Z192m7enmpp+St+HHUIT6jW/CAMCO3wIDAQAB" +d201911e2._domainkey.mailtest IN TXT "v=DKIM1; k=ed25519; p=afulDDnhaTzdqKQN0jtWV04eOhAcyBk3NCyVheOf53Y=" +d202003._domainkey.mailtest IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2BTVZaVLvL3qZBPaF7tRR0SdOKe+hjcpQ5fqO48lEuYiyTb6lkn8DPjDK11gTN3au0Bm+y8KC7ITKSJosuJXytxt3wqc61Pwtmb/Cy7GzmOF1AuegydB3/88VbgHT5DZucHrh6+ValZk4Trkx+/1K26Uo+h2KL2n/Ldb1y91ATHujp8DqxAOhiZ7KN" "aS1okNRRB4/14jPufAbeiN8/iBPiY5Hl80KHmpjM+7vvjb5jiecZ1ZrVDj7eTES4pmVh2v1c106mZLieoqDPYaf/HVbCM4E4n1B6kjbboSOpANADIcqXxGJQ7Be7/Sk9f7KwRusrsMHXmBHgm4wPmwGVZ3QIDAQAB" +d202003e2._domainkey.mailtest IN TXT "v=DKIM1; k=ed25519; p=iqwH/hhozFdeo1xnuldr8KUi7O7g+DzmC+f0SYMKVDc=" +_report.mailtest IN TXT "r=abuse-reports@example.org; rf=ARF; re=postmaster@example.org;" +_smtp-tlsrpt.mailtest IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +_smtp._tls.mailtest IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-reports@example.org" +megalomaniac IN A 198.51.100.254 + IN AAAA 2001:db8:ffef::254 + IN SSHFP 1 2 4E9CED94D3CAF2CE915F85A63CE7279D5118A79EA03DAC59CF4859B825D2F619 + IN SSHFP 3 2 D3556A3DB83AB9CCEC39DC6693DD2F3E28B178C9BBA61880924821C426CC61EB + IN SSHFP 4 2 C60C9D9D4728668F5F46986FF0C5B416C5E913862C4970CBFE211A6F44A111B4 +mta-sts IN A 192.0.2.93 + IN AAAA 2001:db8::48:4558:5345:5256 + IN TXT "v=STSv1; id=20191231r1;" +mx IN A 192.0.2.25 + IN AAAA 2001:db8::48:4558:736d:7470 + IN TXT "v=spf1 a include:_spflarge.example.net -all" +_client._smtp.mx IN SRV 1 2 1 mx.example.org. +_25._tcp.mx IN CNAME _ourca-le-tlsa.example.org. +_26._tcp.mx IN CNAME _ourca-le-tlsa.example.org. +_27._tcp.mx IN CNAME _ourca-le-tlsa.example.org. +news-feed IN A 192.0.2.93 + IN AAAA 2001:db8::48:4558:6e6e:7470 +ns1 IN A 192.0.2.53 + IN AAAA 2001:db8::53:1 +ns2 IN A 203.0.113.53 + IN AAAA 2001:db8:113::53 +nsauth IN A 192.0.2.53 + IN AAAA 2001:db8::53:1 + IN SSHFP 1 2 895804AE022FFF643B2677563CB850607C5BB564D9919896C521098C8ABC40F2 + IN SSHFP 3 2 28A65470BADAE611375747E1A803211C41E3D71E97741FA92CCBDF7B01F34E42 + IN SSHFP 4 2 6E10445C0649C03FA83E18B1873E5B89B3A20893ECB48D01E7CEDB3DD563ECF0 +openpgpkey IN A 192.0.2.92 + IN AAAA 2001:db8::48:4558:53:4543 +opqrstuvwxyz IN CNAME gv-abcdefghijklmn.dv.googlehosted.com. +people IN CNAME services.example.org. +_443._tcp.people IN CNAME _ourca-le-tlsa.example.org. +proxy-chatfiles IN CNAME xmpp.example.org. +_acme-challenge.proxy-chatfiles 15 IN CNAME _acme-challenge.proxy-chatfiles.chat-acme.d.example.net. +realhost IN MX 0 . + IN TXT "v=spf1 -all" +_25._tcp.realhost IN TLSA 3 0 0 0000000000000000000000000000000000000000000000000000000000000000 +security IN A 192.0.2.92 + IN AAAA 2001:db8::48:4558:53:4543 +_443._tcp.security IN CNAME _ourca-le-tlsa.example.org. +ocsp.security IN AAAA 2001:db8::48:4558:6f63:7370 +www.security IN CNAME security.example.org. +_443._tcp.www.security IN CNAME _ourca-le-tlsa.example.org. +services IN A 192.0.2.93 + IN AAAA 2001:db8::48:4558:5345:5256 +_hkp._tcp.sks IN SRV 0 0 0 . +_pgpkey-http._tcp.sks IN SRV 0 0 0 . +_pgpkey-https._tcp.sks IN SRV 0 0 0 . +_hkp._tcp.sks-peer IN SRV 0 0 0 . +_pgpkey-http._tcp.sks-peer IN SRV 0 0 0 . +_pgpkey-https._tcp.sks-peer IN SRV 0 0 0 . +smtp IN A 192.0.2.25 + IN AAAA 2001:db8::48:4558:736d:7470 +_1465._tcp.smtp IN CNAME _ourca-le-tlsa.example.org. +_1587._tcp.smtp IN CNAME _ourca-le-tlsa.example.org. +_465._tcp.smtp IN CNAME _ourca-le-tlsa.example.org. +_587._tcp.smtp IN CNAME _ourca-le-tlsa.example.org. +smtp46 IN A 192.0.2.25 + IN AAAA 2001:db8::48:4558:736d:7470 +_1465._tcp.smtp46 IN CNAME _ourca-le-tlsa.example.org. +_1587._tcp.smtp46 IN CNAME _ourca-le-tlsa.example.org. +_465._tcp.smtp46 IN CNAME _ourca-le-tlsa.example.org. +_587._tcp.smtp46 IN CNAME _ourca-le-tlsa.example.org. +svn IN AAAA 2001:db8::48:4558:73:766e +_443._tcp.svn IN CNAME _ourca-le-tlsa.example.org. +tower IN A 192.0.2.42 + IN AAAA 2001:db8::1:42 + IN SSHFP 1 2 0F211D236E94768911A294F38653C4AF6FA935A5B06C975D8162F59142571451 + IN SSHFP 3 2 88BF7B7401C11FA2E84871EFB06CD73D8FC409154605B354DB2DDA0B82FE1160 + IN SSHFP 4 2 6D30900BE0FAAAE73568FC007A87B4D076CF9A351ECACC1106AEF726C34AD61D +vcs IN A 192.0.2.228 + IN AAAA 2001:db8::48:4558:4456:4353 + IN SSHFP 1 2 B518BE390BABDF43CB2D598AA6BEFA6CE6878546BF107B829D0CFC65253A97D4 + IN SSHFP 3 2 E92545DC0BF501F72333DDEB7A37AFC2C5B408CE39A3AD95FBC66236F0077323 + IN SSHFP 4 2 02289441124A487095A6CDA2E946C6A8ED9087FAF3592EC4135536C3E615521C +webauth IN AAAA 2001:db8::48:4558:7765:6261 +wpad IN CNAME services.example.org. +www IN CNAME services.example.org. +_443._tcp.www IN CNAME _ourca-le-tlsa.example.org. +xmpp IN A 203.0.113.175 + IN AAAA 2001:db8::f0ab:cdef:1234:f00f +_acme-challenge.xmpp 15 IN CNAME _acme-challenge.xmpp.chat-acme.d.example.net. +_5222._tcp.xmpp IN CNAME _ourca-le-tlsa.example.org. +_5223._tcp.xmpp IN CNAME _ourca-le-tlsa.example.org. +fileproxy.xmpp IN CNAME xmpp.example.org. +pubsub.xmpp IN CNAME xmpp-s2s.example.org. +_acme-challenge.pubsub.xmpp 15 IN CNAME _acme-challenge.pubsub.xmpp.chat-acme.d.example.net. +xmpp-s2s IN A 203.0.113.175 + IN AAAA 2001:db8::f0ab:cdef:1234:f00f +_5269._tcp.xmpp-s2s IN CNAME _ourca-le-tlsa.example.org. +yoyo IN NS ns1.he.net. + IN NS ns2.he.net. + IN NS ns3.he.net. + IN NS ns4.he.net. + IN NS ns5.he.net. +zyxwvutsrqpo IN CNAME gv-nmlkjihgfedcba.dv.googlehosted.com. + diff --git a/commands/test_data/simple.com.zone b/commands/test_data/simple.com.zone new file mode 100644 index 000000000..258311096 --- /dev/null +++ b/commands/test_data/simple.com.zone @@ -0,0 +1,21 @@ +$ORIGIN simple.com. +$TTL 300 +@ IN SOA ns3.serverfault.com. sysadmin.stackoverflow.com. 2020022300 3600 600 604800 1440 + 172800 IN NS ns-1313.awsdns-36.org. + 172800 IN NS ns-736.awsdns-28.net. + 172800 IN NS ns-cloud-c1.googledomains.com. + 172800 IN NS ns-cloud-c2.googledomains.com. + IN MX 1 aspmx.l.google.com. + IN MX 5 alt1.aspmx.l.google.com. + IN MX 5 alt2.aspmx.l.google.com. + IN MX 10 alt3.aspmx.l.google.com. + IN MX 10 alt4.aspmx.l.google.com. + IN TXT "google-site-verification=O54a_pYHGr4EB8iLoGFgX8OTZ1DkP1KWnOLpx0YCazI" + IN TXT "v=spf1 mx include:mktomail.com ~all" +m1._domainkey IN TXT "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCZfEV2C82eJ4OA3Mslz4C6msjYYalg1eUcHeJQ//QM1hOZSvn4qz+hSKGi7jwNDqsZNzM8vCt2+XzdDYL3JddwUEhoDsIsZsJW0qzIVVLLWCg6TLNS3FpVyjc171o94dpoHFekfswWDoEwFQ03Woq2jchYWBrbUf7MMcdEj/EQqwIDAQAB" +dev IN CNAME stackoverflowsandbox2.mktoweb.com. +dev-email IN CNAME mkto-sj310056.com. +m1._domainkey.dev-email IN TXT "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIBezZ2Gc+/3PghWk+YOE6T9HdwgUTMTR0Fne2i51MNN9Qs7AqDitVdG/949iDbI2fPNZSnKtOcnlLYwvve9MhMAMI1nZ26ILhgaBJi2BMZQpGFlO4ucuo/Uj4DPZ5Ge/NZHCX0CRhAhR5sRmL2OffNcFXFrymzUuz4KzI/NyUiwIDAQAB" +email IN CNAME mkto-sj280138.com. +info IN CNAME stackoverflow.mktoweb.com. +_sip._tcp IN SRV 10 60 5060 bigbox.example.com. diff --git a/commands/test_data/simple.com.zone.js b/commands/test_data/simple.com.zone.js new file mode 100644 index 000000000..ce6452b14 --- /dev/null +++ b/commands/test_data/simple.com.zone.js @@ -0,0 +1,24 @@ +var bind = NewDnsProvider("bind", "BIND"); +var REG_CHANGEME = NewRegistrar("ThirdParty", "NONE"); +D("simple.com", REG_CHANGEME, + DnsProvider(bind), + //SOA('@', 'ns3.serverfault.com.', 'sysadmin.stackoverflow.com.', 2020022300, 3600, 600, 604800, 1440), + NAMESERVER('ns-1313.awsdns-36.org.'), + NAMESERVER('ns-736.awsdns-28.net.'), + NAMESERVER('ns-cloud-c1.googledomains.com.'), + NAMESERVER('ns-cloud-c2.googledomains.com.'), + MX('@', 1, 'aspmx.l.google.com.'), + MX('@', 5, 'alt1.aspmx.l.google.com.'), + MX('@', 5, 'alt2.aspmx.l.google.com.'), + MX('@', 10, 'alt3.aspmx.l.google.com.'), + MX('@', 10, 'alt4.aspmx.l.google.com.'), + TXT('@', 'google-site-verification=O54a_pYHGr4EB8iLoGFgX8OTZ1DkP1KWnOLpx0YCazI'), + TXT('@', 'v=spf1 mx include:mktomail.com ~all'), + TXT('m1._domainkey', 'v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCZfEV2C82eJ4OA3Mslz4C6msjYYalg1eUcHeJQ//QM1hOZSvn4qz+hSKGi7jwNDqsZNzM8vCt2+XzdDYL3JddwUEhoDsIsZsJW0qzIVVLLWCg6TLNS3FpVyjc171o94dpoHFekfswWDoEwFQ03Woq2jchYWBrbUf7MMcdEj/EQqwIDAQAB'), + CNAME('dev', 'stackoverflowsandbox2.mktoweb.com.'), + CNAME('dev-email', 'mkto-sj310056.com.'), + TXT('m1._domainkey.dev-email', 'v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIBezZ2Gc+/3PghWk+YOE6T9HdwgUTMTR0Fne2i51MNN9Qs7AqDitVdG/949iDbI2fPNZSnKtOcnlLYwvve9MhMAMI1nZ26ILhgaBJi2BMZQpGFlO4ucuo/Uj4DPZ5Ge/NZHCX0CRhAhR5sRmL2OffNcFXFrymzUuz4KzI/NyUiwIDAQAB'), + CNAME('email', 'mkto-sj280138.com.'), + CNAME('info', 'stackoverflow.mktoweb.com.'), + SRV('_sip._tcp', 10, 60, 5060, 'bigbox.example.com.') +) diff --git a/commands/test_data/simple.com.zone.tsv b/commands/test_data/simple.com.zone.tsv new file mode 100644 index 000000000..7e478f434 --- /dev/null +++ b/commands/test_data/simple.com.zone.tsv @@ -0,0 +1,19 @@ +simple.com @ 300 IN SOA ns3.serverfault.com. sysadmin.stackoverflow.com. 2020022300 3600 600 604800 1440 +simple.com @ 172800 IN NS ns-1313.awsdns-36.org. +simple.com @ 172800 IN NS ns-736.awsdns-28.net. +simple.com @ 172800 IN NS ns-cloud-c1.googledomains.com. +simple.com @ 172800 IN NS ns-cloud-c2.googledomains.com. +simple.com @ 300 IN MX 1 aspmx.l.google.com. +simple.com @ 300 IN MX 5 alt1.aspmx.l.google.com. +simple.com @ 300 IN MX 5 alt2.aspmx.l.google.com. +simple.com @ 300 IN MX 10 alt3.aspmx.l.google.com. +simple.com @ 300 IN MX 10 alt4.aspmx.l.google.com. +simple.com @ 300 IN TXT "google-site-verification=O54a_pYHGr4EB8iLoGFgX8OTZ1DkP1KWnOLpx0YCazI" +simple.com @ 300 IN TXT "v=spf1 mx include:mktomail.com ~all" +m1._domainkey.simple.com m1._domainkey 300 IN TXT "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCZfEV2C82eJ4OA3Mslz4C6msjYYalg1eUcHeJQ//QM1hOZSvn4qz+hSKGi7jwNDqsZNzM8vCt2+XzdDYL3JddwUEhoDsIsZsJW0qzIVVLLWCg6TLNS3FpVyjc171o94dpoHFekfswWDoEwFQ03Woq2jchYWBrbUf7MMcdEj/EQqwIDAQAB" +dev.simple.com dev 300 IN CNAME stackoverflowsandbox2.mktoweb.com. +dev-email.simple.com dev-email 300 IN CNAME mkto-sj310056.com. +m1._domainkey.dev-email.simple.com m1._domainkey.dev-email 300 IN TXT "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIBezZ2Gc+/3PghWk+YOE6T9HdwgUTMTR0Fne2i51MNN9Qs7AqDitVdG/949iDbI2fPNZSnKtOcnlLYwvve9MhMAMI1nZ26ILhgaBJi2BMZQpGFlO4ucuo/Uj4DPZ5Ge/NZHCX0CRhAhR5sRmL2OffNcFXFrymzUuz4KzI/NyUiwIDAQAB" +email.simple.com email 300 IN CNAME mkto-sj280138.com. +info.simple.com info 300 IN CNAME stackoverflow.mktoweb.com. +_sip._tcp.simple.com _sip._tcp 300 IN SRV 10 60 5060 bigbox.example.com. diff --git a/commands/test_data/simple.com.zone.zone b/commands/test_data/simple.com.zone.zone new file mode 100644 index 000000000..f3bac054d --- /dev/null +++ b/commands/test_data/simple.com.zone.zone @@ -0,0 +1,22 @@ +$ORIGIN simple.com. +$TTL 300 +@ IN SOA ns3.serverfault.com. sysadmin.stackoverflow.com. 2020022300 3600 600 604800 1440 + 172800 IN NS ns-1313.awsdns-36.org. + 172800 IN NS ns-736.awsdns-28.net. + 172800 IN NS ns-cloud-c1.googledomains.com. + 172800 IN NS ns-cloud-c2.googledomains.com. + IN MX 1 aspmx.l.google.com. + IN MX 5 alt1.aspmx.l.google.com. + IN MX 5 alt2.aspmx.l.google.com. + IN MX 10 alt3.aspmx.l.google.com. + IN MX 10 alt4.aspmx.l.google.com. + IN TXT "google-site-verification=O54a_pYHGr4EB8iLoGFgX8OTZ1DkP1KWnOLpx0YCazI" + IN TXT "v=spf1 mx include:mktomail.com ~all" +m1._domainkey IN TXT "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCZfEV2C82eJ4OA3Mslz4C6msjYYalg1eUcHeJQ//QM1hOZSvn4qz+hSKGi7jwNDqsZNzM8vCt2+XzdDYL3JddwUEhoDsIsZsJW0qzIVVLLWCg6TLNS3FpVyjc171o94dpoHFekfswWDoEwFQ03Woq2jchYWBrbUf7MMcdEj/EQqwIDAQAB" +_sip._tcp IN SRV 10 60 5060 bigbox.example.com. +dev IN CNAME stackoverflowsandbox2.mktoweb.com. +dev-email IN CNAME mkto-sj310056.com. +m1._domainkey.dev-email IN TXT "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIBezZ2Gc+/3PghWk+YOE6T9HdwgUTMTR0Fne2i51MNN9Qs7AqDitVdG/949iDbI2fPNZSnKtOcnlLYwvve9MhMAMI1nZ26ILhgaBJi2BMZQpGFlO4ucuo/Uj4DPZ5Ge/NZHCX0CRhAhR5sRmL2OffNcFXFrymzUuz4KzI/NyUiwIDAQAB" +email IN CNAME mkto-sj280138.com. +info IN CNAME stackoverflow.mktoweb.com. + diff --git a/docs/get-zones.md b/docs/get-zones.md index bb71e8f30..2bf07bc36 100644 --- a/docs/get-zones.md +++ b/docs/get-zones.md @@ -19,7 +19,7 @@ Syntax: dnscontrol get-zones [command options] credkey provider zone [...] --creds value Provider credentials JSON file (default: "creds.json") - --format value Output format: js zone tsv nameonly (default: "zone") + --format value Output format: js djs zone tsv nameonly (default: "zone") --out value Instead of stdout, write to this file --ttl value Default TTL (0 picks the zone's most common TTL) (default: 0) @@ -29,31 +29,34 @@ ARGUMENTS: zone: One or more zones (domains) to download; or "all". FORMATS: - --format=js dnsconfig.js format (not perfect, but a decent first draft) + --format=js dnsconfig.js format (not perfect, just a decent first draft) + --format=djs js with disco commas --format=zone BIND Zonefile format --format=tsv TAB separated value (useful for AWK) --format=nameonly Just print the zone names -When using `tsv`, the columns are: +The columns in --format=tsv are: FQDN (the label with the domain) ShortName (just the label, "@" if it is the naked domain) TTL Record Type (A, AAAA, CNAME, etc.) Target and arguments (quoted like in a zonefile) -The --ttl flag applies to zone and js formats. +The --ttl flag only applies to zone/js/djs formats. EXAMPLES: dnscontrol get-zones myr53 ROUTE53 example.com dnscontrol get-zones gmain GANDI_V5 example.comn other.com dnscontrol get-zones cfmain CLOUDFLAREAPI all dnscontrol get-zones -format=tsv bind BIND example.com - dnscontrol get-zones -format=js -out=draft.js glcoud GCLOUD example.com`, + dnscontrol get-zones -format=djs -out=draft.js glcoud GCLOUD example.com`, +Read a zonefile, generate a JS file, then use the JS file to see how +different it is from the zonefile: -# Example commands + dnscontrol get-zone --format=js -out=foo.djs bind BIND example.org + dnscontrol preview --config foo.js -dnscontrol get-zone # Developer Note diff --git a/go.mod b/go.mod index 4a8f9d85d..e3114ffc7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/Azure/go-autorest/autorest/to v0.3.0 github.com/DisposaBoy/JsonConfigReader v0.0.0-20171218180944-5ea4d0ddac55 github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d + github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 github.com/aws/aws-sdk-go v1.29.1 github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8 github.com/cenkalti/backoff v2.1.1+incompatible // indirect @@ -21,6 +22,7 @@ require ( github.com/go-acme/lego v2.7.2+incompatible github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe github.com/golang/protobuf v1.3.3 // indirect + github.com/google/go-cmp v0.4.0 // indirect github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.1-0.20190318165438-c8c88dbee036 // indirect github.com/google/uuid v1.1.2-0.20190416172445-c2e93f3ae59f @@ -29,7 +31,6 @@ require ( github.com/hexonet/go-sdk v2.2.3+incompatible github.com/jarcoal/httpmock v1.0.4 // indirect github.com/kolo/xmlrpc v0.0.0-20150413191830-0826b98aaa29 // indirect - github.com/kr/pretty v0.1.0 // indirect github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 // indirect github.com/miekg/dns v1.1.27 github.com/mjibson/esc v0.2.0 @@ -39,6 +40,7 @@ require ( github.com/prasmussen/gandi-api v0.0.0-20180224132202-58d3d4205661 github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff + github.com/sergi/go-diff v1.1.0 // indirect github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect github.com/softlayer/softlayer-go v0.0.0-20170804160555-5e1c8cccc730 github.com/stretchr/testify v1.4.0 @@ -49,15 +51,12 @@ require ( github.com/urfave/cli/v2 v2.1.1 github.com/vultr/govultr v0.2.0 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d // indirect - golang.org/x/mod v0.2.0 // indirect golang.org/x/net v0.0.0-20200226121028-0de0cce0169b golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8 // indirect - golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect google.golang.org/api v0.17.0 google.golang.org/appengine v1.6.5 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/ini.v1 v1.42.0 // indirect gopkg.in/ns1/ns1-go.v2 v2.0.0-20170502175150-c563826f4cbe gopkg.in/sourcemap.v1 v1.0.5 // indirect diff --git a/go.sum b/go.sum index 4523a6862..4ebfed1df 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d/go.mod h1:WML6KO github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.29.1 h1:U2vZ5WprhGAMjzb4bKVzl2QecUtZFW2BXVqa5bnd+OY= @@ -100,6 +102,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -212,6 +216,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -275,8 +281,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -292,6 +296,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -331,8 +336,6 @@ golang.org/x/tools v0.0.0-20190624190245-7f2218787638 h1:uIfBkD8gLczr4XDgYpt/qJY golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc h1:AlqcddMxhvJRMNuW1bFwyuNdm+TCkcEGKvw6FVPTuR8= -golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8 h1:2oWBslXKHx1vReVc1bA4mflTOcSxgXz536kSRBhwDds= golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -373,8 +376,8 @@ google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ns1/ns1-go.v2 v2.0.0-20170502175150-c563826f4cbe h1:fuu3vZ8C6O8mk8Ich8YfkDv/Zpnx1HUotQk8JocBcSw= @@ -385,6 +388,7 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/andreyvit/diff/.gitignore b/vendor/github.com/andreyvit/diff/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/vendor/github.com/andreyvit/diff/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/andreyvit/diff/LICENSE b/vendor/github.com/andreyvit/diff/LICENSE new file mode 100644 index 000000000..05b98e1f7 --- /dev/null +++ b/vendor/github.com/andreyvit/diff/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Andrey Tarantsov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/andreyvit/diff/README.md b/vendor/github.com/andreyvit/diff/README.md new file mode 100644 index 000000000..5b52fea8a --- /dev/null +++ b/vendor/github.com/andreyvit/diff/README.md @@ -0,0 +1,28 @@ +# diff + +Quick'n'easy string diffing functions for Golang based on [github.com/sergi/go-diff](https://github.com/sergi/go-diff). Mainly for diffing strings in tests. + +See [the docs on GoDoc](https://godoc.org/github.com/andreyvit/diff). + +Get it: + + go get -u github.com/andreyvit/diff + +Example: + + import ( + "strings" + "testing" + "github.com/andreyvit/diff" + ) + + const expected = ` + ... + ` + + func TestFoo(t *testing.T) { + actual := Foo(...) + if a, e := strings.TrimSpace(actual), strings.TrimSpace(expected); a != e { + t.Errorf("Result not as expected:\n%v", diff.LineDiff(e, a)) + } + } diff --git a/vendor/github.com/andreyvit/diff/diff.go b/vendor/github.com/andreyvit/diff/diff.go new file mode 100644 index 000000000..12953c83a --- /dev/null +++ b/vendor/github.com/andreyvit/diff/diff.go @@ -0,0 +1,128 @@ +package diff + +import ( + "bytes" + "strings" + + "github.com/sergi/go-diff/diffmatchpatch" +) + +func diff(a, b string) []diffmatchpatch.Diff { + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(a, b, true) + if len(diffs) > 2 { + diffs = dmp.DiffCleanupSemantic(diffs) + diffs = dmp.DiffCleanupEfficiency(diffs) + } + return diffs +} + +// CharacterDiff returns an inline diff between the two strings, using (++added++) and (~~deleted~~) markup. +func CharacterDiff(a, b string) string { + return diffsToString(diff(a, b)) +} + +func diffsToString(diffs []diffmatchpatch.Diff) string { + var buff bytes.Buffer + for _, diff := range diffs { + text := diff.Text + switch diff.Type { + case diffmatchpatch.DiffInsert: + buff.WriteString("(++") + buff.WriteString(text) + buff.WriteString("++)") + case diffmatchpatch.DiffDelete: + buff.WriteString("(~~") + buff.WriteString(text) + buff.WriteString("~~)") + case diffmatchpatch.DiffEqual: + buff.WriteString(text) + } + } + return buff.String() +} + +// LineDiff returns a normal linewise diff between the two given strings. +func LineDiff(a, b string) string { + return strings.Join(LineDiffAsLines(a, b), "\n") +} + +// LineDiffAsLines returns the lines of a linewise diff between the two given strings. +func LineDiffAsLines(a, b string) []string { + return diffsToPatchLines(diff(a, b)) +} + +type patchBuilder struct { + output []string + oldLines []string + newLines []string + newLineBuffer bytes.Buffer + oldLineBuffer bytes.Buffer +} + +func (b *patchBuilder) AddCharacters(text string, op diffmatchpatch.Operation) { + if op == diffmatchpatch.DiffInsert || op == diffmatchpatch.DiffEqual { + b.newLineBuffer.WriteString(text) + } + if op == diffmatchpatch.DiffDelete || op == diffmatchpatch.DiffEqual { + b.oldLineBuffer.WriteString(text) + } +} +func (b *patchBuilder) AddNewline(op diffmatchpatch.Operation) { + oldLine := b.oldLineBuffer.String() + newLine := b.newLineBuffer.String() + + if op == diffmatchpatch.DiffEqual && (oldLine == newLine) { + b.FlushChunk() + b.output = append(b.output, " "+newLine) + b.oldLineBuffer.Reset() + b.newLineBuffer.Reset() + } else { + if op == diffmatchpatch.DiffDelete || op == diffmatchpatch.DiffEqual { + b.oldLines = append(b.oldLines, "-"+oldLine) + b.oldLineBuffer.Reset() + } + if op == diffmatchpatch.DiffInsert || op == diffmatchpatch.DiffEqual { + b.newLines = append(b.newLines, "+"+newLine) + b.newLineBuffer.Reset() + } + } +} +func (b *patchBuilder) FlushChunk() { + if b.oldLines != nil { + b.output = append(b.output, b.oldLines...) + b.oldLines = nil + } + if b.newLines != nil { + b.output = append(b.output, b.newLines...) + b.newLines = nil + } +} +func (b *patchBuilder) Flush() { + if b.oldLineBuffer.Len() > 0 && b.newLineBuffer.Len() > 0 { + b.AddNewline(diffmatchpatch.DiffEqual) + } else if b.oldLineBuffer.Len() > 0 { + b.AddNewline(diffmatchpatch.DiffDelete) + } else if b.newLineBuffer.Len() > 0 { + b.AddNewline(diffmatchpatch.DiffInsert) + } + b.FlushChunk() +} + +func diffsToPatchLines(diffs []diffmatchpatch.Diff) []string { + b := new(patchBuilder) + b.output = make([]string, 0, len(diffs)) + + for _, diff := range diffs { + lines := strings.Split(diff.Text, "\n") + for idx, line := range lines { + if idx > 0 { + b.AddNewline(diff.Type) + } + b.AddCharacters(line, diff.Type) + } + } + + b.Flush() + return b.output +} diff --git a/vendor/github.com/andreyvit/diff/doc.go b/vendor/github.com/andreyvit/diff/doc.go new file mode 100644 index 000000000..98e247083 --- /dev/null +++ b/vendor/github.com/andreyvit/diff/doc.go @@ -0,0 +1,2 @@ +// diff provides quick and easy string diffing functions based on github.com/sergi/go-diff, mainly for diffing strings in tests +package diff diff --git a/vendor/github.com/andreyvit/diff/trim.go b/vendor/github.com/andreyvit/diff/trim.go new file mode 100644 index 000000000..87871f99a --- /dev/null +++ b/vendor/github.com/andreyvit/diff/trim.go @@ -0,0 +1,19 @@ +package diff + +import ( + "strings" +) + +// TrimLines applies TrimSpace to each string in the given array. +func TrimLines(input []string) []string { + result := make([]string, 0, len(input)) + for _, el := range input { + result = append(result, strings.TrimSpace(el)) + } + return result +} + +// TrimLinesInString applies TrimSpace to each line in the given string, and returns the new trimmed string. Empty lines are not removed. +func TrimLinesInString(input string) string { + return strings.Join(TrimLines(strings.Split(strings.TrimSpace(input), "\n")), "\n") +} diff --git a/vendor/github.com/sergi/go-diff/AUTHORS b/vendor/github.com/sergi/go-diff/AUTHORS new file mode 100644 index 000000000..2d7bb2bf5 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/AUTHORS @@ -0,0 +1,25 @@ +# This is the official list of go-diff authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Danny Yoo +James Kolb +Jonathan Amsterdam +Markus Zimmermann +Matt Kovars +Örjan Persson +Osman Masood +Robert Carlsen +Rory Flynn +Sergi Mansilla +Shatrugna Sadhu +Shawn Smith +Stas Maksimov +Tor Arvid Lund +Zac Bergquist diff --git a/vendor/github.com/sergi/go-diff/CONTRIBUTORS b/vendor/github.com/sergi/go-diff/CONTRIBUTORS new file mode 100644 index 000000000..369e3d551 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/CONTRIBUTORS @@ -0,0 +1,32 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the go-diff +# repository. +# +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, ACME Inc. employees would be listed here +# but not in AUTHORS, because ACME Inc. would hold the copyright. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Danny Yoo +James Kolb +Jonathan Amsterdam +Markus Zimmermann +Matt Kovars +Örjan Persson +Osman Masood +Robert Carlsen +Rory Flynn +Sergi Mansilla +Shatrugna Sadhu +Shawn Smith +Stas Maksimov +Tor Arvid Lund +Zac Bergquist diff --git a/vendor/github.com/sergi/go-diff/LICENSE b/vendor/github.com/sergi/go-diff/LICENSE new file mode 100644 index 000000000..937942c2b --- /dev/null +++ b/vendor/github.com/sergi/go-diff/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012-2016 The go-diff Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go new file mode 100644 index 000000000..cb25b4375 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go @@ -0,0 +1,1345 @@ +// Copyright (c) 2012-2016 The go-diff authors. All rights reserved. +// https://github.com/sergi/go-diff +// See the included LICENSE file for license details. +// +// go-diff is a Go implementation of Google's Diff, Match, and Patch library +// Original library is Copyright (c) 2006 Google Inc. +// http://code.google.com/p/google-diff-match-patch/ + +package diffmatchpatch + +import ( + "bytes" + "errors" + "fmt" + "html" + "math" + "net/url" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +// Operation defines the operation of a diff item. +type Operation int8 + +//go:generate stringer -type=Operation -trimprefix=Diff + +const ( + // DiffDelete item represents a delete diff. + DiffDelete Operation = -1 + // DiffInsert item represents an insert diff. + DiffInsert Operation = 1 + // DiffEqual item represents an equal diff. + DiffEqual Operation = 0 +) + +// Diff represents one diff operation +type Diff struct { + Type Operation + Text string +} + +// splice removes amount elements from slice at index index, replacing them with elements. +func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff { + if len(elements) == amount { + // Easy case: overwrite the relevant items. + copy(slice[index:], elements) + return slice + } + if len(elements) < amount { + // Fewer new items than old. + // Copy in the new items. + copy(slice[index:], elements) + // Shift the remaining items left. + copy(slice[index+len(elements):], slice[index+amount:]) + // Calculate the new end of the slice. + end := len(slice) - amount + len(elements) + // Zero stranded elements at end so that they can be garbage collected. + tail := slice[end:] + for i := range tail { + tail[i] = Diff{} + } + return slice[:end] + } + // More new items than old. + // Make room in slice for new elements. + // There's probably an even more efficient way to do this, + // but this is simple and clear. + need := len(slice) - amount + len(elements) + for len(slice) < need { + slice = append(slice, Diff{}) + } + // Shift slice elements right to make room for new elements. + copy(slice[index+len(elements):], slice[index+amount:]) + // Copy in new elements. + copy(slice[index:], elements) + return slice +} + +// DiffMain finds the differences between two texts. +// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character. +func (dmp *DiffMatchPatch) DiffMain(text1, text2 string, checklines bool) []Diff { + return dmp.DiffMainRunes([]rune(text1), []rune(text2), checklines) +} + +// DiffMainRunes finds the differences between two rune sequences. +// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character. +func (dmp *DiffMatchPatch) DiffMainRunes(text1, text2 []rune, checklines bool) []Diff { + var deadline time.Time + if dmp.DiffTimeout > 0 { + deadline = time.Now().Add(dmp.DiffTimeout) + } + return dmp.diffMainRunes(text1, text2, checklines, deadline) +} + +func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, deadline time.Time) []Diff { + if runesEqual(text1, text2) { + var diffs []Diff + if len(text1) > 0 { + diffs = append(diffs, Diff{DiffEqual, string(text1)}) + } + return diffs + } + // Trim off common prefix (speedup). + commonlength := commonPrefixLength(text1, text2) + commonprefix := text1[:commonlength] + text1 = text1[commonlength:] + text2 = text2[commonlength:] + + // Trim off common suffix (speedup). + commonlength = commonSuffixLength(text1, text2) + commonsuffix := text1[len(text1)-commonlength:] + text1 = text1[:len(text1)-commonlength] + text2 = text2[:len(text2)-commonlength] + + // Compute the diff on the middle block. + diffs := dmp.diffCompute(text1, text2, checklines, deadline) + + // Restore the prefix and suffix. + if len(commonprefix) != 0 { + diffs = append([]Diff{Diff{DiffEqual, string(commonprefix)}}, diffs...) + } + if len(commonsuffix) != 0 { + diffs = append(diffs, Diff{DiffEqual, string(commonsuffix)}) + } + + return dmp.DiffCleanupMerge(diffs) +} + +// diffCompute finds the differences between two rune slices. Assumes that the texts do not have any common prefix or suffix. +func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, deadline time.Time) []Diff { + diffs := []Diff{} + if len(text1) == 0 { + // Just add some text (speedup). + return append(diffs, Diff{DiffInsert, string(text2)}) + } else if len(text2) == 0 { + // Just delete some text (speedup). + return append(diffs, Diff{DiffDelete, string(text1)}) + } + + var longtext, shorttext []rune + if len(text1) > len(text2) { + longtext = text1 + shorttext = text2 + } else { + longtext = text2 + shorttext = text1 + } + + if i := runesIndex(longtext, shorttext); i != -1 { + op := DiffInsert + // Swap insertions for deletions if diff is reversed. + if len(text1) > len(text2) { + op = DiffDelete + } + // Shorter text is inside the longer text (speedup). + return []Diff{ + Diff{op, string(longtext[:i])}, + Diff{DiffEqual, string(shorttext)}, + Diff{op, string(longtext[i+len(shorttext):])}, + } + } else if len(shorttext) == 1 { + // Single character string. + // After the previous speedup, the character can't be an equality. + return []Diff{ + Diff{DiffDelete, string(text1)}, + Diff{DiffInsert, string(text2)}, + } + // Check to see if the problem can be split in two. + } else if hm := dmp.diffHalfMatch(text1, text2); hm != nil { + // A half-match was found, sort out the return data. + text1A := hm[0] + text1B := hm[1] + text2A := hm[2] + text2B := hm[3] + midCommon := hm[4] + // Send both pairs off for separate processing. + diffsA := dmp.diffMainRunes(text1A, text2A, checklines, deadline) + diffsB := dmp.diffMainRunes(text1B, text2B, checklines, deadline) + // Merge the results. + diffs := diffsA + diffs = append(diffs, Diff{DiffEqual, string(midCommon)}) + diffs = append(diffs, diffsB...) + return diffs + } else if checklines && len(text1) > 100 && len(text2) > 100 { + return dmp.diffLineMode(text1, text2, deadline) + } + return dmp.diffBisect(text1, text2, deadline) +} + +// diffLineMode does a quick line-level diff on both []runes, then rediff the parts for greater accuracy. This speedup can produce non-minimal diffs. +func (dmp *DiffMatchPatch) diffLineMode(text1, text2 []rune, deadline time.Time) []Diff { + // Scan the text on a line-by-line basis first. + text1, text2, linearray := dmp.diffLinesToRunes(text1, text2) + + diffs := dmp.diffMainRunes(text1, text2, false, deadline) + + // Convert the diff back to original text. + diffs = dmp.DiffCharsToLines(diffs, linearray) + // Eliminate freak matches (e.g. blank lines) + diffs = dmp.DiffCleanupSemantic(diffs) + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs = append(diffs, Diff{DiffEqual, ""}) + + pointer := 0 + countDelete := 0 + countInsert := 0 + + // NOTE: Rune slices are slower than using strings in this case. + textDelete := "" + textInsert := "" + + for pointer < len(diffs) { + switch diffs[pointer].Type { + case DiffInsert: + countInsert++ + textInsert += diffs[pointer].Text + case DiffDelete: + countDelete++ + textDelete += diffs[pointer].Text + case DiffEqual: + // Upon reaching an equality, check for prior redundancies. + if countDelete >= 1 && countInsert >= 1 { + // Delete the offending records and add the merged ones. + diffs = splice(diffs, pointer-countDelete-countInsert, + countDelete+countInsert) + + pointer = pointer - countDelete - countInsert + a := dmp.diffMainRunes([]rune(textDelete), []rune(textInsert), false, deadline) + for j := len(a) - 1; j >= 0; j-- { + diffs = splice(diffs, pointer, 0, a[j]) + } + pointer = pointer + len(a) + } + + countInsert = 0 + countDelete = 0 + textDelete = "" + textInsert = "" + } + pointer++ + } + + return diffs[:len(diffs)-1] // Remove the dummy entry at the end. +} + +// DiffBisect finds the 'middle snake' of a diff, split the problem in two and return the recursively constructed diff. +// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character. +// See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. +func (dmp *DiffMatchPatch) DiffBisect(text1, text2 string, deadline time.Time) []Diff { + // Unused in this code, but retained for interface compatibility. + return dmp.diffBisect([]rune(text1), []rune(text2), deadline) +} + +// diffBisect finds the 'middle snake' of a diff, splits the problem in two and returns the recursively constructed diff. +// See Myers's 1986 paper: An O(ND) Difference Algorithm and Its Variations. +func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) []Diff { + // Cache the text lengths to prevent multiple calls. + runes1Len, runes2Len := len(runes1), len(runes2) + + maxD := (runes1Len + runes2Len + 1) / 2 + vOffset := maxD + vLength := 2 * maxD + + v1 := make([]int, vLength) + v2 := make([]int, vLength) + for i := range v1 { + v1[i] = -1 + v2[i] = -1 + } + v1[vOffset+1] = 0 + v2[vOffset+1] = 0 + + delta := runes1Len - runes2Len + // If the total number of characters is odd, then the front path will collide with the reverse path. + front := (delta%2 != 0) + // Offsets for start and end of k loop. Prevents mapping of space beyond the grid. + k1start := 0 + k1end := 0 + k2start := 0 + k2end := 0 + for d := 0; d < maxD; d++ { + // Bail out if deadline is reached. + if !deadline.IsZero() && d%16 == 0 && time.Now().After(deadline) { + break + } + + // Walk the front path one step. + for k1 := -d + k1start; k1 <= d-k1end; k1 += 2 { + k1Offset := vOffset + k1 + var x1 int + + if k1 == -d || (k1 != d && v1[k1Offset-1] < v1[k1Offset+1]) { + x1 = v1[k1Offset+1] + } else { + x1 = v1[k1Offset-1] + 1 + } + + y1 := x1 - k1 + for x1 < runes1Len && y1 < runes2Len { + if runes1[x1] != runes2[y1] { + break + } + x1++ + y1++ + } + v1[k1Offset] = x1 + if x1 > runes1Len { + // Ran off the right of the graph. + k1end += 2 + } else if y1 > runes2Len { + // Ran off the bottom of the graph. + k1start += 2 + } else if front { + k2Offset := vOffset + delta - k1 + if k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] != -1 { + // Mirror x2 onto top-left coordinate system. + x2 := runes1Len - v2[k2Offset] + if x1 >= x2 { + // Overlap detected. + return dmp.diffBisectSplit(runes1, runes2, x1, y1, deadline) + } + } + } + } + // Walk the reverse path one step. + for k2 := -d + k2start; k2 <= d-k2end; k2 += 2 { + k2Offset := vOffset + k2 + var x2 int + if k2 == -d || (k2 != d && v2[k2Offset-1] < v2[k2Offset+1]) { + x2 = v2[k2Offset+1] + } else { + x2 = v2[k2Offset-1] + 1 + } + var y2 = x2 - k2 + for x2 < runes1Len && y2 < runes2Len { + if runes1[runes1Len-x2-1] != runes2[runes2Len-y2-1] { + break + } + x2++ + y2++ + } + v2[k2Offset] = x2 + if x2 > runes1Len { + // Ran off the left of the graph. + k2end += 2 + } else if y2 > runes2Len { + // Ran off the top of the graph. + k2start += 2 + } else if !front { + k1Offset := vOffset + delta - k2 + if k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] != -1 { + x1 := v1[k1Offset] + y1 := vOffset + x1 - k1Offset + // Mirror x2 onto top-left coordinate system. + x2 = runes1Len - x2 + if x1 >= x2 { + // Overlap detected. + return dmp.diffBisectSplit(runes1, runes2, x1, y1, deadline) + } + } + } + } + } + // Diff took too long and hit the deadline or number of diffs equals number of characters, no commonality at all. + return []Diff{ + Diff{DiffDelete, string(runes1)}, + Diff{DiffInsert, string(runes2)}, + } +} + +func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int, + deadline time.Time) []Diff { + runes1a := runes1[:x] + runes2a := runes2[:y] + runes1b := runes1[x:] + runes2b := runes2[y:] + + // Compute both diffs serially. + diffs := dmp.diffMainRunes(runes1a, runes2a, false, deadline) + diffsb := dmp.diffMainRunes(runes1b, runes2b, false, deadline) + + return append(diffs, diffsb...) +} + +// DiffLinesToChars splits two texts into a list of strings, and educes the texts to a string of hashes where each Unicode character represents one line. +// It's slightly faster to call DiffLinesToRunes first, followed by DiffMainRunes. +func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) { + chars1, chars2, lineArray := dmp.DiffLinesToRunes(text1, text2) + return string(chars1), string(chars2), lineArray +} + +// DiffLinesToRunes splits two texts into a list of runes. Each rune represents one line. +func (dmp *DiffMatchPatch) DiffLinesToRunes(text1, text2 string) ([]rune, []rune, []string) { + // '\x00' is a valid character, but various debuggers don't like it. So we'll insert a junk entry to avoid generating a null character. + lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n' + lineHash := map[string]int{} // e.g. lineHash['Hello\n'] == 4 + + chars1 := dmp.diffLinesToRunesMunge(text1, &lineArray, lineHash) + chars2 := dmp.diffLinesToRunesMunge(text2, &lineArray, lineHash) + + return chars1, chars2, lineArray +} + +func (dmp *DiffMatchPatch) diffLinesToRunes(text1, text2 []rune) ([]rune, []rune, []string) { + return dmp.DiffLinesToRunes(string(text1), string(text2)) +} + +// diffLinesToRunesMunge splits a text into an array of strings, and reduces the texts to a []rune where each Unicode character represents one line. +// We use strings instead of []runes as input mainly because you can't use []rune as a map key. +func (dmp *DiffMatchPatch) diffLinesToRunesMunge(text string, lineArray *[]string, lineHash map[string]int) []rune { + // Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect. + lineStart := 0 + lineEnd := -1 + runes := []rune{} + + for lineEnd < len(text)-1 { + lineEnd = indexOf(text, "\n", lineStart) + + if lineEnd == -1 { + lineEnd = len(text) - 1 + } + + line := text[lineStart : lineEnd+1] + lineStart = lineEnd + 1 + lineValue, ok := lineHash[line] + + if ok { + runes = append(runes, rune(lineValue)) + } else { + *lineArray = append(*lineArray, line) + lineHash[line] = len(*lineArray) - 1 + runes = append(runes, rune(len(*lineArray)-1)) + } + } + + return runes +} + +// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of text. +func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff { + hydrated := make([]Diff, 0, len(diffs)) + for _, aDiff := range diffs { + chars := aDiff.Text + text := make([]string, len(chars)) + + for i, r := range chars { + text[i] = lineArray[r] + } + + aDiff.Text = strings.Join(text, "") + hydrated = append(hydrated, aDiff) + } + return hydrated +} + +// DiffCommonPrefix determines the common prefix length of two strings. +func (dmp *DiffMatchPatch) DiffCommonPrefix(text1, text2 string) int { + // Unused in this code, but retained for interface compatibility. + return commonPrefixLength([]rune(text1), []rune(text2)) +} + +// DiffCommonSuffix determines the common suffix length of two strings. +func (dmp *DiffMatchPatch) DiffCommonSuffix(text1, text2 string) int { + // Unused in this code, but retained for interface compatibility. + return commonSuffixLength([]rune(text1), []rune(text2)) +} + +// commonPrefixLength returns the length of the common prefix of two rune slices. +func commonPrefixLength(text1, text2 []rune) int { + // Linear search. See comment in commonSuffixLength. + n := 0 + for ; n < len(text1) && n < len(text2); n++ { + if text1[n] != text2[n] { + return n + } + } + return n +} + +// commonSuffixLength returns the length of the common suffix of two rune slices. +func commonSuffixLength(text1, text2 []rune) int { + // Use linear search rather than the binary search discussed at https://neil.fraser.name/news/2007/10/09/. + // See discussion at https://github.com/sergi/go-diff/issues/54. + i1 := len(text1) + i2 := len(text2) + for n := 0; ; n++ { + i1-- + i2-- + if i1 < 0 || i2 < 0 || text1[i1] != text2[i2] { + return n + } + } +} + +// DiffCommonOverlap determines if the suffix of one string is the prefix of another. +func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int { + // Cache the text lengths to prevent multiple calls. + text1Length := len(text1) + text2Length := len(text2) + // Eliminate the null case. + if text1Length == 0 || text2Length == 0 { + return 0 + } + // Truncate the longer string. + if text1Length > text2Length { + text1 = text1[text1Length-text2Length:] + } else if text1Length < text2Length { + text2 = text2[0:text1Length] + } + textLength := int(math.Min(float64(text1Length), float64(text2Length))) + // Quick check for the worst case. + if text1 == text2 { + return textLength + } + + // Start by looking for a single character match and increase length until no match is found. Performance analysis: http://neil.fraser.name/news/2010/11/04/ + best := 0 + length := 1 + for { + pattern := text1[textLength-length:] + found := strings.Index(text2, pattern) + if found == -1 { + break + } + length += found + if found == 0 || text1[textLength-length:] == text2[0:length] { + best = length + length++ + } + } + + return best +} + +// DiffHalfMatch checks whether the two texts share a substring which is at least half the length of the longer text. This speedup can produce non-minimal diffs. +func (dmp *DiffMatchPatch) DiffHalfMatch(text1, text2 string) []string { + // Unused in this code, but retained for interface compatibility. + runeSlices := dmp.diffHalfMatch([]rune(text1), []rune(text2)) + if runeSlices == nil { + return nil + } + + result := make([]string, len(runeSlices)) + for i, r := range runeSlices { + result[i] = string(r) + } + return result +} + +func (dmp *DiffMatchPatch) diffHalfMatch(text1, text2 []rune) [][]rune { + if dmp.DiffTimeout <= 0 { + // Don't risk returning a non-optimal diff if we have unlimited time. + return nil + } + + var longtext, shorttext []rune + if len(text1) > len(text2) { + longtext = text1 + shorttext = text2 + } else { + longtext = text2 + shorttext = text1 + } + + if len(longtext) < 4 || len(shorttext)*2 < len(longtext) { + return nil // Pointless. + } + + // First check if the second quarter is the seed for a half-match. + hm1 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+3)/4)) + + // Check again based on the third quarter. + hm2 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+1)/2)) + + hm := [][]rune{} + if hm1 == nil && hm2 == nil { + return nil + } else if hm2 == nil { + hm = hm1 + } else if hm1 == nil { + hm = hm2 + } else { + // Both matched. Select the longest. + if len(hm1[4]) > len(hm2[4]) { + hm = hm1 + } else { + hm = hm2 + } + } + + // A half-match was found, sort out the return data. + if len(text1) > len(text2) { + return hm + } + + return [][]rune{hm[2], hm[3], hm[0], hm[1], hm[4]} +} + +// diffHalfMatchI checks if a substring of shorttext exist within longtext such that the substring is at least half the length of longtext? +// Returns a slice containing the prefix of longtext, the suffix of longtext, the prefix of shorttext, the suffix of shorttext and the common middle, or null if there was no match. +func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune { + var bestCommonA []rune + var bestCommonB []rune + var bestCommonLen int + var bestLongtextA []rune + var bestLongtextB []rune + var bestShorttextA []rune + var bestShorttextB []rune + + // Start with a 1/4 length substring at position i as a seed. + seed := l[i : i+len(l)/4] + + for j := runesIndexOf(s, seed, 0); j != -1; j = runesIndexOf(s, seed, j+1) { + prefixLength := commonPrefixLength(l[i:], s[j:]) + suffixLength := commonSuffixLength(l[:i], s[:j]) + + if bestCommonLen < suffixLength+prefixLength { + bestCommonA = s[j-suffixLength : j] + bestCommonB = s[j : j+prefixLength] + bestCommonLen = len(bestCommonA) + len(bestCommonB) + bestLongtextA = l[:i-suffixLength] + bestLongtextB = l[i+prefixLength:] + bestShorttextA = s[:j-suffixLength] + bestShorttextB = s[j+prefixLength:] + } + } + + if bestCommonLen*2 < len(l) { + return nil + } + + return [][]rune{ + bestLongtextA, + bestLongtextB, + bestShorttextA, + bestShorttextB, + append(bestCommonA, bestCommonB...), + } +} + +// DiffCleanupSemantic reduces the number of edits by eliminating semantically trivial equalities. +func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff { + changes := false + // Stack of indices where equalities are found. + equalities := make([]int, 0, len(diffs)) + + var lastequality string + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + var pointer int // Index of current position. + // Number of characters that changed prior to the equality. + var lengthInsertions1, lengthDeletions1 int + // Number of characters that changed after the equality. + var lengthInsertions2, lengthDeletions2 int + + for pointer < len(diffs) { + if diffs[pointer].Type == DiffEqual { + // Equality found. + equalities = append(equalities, pointer) + lengthInsertions1 = lengthInsertions2 + lengthDeletions1 = lengthDeletions2 + lengthInsertions2 = 0 + lengthDeletions2 = 0 + lastequality = diffs[pointer].Text + } else { + // An insertion or deletion. + + if diffs[pointer].Type == DiffInsert { + lengthInsertions2 += len(diffs[pointer].Text) + } else { + lengthDeletions2 += len(diffs[pointer].Text) + } + // Eliminate an equality that is smaller or equal to the edits on both sides of it. + difference1 := int(math.Max(float64(lengthInsertions1), float64(lengthDeletions1))) + difference2 := int(math.Max(float64(lengthInsertions2), float64(lengthDeletions2))) + if len(lastequality) > 0 && + (len(lastequality) <= difference1) && + (len(lastequality) <= difference2) { + // Duplicate record. + insPoint := equalities[len(equalities)-1] + diffs = splice(diffs, insPoint, 0, Diff{DiffDelete, lastequality}) + + // Change second copy to insert. + diffs[insPoint+1].Type = DiffInsert + // Throw away the equality we just deleted. + equalities = equalities[:len(equalities)-1] + + if len(equalities) > 0 { + equalities = equalities[:len(equalities)-1] + } + pointer = -1 + if len(equalities) > 0 { + pointer = equalities[len(equalities)-1] + } + + lengthInsertions1 = 0 // Reset the counters. + lengthDeletions1 = 0 + lengthInsertions2 = 0 + lengthDeletions2 = 0 + lastequality = "" + changes = true + } + } + pointer++ + } + + // Normalize the diff. + if changes { + diffs = dmp.DiffCleanupMerge(diffs) + } + diffs = dmp.DiffCleanupSemanticLossless(diffs) + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1 + for pointer < len(diffs) { + if diffs[pointer-1].Type == DiffDelete && + diffs[pointer].Type == DiffInsert { + deletion := diffs[pointer-1].Text + insertion := diffs[pointer].Text + overlapLength1 := dmp.DiffCommonOverlap(deletion, insertion) + overlapLength2 := dmp.DiffCommonOverlap(insertion, deletion) + if overlapLength1 >= overlapLength2 { + if float64(overlapLength1) >= float64(len(deletion))/2 || + float64(overlapLength1) >= float64(len(insertion))/2 { + + // Overlap found. Insert an equality and trim the surrounding edits. + diffs = splice(diffs, pointer, 0, Diff{DiffEqual, insertion[:overlapLength1]}) + diffs[pointer-1].Text = + deletion[0 : len(deletion)-overlapLength1] + diffs[pointer+1].Text = insertion[overlapLength1:] + pointer++ + } + } else { + if float64(overlapLength2) >= float64(len(deletion))/2 || + float64(overlapLength2) >= float64(len(insertion))/2 { + // Reverse overlap found. Insert an equality and swap and trim the surrounding edits. + overlap := Diff{DiffEqual, deletion[:overlapLength2]} + diffs = splice(diffs, pointer, 0, overlap) + diffs[pointer-1].Type = DiffInsert + diffs[pointer-1].Text = insertion[0 : len(insertion)-overlapLength2] + diffs[pointer+1].Type = DiffDelete + diffs[pointer+1].Text = deletion[overlapLength2:] + pointer++ + } + } + pointer++ + } + pointer++ + } + + return diffs +} + +// Define some regex patterns for matching boundaries. +var ( + nonAlphaNumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]`) + whitespaceRegex = regexp.MustCompile(`\s`) + linebreakRegex = regexp.MustCompile(`[\r\n]`) + blanklineEndRegex = regexp.MustCompile(`\n\r?\n$`) + blanklineStartRegex = regexp.MustCompile(`^\r?\n\r?\n`) +) + +// diffCleanupSemanticScore computes a score representing whether the internal boundary falls on logical boundaries. +// Scores range from 6 (best) to 0 (worst). Closure, but does not reference any external variables. +func diffCleanupSemanticScore(one, two string) int { + if len(one) == 0 || len(two) == 0 { + // Edges are the best. + return 6 + } + + // Each port of this function behaves slightly differently due to subtle differences in each language's definition of things like 'whitespace'. Since this function's purpose is largely cosmetic, the choice has been made to use each language's native features rather than force total conformity. + rune1, _ := utf8.DecodeLastRuneInString(one) + rune2, _ := utf8.DecodeRuneInString(two) + char1 := string(rune1) + char2 := string(rune2) + + nonAlphaNumeric1 := nonAlphaNumericRegex.MatchString(char1) + nonAlphaNumeric2 := nonAlphaNumericRegex.MatchString(char2) + whitespace1 := nonAlphaNumeric1 && whitespaceRegex.MatchString(char1) + whitespace2 := nonAlphaNumeric2 && whitespaceRegex.MatchString(char2) + lineBreak1 := whitespace1 && linebreakRegex.MatchString(char1) + lineBreak2 := whitespace2 && linebreakRegex.MatchString(char2) + blankLine1 := lineBreak1 && blanklineEndRegex.MatchString(one) + blankLine2 := lineBreak2 && blanklineEndRegex.MatchString(two) + + if blankLine1 || blankLine2 { + // Five points for blank lines. + return 5 + } else if lineBreak1 || lineBreak2 { + // Four points for line breaks. + return 4 + } else if nonAlphaNumeric1 && !whitespace1 && whitespace2 { + // Three points for end of sentences. + return 3 + } else if whitespace1 || whitespace2 { + // Two points for whitespace. + return 2 + } else if nonAlphaNumeric1 || nonAlphaNumeric2 { + // One point for non-alphanumeric. + return 1 + } + return 0 +} + +// DiffCleanupSemanticLossless looks for single edits surrounded on both sides by equalities which can be shifted sideways to align the edit to a word boundary. +// E.g: The cat came. -> The cat came. +func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff { + pointer := 1 + + // Intentionally ignore the first and last element (don't need checking). + for pointer < len(diffs)-1 { + if diffs[pointer-1].Type == DiffEqual && + diffs[pointer+1].Type == DiffEqual { + + // This is a single edit surrounded by equalities. + equality1 := diffs[pointer-1].Text + edit := diffs[pointer].Text + equality2 := diffs[pointer+1].Text + + // First, shift the edit as far left as possible. + commonOffset := dmp.DiffCommonSuffix(equality1, edit) + if commonOffset > 0 { + commonString := edit[len(edit)-commonOffset:] + equality1 = equality1[0 : len(equality1)-commonOffset] + edit = commonString + edit[:len(edit)-commonOffset] + equality2 = commonString + equality2 + } + + // Second, step character by character right, looking for the best fit. + bestEquality1 := equality1 + bestEdit := edit + bestEquality2 := equality2 + bestScore := diffCleanupSemanticScore(equality1, edit) + + diffCleanupSemanticScore(edit, equality2) + + for len(edit) != 0 && len(equality2) != 0 { + _, sz := utf8.DecodeRuneInString(edit) + if len(equality2) < sz || edit[:sz] != equality2[:sz] { + break + } + equality1 += edit[:sz] + edit = edit[sz:] + equality2[:sz] + equality2 = equality2[sz:] + score := diffCleanupSemanticScore(equality1, edit) + + diffCleanupSemanticScore(edit, equality2) + // The >= encourages trailing rather than leading whitespace on edits. + if score >= bestScore { + bestScore = score + bestEquality1 = equality1 + bestEdit = edit + bestEquality2 = equality2 + } + } + + if diffs[pointer-1].Text != bestEquality1 { + // We have an improvement, save it back to the diff. + if len(bestEquality1) != 0 { + diffs[pointer-1].Text = bestEquality1 + } else { + diffs = splice(diffs, pointer-1, 1) + pointer-- + } + + diffs[pointer].Text = bestEdit + if len(bestEquality2) != 0 { + diffs[pointer+1].Text = bestEquality2 + } else { + diffs = append(diffs[:pointer+1], diffs[pointer+2:]...) + pointer-- + } + } + } + pointer++ + } + + return diffs +} + +// DiffCleanupEfficiency reduces the number of edits by eliminating operationally trivial equalities. +func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff { + changes := false + // Stack of indices where equalities are found. + type equality struct { + data int + next *equality + } + var equalities *equality + // Always equal to equalities[equalitiesLength-1][1] + lastequality := "" + pointer := 0 // Index of current position. + // Is there an insertion operation before the last equality. + preIns := false + // Is there a deletion operation before the last equality. + preDel := false + // Is there an insertion operation after the last equality. + postIns := false + // Is there a deletion operation after the last equality. + postDel := false + for pointer < len(diffs) { + if diffs[pointer].Type == DiffEqual { // Equality found. + if len(diffs[pointer].Text) < dmp.DiffEditCost && + (postIns || postDel) { + // Candidate found. + equalities = &equality{ + data: pointer, + next: equalities, + } + preIns = postIns + preDel = postDel + lastequality = diffs[pointer].Text + } else { + // Not a candidate, and can never become one. + equalities = nil + lastequality = "" + } + postIns = false + postDel = false + } else { // An insertion or deletion. + if diffs[pointer].Type == DiffDelete { + postDel = true + } else { + postIns = true + } + + // Five types to be split: + // ABXYCD + // AXCD + // ABXC + // AXCD + // ABXC + var sumPres int + if preIns { + sumPres++ + } + if preDel { + sumPres++ + } + if postIns { + sumPres++ + } + if postDel { + sumPres++ + } + if len(lastequality) > 0 && + ((preIns && preDel && postIns && postDel) || + ((len(lastequality) < dmp.DiffEditCost/2) && sumPres == 3)) { + + insPoint := equalities.data + + // Duplicate record. + diffs = splice(diffs, insPoint, 0, Diff{DiffDelete, lastequality}) + + // Change second copy to insert. + diffs[insPoint+1].Type = DiffInsert + // Throw away the equality we just deleted. + equalities = equalities.next + lastequality = "" + + if preIns && preDel { + // No changes made which could affect previous entry, keep going. + postIns = true + postDel = true + equalities = nil + } else { + if equalities != nil { + equalities = equalities.next + } + if equalities != nil { + pointer = equalities.data + } else { + pointer = -1 + } + postIns = false + postDel = false + } + changes = true + } + } + pointer++ + } + + if changes { + diffs = dmp.DiffCleanupMerge(diffs) + } + + return diffs +} + +// DiffCleanupMerge reorders and merges like edit sections. Merge equalities. +// Any edit section can move as long as it doesn't cross an equality. +func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff { + // Add a dummy entry at the end. + diffs = append(diffs, Diff{DiffEqual, ""}) + pointer := 0 + countDelete := 0 + countInsert := 0 + commonlength := 0 + textDelete := []rune(nil) + textInsert := []rune(nil) + + for pointer < len(diffs) { + switch diffs[pointer].Type { + case DiffInsert: + countInsert++ + textInsert = append(textInsert, []rune(diffs[pointer].Text)...) + pointer++ + break + case DiffDelete: + countDelete++ + textDelete = append(textDelete, []rune(diffs[pointer].Text)...) + pointer++ + break + case DiffEqual: + // Upon reaching an equality, check for prior redundancies. + if countDelete+countInsert > 1 { + if countDelete != 0 && countInsert != 0 { + // Factor out any common prefixies. + commonlength = commonPrefixLength(textInsert, textDelete) + if commonlength != 0 { + x := pointer - countDelete - countInsert + if x > 0 && diffs[x-1].Type == DiffEqual { + diffs[x-1].Text += string(textInsert[:commonlength]) + } else { + diffs = append([]Diff{Diff{DiffEqual, string(textInsert[:commonlength])}}, diffs...) + pointer++ + } + textInsert = textInsert[commonlength:] + textDelete = textDelete[commonlength:] + } + // Factor out any common suffixies. + commonlength = commonSuffixLength(textInsert, textDelete) + if commonlength != 0 { + insertIndex := len(textInsert) - commonlength + deleteIndex := len(textDelete) - commonlength + diffs[pointer].Text = string(textInsert[insertIndex:]) + diffs[pointer].Text + textInsert = textInsert[:insertIndex] + textDelete = textDelete[:deleteIndex] + } + } + // Delete the offending records and add the merged ones. + if countDelete == 0 { + diffs = splice(diffs, pointer-countInsert, + countDelete+countInsert, + Diff{DiffInsert, string(textInsert)}) + } else if countInsert == 0 { + diffs = splice(diffs, pointer-countDelete, + countDelete+countInsert, + Diff{DiffDelete, string(textDelete)}) + } else { + diffs = splice(diffs, pointer-countDelete-countInsert, + countDelete+countInsert, + Diff{DiffDelete, string(textDelete)}, + Diff{DiffInsert, string(textInsert)}) + } + + pointer = pointer - countDelete - countInsert + 1 + if countDelete != 0 { + pointer++ + } + if countInsert != 0 { + pointer++ + } + } else if pointer != 0 && diffs[pointer-1].Type == DiffEqual { + // Merge this equality with the previous one. + diffs[pointer-1].Text += diffs[pointer].Text + diffs = append(diffs[:pointer], diffs[pointer+1:]...) + } else { + pointer++ + } + countInsert = 0 + countDelete = 0 + textDelete = nil + textInsert = nil + break + } + } + + if len(diffs[len(diffs)-1].Text) == 0 { + diffs = diffs[0 : len(diffs)-1] // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities which can be shifted sideways to eliminate an equality. E.g: ABAC -> ABAC + changes := false + pointer = 1 + // Intentionally ignore the first and last element (don't need checking). + for pointer < (len(diffs) - 1) { + if diffs[pointer-1].Type == DiffEqual && + diffs[pointer+1].Type == DiffEqual { + // This is a single edit surrounded by equalities. + if strings.HasSuffix(diffs[pointer].Text, diffs[pointer-1].Text) { + // Shift the edit over the previous equality. + diffs[pointer].Text = diffs[pointer-1].Text + + diffs[pointer].Text[:len(diffs[pointer].Text)-len(diffs[pointer-1].Text)] + diffs[pointer+1].Text = diffs[pointer-1].Text + diffs[pointer+1].Text + diffs = splice(diffs, pointer-1, 1) + changes = true + } else if strings.HasPrefix(diffs[pointer].Text, diffs[pointer+1].Text) { + // Shift the edit over the next equality. + diffs[pointer-1].Text += diffs[pointer+1].Text + diffs[pointer].Text = + diffs[pointer].Text[len(diffs[pointer+1].Text):] + diffs[pointer+1].Text + diffs = splice(diffs, pointer+1, 1) + changes = true + } + } + pointer++ + } + + // If shifts were made, the diff needs reordering and another shift sweep. + if changes { + diffs = dmp.DiffCleanupMerge(diffs) + } + + return diffs +} + +// DiffXIndex returns the equivalent location in s2. +func (dmp *DiffMatchPatch) DiffXIndex(diffs []Diff, loc int) int { + chars1 := 0 + chars2 := 0 + lastChars1 := 0 + lastChars2 := 0 + lastDiff := Diff{} + for i := 0; i < len(diffs); i++ { + aDiff := diffs[i] + if aDiff.Type != DiffInsert { + // Equality or deletion. + chars1 += len(aDiff.Text) + } + if aDiff.Type != DiffDelete { + // Equality or insertion. + chars2 += len(aDiff.Text) + } + if chars1 > loc { + // Overshot the location. + lastDiff = aDiff + break + } + lastChars1 = chars1 + lastChars2 = chars2 + } + if lastDiff.Type == DiffDelete { + // The location was deleted. + return lastChars2 + } + // Add the remaining character length. + return lastChars2 + (loc - lastChars1) +} + +// DiffPrettyHtml converts a []Diff into a pretty HTML report. +// It is intended as an example from which to write one's own display functions. +func (dmp *DiffMatchPatch) DiffPrettyHtml(diffs []Diff) string { + var buff bytes.Buffer + for _, diff := range diffs { + text := strings.Replace(html.EscapeString(diff.Text), "\n", "¶
", -1) + switch diff.Type { + case DiffInsert: + _, _ = buff.WriteString("") + _, _ = buff.WriteString(text) + _, _ = buff.WriteString("") + case DiffDelete: + _, _ = buff.WriteString("") + _, _ = buff.WriteString(text) + _, _ = buff.WriteString("") + case DiffEqual: + _, _ = buff.WriteString("") + _, _ = buff.WriteString(text) + _, _ = buff.WriteString("") + } + } + return buff.String() +} + +// DiffPrettyText converts a []Diff into a colored text report. +func (dmp *DiffMatchPatch) DiffPrettyText(diffs []Diff) string { + var buff bytes.Buffer + for _, diff := range diffs { + text := diff.Text + + switch diff.Type { + case DiffInsert: + _, _ = buff.WriteString("\x1b[32m") + _, _ = buff.WriteString(text) + _, _ = buff.WriteString("\x1b[0m") + case DiffDelete: + _, _ = buff.WriteString("\x1b[31m") + _, _ = buff.WriteString(text) + _, _ = buff.WriteString("\x1b[0m") + case DiffEqual: + _, _ = buff.WriteString(text) + } + } + + return buff.String() +} + +// DiffText1 computes and returns the source text (all equalities and deletions). +func (dmp *DiffMatchPatch) DiffText1(diffs []Diff) string { + //StringBuilder text = new StringBuilder() + var text bytes.Buffer + + for _, aDiff := range diffs { + if aDiff.Type != DiffInsert { + _, _ = text.WriteString(aDiff.Text) + } + } + return text.String() +} + +// DiffText2 computes and returns the destination text (all equalities and insertions). +func (dmp *DiffMatchPatch) DiffText2(diffs []Diff) string { + var text bytes.Buffer + + for _, aDiff := range diffs { + if aDiff.Type != DiffDelete { + _, _ = text.WriteString(aDiff.Text) + } + } + return text.String() +} + +// DiffLevenshtein computes the Levenshtein distance that is the number of inserted, deleted or substituted characters. +func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int { + levenshtein := 0 + insertions := 0 + deletions := 0 + + for _, aDiff := range diffs { + switch aDiff.Type { + case DiffInsert: + insertions += utf8.RuneCountInString(aDiff.Text) + case DiffDelete: + deletions += utf8.RuneCountInString(aDiff.Text) + case DiffEqual: + // A deletion and an insertion is one substitution. + levenshtein += max(insertions, deletions) + insertions = 0 + deletions = 0 + } + } + + levenshtein += max(insertions, deletions) + return levenshtein +} + +// DiffToDelta crushes the diff into an encoded string which describes the operations required to transform text1 into text2. +// E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. Operations are tab-separated. Inserted text is escaped using %xx notation. +func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string { + var text bytes.Buffer + for _, aDiff := range diffs { + switch aDiff.Type { + case DiffInsert: + _, _ = text.WriteString("+") + _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1)) + _, _ = text.WriteString("\t") + break + case DiffDelete: + _, _ = text.WriteString("-") + _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text))) + _, _ = text.WriteString("\t") + break + case DiffEqual: + _, _ = text.WriteString("=") + _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text))) + _, _ = text.WriteString("\t") + break + } + } + delta := text.String() + if len(delta) != 0 { + // Strip off trailing tab character. + delta = delta[0 : utf8.RuneCountInString(delta)-1] + delta = unescaper.Replace(delta) + } + return delta +} + +// DiffFromDelta given the original text1, and an encoded string which describes the operations required to transform text1 into text2, comAdde the full diff. +func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Diff, err error) { + i := 0 + runes := []rune(text1) + + for _, token := range strings.Split(delta, "\t") { + if len(token) == 0 { + // Blank tokens are ok (from a trailing \t). + continue + } + + // Each token begins with a one character parameter which specifies the operation of this token (delete, insert, equality). + param := token[1:] + + switch op := token[0]; op { + case '+': + // Decode would Diff all "+" to " " + param = strings.Replace(param, "+", "%2b", -1) + param, err = url.QueryUnescape(param) + if err != nil { + return nil, err + } + if !utf8.ValidString(param) { + return nil, fmt.Errorf("invalid UTF-8 token: %q", param) + } + + diffs = append(diffs, Diff{DiffInsert, param}) + case '=', '-': + n, err := strconv.ParseInt(param, 10, 0) + if err != nil { + return nil, err + } else if n < 0 { + return nil, errors.New("Negative number in DiffFromDelta: " + param) + } + + i += int(n) + // Break out if we are out of bounds, go1.6 can't handle this very well + if i > len(runes) { + break + } + // Remember that string slicing is by byte - we want by rune here. + text := string(runes[i-int(n) : i]) + + if op == '=' { + diffs = append(diffs, Diff{DiffEqual, text}) + } else { + diffs = append(diffs, Diff{DiffDelete, text}) + } + default: + // Anything else is an error. + return nil, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0])) + } + } + + if i != len(runes) { + return nil, fmt.Errorf("Delta length (%v) is different from source text length (%v)", i, len(text1)) + } + + return diffs, nil +} diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go new file mode 100644 index 000000000..d3acc32ce --- /dev/null +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2016 The go-diff authors. All rights reserved. +// https://github.com/sergi/go-diff +// See the included LICENSE file for license details. +// +// go-diff is a Go implementation of Google's Diff, Match, and Patch library +// Original library is Copyright (c) 2006 Google Inc. +// http://code.google.com/p/google-diff-match-patch/ + +// Package diffmatchpatch offers robust algorithms to perform the operations required for synchronizing plain text. +package diffmatchpatch + +import ( + "time" +) + +// DiffMatchPatch holds the configuration for diff-match-patch operations. +type DiffMatchPatch struct { + // Number of seconds to map a diff before giving up (0 for infinity). + DiffTimeout time.Duration + // Cost of an empty edit operation in terms of edit characters. + DiffEditCost int + // How far to search for a match (0 = exact location, 1000+ = broad match). A match this many characters away from the expected location will add 1.0 to the score (0.0 is a perfect match). + MatchDistance int + // When deleting a large block of text (over ~64 characters), how close do the contents have to be to match the expected contents. (0.0 = perfection, 1.0 = very loose). Note that MatchThreshold controls how closely the end points of a delete need to match. + PatchDeleteThreshold float64 + // Chunk size for context length. + PatchMargin int + // The number of bits in an int. + MatchMaxBits int + // At what point is no match declared (0.0 = perfection, 1.0 = very loose). + MatchThreshold float64 +} + +// New creates a new DiffMatchPatch object with default parameters. +func New() *DiffMatchPatch { + // Defaults. + return &DiffMatchPatch{ + DiffTimeout: time.Second, + DiffEditCost: 4, + MatchThreshold: 0.5, + MatchDistance: 1000, + PatchDeleteThreshold: 0.5, + PatchMargin: 4, + MatchMaxBits: 32, + } +} diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go new file mode 100644 index 000000000..17374e109 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go @@ -0,0 +1,160 @@ +// Copyright (c) 2012-2016 The go-diff authors. All rights reserved. +// https://github.com/sergi/go-diff +// See the included LICENSE file for license details. +// +// go-diff is a Go implementation of Google's Diff, Match, and Patch library +// Original library is Copyright (c) 2006 Google Inc. +// http://code.google.com/p/google-diff-match-patch/ + +package diffmatchpatch + +import ( + "math" +) + +// MatchMain locates the best instance of 'pattern' in 'text' near 'loc'. +// Returns -1 if no match found. +func (dmp *DiffMatchPatch) MatchMain(text, pattern string, loc int) int { + // Check for null inputs not needed since null can't be passed in C#. + + loc = int(math.Max(0, math.Min(float64(loc), float64(len(text))))) + if text == pattern { + // Shortcut (potentially not guaranteed by the algorithm) + return 0 + } else if len(text) == 0 { + // Nothing to match. + return -1 + } else if loc+len(pattern) <= len(text) && text[loc:loc+len(pattern)] == pattern { + // Perfect match at the perfect spot! (Includes case of null pattern) + return loc + } + // Do a fuzzy compare. + return dmp.MatchBitap(text, pattern, loc) +} + +// MatchBitap locates the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm. +// Returns -1 if no match was found. +func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int { + // Initialise the alphabet. + s := dmp.MatchAlphabet(pattern) + + // Highest score beyond which we give up. + scoreThreshold := dmp.MatchThreshold + // Is there a nearby exact match? (speedup) + bestLoc := indexOf(text, pattern, loc) + if bestLoc != -1 { + scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc, + pattern), scoreThreshold) + // What about in the other direction? (speedup) + bestLoc = lastIndexOf(text, pattern, loc+len(pattern)) + if bestLoc != -1 { + scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc, + pattern), scoreThreshold) + } + } + + // Initialise the bit arrays. + matchmask := 1 << uint((len(pattern) - 1)) + bestLoc = -1 + + var binMin, binMid int + binMax := len(pattern) + len(text) + lastRd := []int{} + for d := 0; d < len(pattern); d++ { + // Scan for the best match; each iteration allows for one more error. Run a binary search to determine how far from 'loc' we can stray at this error level. + binMin = 0 + binMid = binMax + for binMin < binMid { + if dmp.matchBitapScore(d, loc+binMid, loc, pattern) <= scoreThreshold { + binMin = binMid + } else { + binMax = binMid + } + binMid = (binMax-binMin)/2 + binMin + } + // Use the result from this iteration as the maximum for the next. + binMax = binMid + start := int(math.Max(1, float64(loc-binMid+1))) + finish := int(math.Min(float64(loc+binMid), float64(len(text))) + float64(len(pattern))) + + rd := make([]int, finish+2) + rd[finish+1] = (1 << uint(d)) - 1 + + for j := finish; j >= start; j-- { + var charMatch int + if len(text) <= j-1 { + // Out of range. + charMatch = 0 + } else if _, ok := s[text[j-1]]; !ok { + charMatch = 0 + } else { + charMatch = s[text[j-1]] + } + + if d == 0 { + // First pass: exact match. + rd[j] = ((rd[j+1] << 1) | 1) & charMatch + } else { + // Subsequent passes: fuzzy match. + rd[j] = ((rd[j+1]<<1)|1)&charMatch | (((lastRd[j+1] | lastRd[j]) << 1) | 1) | lastRd[j+1] + } + if (rd[j] & matchmask) != 0 { + score := dmp.matchBitapScore(d, j-1, loc, pattern) + // This match will almost certainly be better than any existing match. But check anyway. + if score <= scoreThreshold { + // Told you so. + scoreThreshold = score + bestLoc = j - 1 + if bestLoc > loc { + // When passing loc, don't exceed our current distance from loc. + start = int(math.Max(1, float64(2*loc-bestLoc))) + } else { + // Already passed loc, downhill from here on in. + break + } + } + } + } + if dmp.matchBitapScore(d+1, loc, loc, pattern) > scoreThreshold { + // No hope for a (better) match at greater error levels. + break + } + lastRd = rd + } + return bestLoc +} + +// matchBitapScore computes and returns the score for a match with e errors and x location. +func (dmp *DiffMatchPatch) matchBitapScore(e, x, loc int, pattern string) float64 { + accuracy := float64(e) / float64(len(pattern)) + proximity := math.Abs(float64(loc - x)) + if dmp.MatchDistance == 0 { + // Dodge divide by zero error. + if proximity == 0 { + return accuracy + } + + return 1.0 + } + return accuracy + (proximity / float64(dmp.MatchDistance)) +} + +// MatchAlphabet initialises the alphabet for the Bitap algorithm. +func (dmp *DiffMatchPatch) MatchAlphabet(pattern string) map[byte]int { + s := map[byte]int{} + charPattern := []byte(pattern) + for _, c := range charPattern { + _, ok := s[c] + if !ok { + s[c] = 0 + } + } + i := 0 + + for _, c := range charPattern { + value := s[c] | int(uint(1)< y { + return x + } + return y +} diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/operation_string.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/operation_string.go new file mode 100644 index 000000000..533ec0da7 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/operation_string.go @@ -0,0 +1,17 @@ +// Code generated by "stringer -type=Operation -trimprefix=Diff"; DO NOT EDIT. + +package diffmatchpatch + +import "fmt" + +const _Operation_name = "DeleteEqualInsert" + +var _Operation_index = [...]uint8{0, 6, 11, 17} + +func (i Operation) String() string { + i -= -1 + if i < 0 || i >= Operation(len(_Operation_index)-1) { + return fmt.Sprintf("Operation(%d)", i+-1) + } + return _Operation_name[_Operation_index[i]:_Operation_index[i+1]] +} diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go new file mode 100644 index 000000000..223c43c42 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go @@ -0,0 +1,556 @@ +// Copyright (c) 2012-2016 The go-diff authors. All rights reserved. +// https://github.com/sergi/go-diff +// See the included LICENSE file for license details. +// +// go-diff is a Go implementation of Google's Diff, Match, and Patch library +// Original library is Copyright (c) 2006 Google Inc. +// http://code.google.com/p/google-diff-match-patch/ + +package diffmatchpatch + +import ( + "bytes" + "errors" + "math" + "net/url" + "regexp" + "strconv" + "strings" +) + +// Patch represents one patch operation. +type Patch struct { + diffs []Diff + Start1 int + Start2 int + Length1 int + Length2 int +} + +// String emulates GNU diff's format. +// Header: @@ -382,8 +481,9 @@ +// Indices are printed as 1-based, not 0-based. +func (p *Patch) String() string { + var coords1, coords2 string + + if p.Length1 == 0 { + coords1 = strconv.Itoa(p.Start1) + ",0" + } else if p.Length1 == 1 { + coords1 = strconv.Itoa(p.Start1 + 1) + } else { + coords1 = strconv.Itoa(p.Start1+1) + "," + strconv.Itoa(p.Length1) + } + + if p.Length2 == 0 { + coords2 = strconv.Itoa(p.Start2) + ",0" + } else if p.Length2 == 1 { + coords2 = strconv.Itoa(p.Start2 + 1) + } else { + coords2 = strconv.Itoa(p.Start2+1) + "," + strconv.Itoa(p.Length2) + } + + var text bytes.Buffer + _, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n") + + // Escape the body of the patch with %xx notation. + for _, aDiff := range p.diffs { + switch aDiff.Type { + case DiffInsert: + _, _ = text.WriteString("+") + case DiffDelete: + _, _ = text.WriteString("-") + case DiffEqual: + _, _ = text.WriteString(" ") + } + + _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1)) + _, _ = text.WriteString("\n") + } + + return unescaper.Replace(text.String()) +} + +// PatchAddContext increases the context until it is unique, but doesn't let the pattern expand beyond MatchMaxBits. +func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch { + if len(text) == 0 { + return patch + } + + pattern := text[patch.Start2 : patch.Start2+patch.Length1] + padding := 0 + + // Look for the first and last matches of pattern in text. If two different matches are found, increase the pattern length. + for strings.Index(text, pattern) != strings.LastIndex(text, pattern) && + len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin { + padding += dmp.PatchMargin + maxStart := max(0, patch.Start2-padding) + minEnd := min(len(text), patch.Start2+patch.Length1+padding) + pattern = text[maxStart:minEnd] + } + // Add one chunk for good luck. + padding += dmp.PatchMargin + + // Add the prefix. + prefix := text[max(0, patch.Start2-padding):patch.Start2] + if len(prefix) != 0 { + patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...) + } + // Add the suffix. + suffix := text[patch.Start2+patch.Length1 : min(len(text), patch.Start2+patch.Length1+padding)] + if len(suffix) != 0 { + patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix}) + } + + // Roll back the start points. + patch.Start1 -= len(prefix) + patch.Start2 -= len(prefix) + // Extend the lengths. + patch.Length1 += len(prefix) + len(suffix) + patch.Length2 += len(prefix) + len(suffix) + + return patch +} + +// PatchMake computes a list of patches. +func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch { + if len(opt) == 1 { + diffs, _ := opt[0].([]Diff) + text1 := dmp.DiffText1(diffs) + return dmp.PatchMake(text1, diffs) + } else if len(opt) == 2 { + text1 := opt[0].(string) + switch t := opt[1].(type) { + case string: + diffs := dmp.DiffMain(text1, t, true) + if len(diffs) > 2 { + diffs = dmp.DiffCleanupSemantic(diffs) + diffs = dmp.DiffCleanupEfficiency(diffs) + } + return dmp.PatchMake(text1, diffs) + case []Diff: + return dmp.patchMake2(text1, t) + } + } else if len(opt) == 3 { + return dmp.PatchMake(opt[0], opt[2]) + } + return []Patch{} +} + +// patchMake2 computes a list of patches to turn text1 into text2. +// text2 is not provided, diffs are the delta between text1 and text2. +func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { + // Check for null inputs not needed since null can't be passed in C#. + patches := []Patch{} + if len(diffs) == 0 { + return patches // Get rid of the null case. + } + + patch := Patch{} + charCount1 := 0 // Number of characters into the text1 string. + charCount2 := 0 // Number of characters into the text2 string. + // Start with text1 (prepatchText) and apply the diffs until we arrive at text2 (postpatchText). We recreate the patches one by one to determine context info. + prepatchText := text1 + postpatchText := text1 + + for i, aDiff := range diffs { + if len(patch.diffs) == 0 && aDiff.Type != DiffEqual { + // A new patch starts here. + patch.Start1 = charCount1 + patch.Start2 = charCount2 + } + + switch aDiff.Type { + case DiffInsert: + patch.diffs = append(patch.diffs, aDiff) + patch.Length2 += len(aDiff.Text) + postpatchText = postpatchText[:charCount2] + + aDiff.Text + postpatchText[charCount2:] + case DiffDelete: + patch.Length1 += len(aDiff.Text) + patch.diffs = append(patch.diffs, aDiff) + postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):] + case DiffEqual: + if len(aDiff.Text) <= 2*dmp.PatchMargin && + len(patch.diffs) != 0 && i != len(diffs)-1 { + // Small equality inside a patch. + patch.diffs = append(patch.diffs, aDiff) + patch.Length1 += len(aDiff.Text) + patch.Length2 += len(aDiff.Text) + } + if len(aDiff.Text) >= 2*dmp.PatchMargin { + // Time for a new patch. + if len(patch.diffs) != 0 { + patch = dmp.PatchAddContext(patch, prepatchText) + patches = append(patches, patch) + patch = Patch{} + // Unlike Unidiff, our patch lists have a rolling context. http://code.google.com/p/google-diff-match-patch/wiki/Unidiff Update prepatch text & pos to reflect the application of the just completed patch. + prepatchText = postpatchText + charCount1 = charCount2 + } + } + } + + // Update the current character count. + if aDiff.Type != DiffInsert { + charCount1 += len(aDiff.Text) + } + if aDiff.Type != DiffDelete { + charCount2 += len(aDiff.Text) + } + } + + // Pick up the leftover patch if not empty. + if len(patch.diffs) != 0 { + patch = dmp.PatchAddContext(patch, prepatchText) + patches = append(patches, patch) + } + + return patches +} + +// PatchDeepCopy returns an array that is identical to a given an array of patches. +func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch { + patchesCopy := []Patch{} + for _, aPatch := range patches { + patchCopy := Patch{} + for _, aDiff := range aPatch.diffs { + patchCopy.diffs = append(patchCopy.diffs, Diff{ + aDiff.Type, + aDiff.Text, + }) + } + patchCopy.Start1 = aPatch.Start1 + patchCopy.Start2 = aPatch.Start2 + patchCopy.Length1 = aPatch.Length1 + patchCopy.Length2 = aPatch.Length2 + patchesCopy = append(patchesCopy, patchCopy) + } + return patchesCopy +} + +// PatchApply merges a set of patches onto the text. Returns a patched text, as well as an array of true/false values indicating which patches were applied. +func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) { + if len(patches) == 0 { + return text, []bool{} + } + + // Deep copy the patches so that no changes are made to originals. + patches = dmp.PatchDeepCopy(patches) + + nullPadding := dmp.PatchAddPadding(patches) + text = nullPadding + text + nullPadding + patches = dmp.PatchSplitMax(patches) + + x := 0 + // delta keeps track of the offset between the expected and actual location of the previous patch. If there are patches expected at positions 10 and 20, but the first patch was found at 12, delta is 2 and the second patch has an effective expected position of 22. + delta := 0 + results := make([]bool, len(patches)) + for _, aPatch := range patches { + expectedLoc := aPatch.Start2 + delta + text1 := dmp.DiffText1(aPatch.diffs) + var startLoc int + endLoc := -1 + if len(text1) > dmp.MatchMaxBits { + // PatchSplitMax will only provide an oversized pattern in the case of a monster delete. + startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc) + if startLoc != -1 { + endLoc = dmp.MatchMain(text, + text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits) + if endLoc == -1 || startLoc >= endLoc { + // Can't find valid trailing context. Drop this patch. + startLoc = -1 + } + } + } else { + startLoc = dmp.MatchMain(text, text1, expectedLoc) + } + if startLoc == -1 { + // No match found. :( + results[x] = false + // Subtract the delta for this failed patch from subsequent patches. + delta -= aPatch.Length2 - aPatch.Length1 + } else { + // Found a match. :) + results[x] = true + delta = startLoc - expectedLoc + var text2 string + if endLoc == -1 { + text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))] + } else { + text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))] + } + if text1 == text2 { + // Perfect match, just shove the Replacement text in. + text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):] + } else { + // Imperfect match. Run a diff to get a framework of equivalent indices. + diffs := dmp.DiffMain(text1, text2, false) + if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold { + // The end points match, but the content is unacceptably bad. + results[x] = false + } else { + diffs = dmp.DiffCleanupSemanticLossless(diffs) + index1 := 0 + for _, aDiff := range aPatch.diffs { + if aDiff.Type != DiffEqual { + index2 := dmp.DiffXIndex(diffs, index1) + if aDiff.Type == DiffInsert { + // Insertion + text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:] + } else if aDiff.Type == DiffDelete { + // Deletion + startIndex := startLoc + index2 + text = text[:startIndex] + + text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:] + } + } + if aDiff.Type != DiffDelete { + index1 += len(aDiff.Text) + } + } + } + } + } + x++ + } + // Strip the padding off. + text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))] + return text, results +} + +// PatchAddPadding adds some padding on text start and end so that edges can match something. +// Intended to be called only from within patchApply. +func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string { + paddingLength := dmp.PatchMargin + nullPadding := "" + for x := 1; x <= paddingLength; x++ { + nullPadding += string(x) + } + + // Bump all the patches forward. + for i := range patches { + patches[i].Start1 += paddingLength + patches[i].Start2 += paddingLength + } + + // Add some padding on start of first diff. + if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual { + // Add nullPadding equality. + patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...) + patches[0].Start1 -= paddingLength // Should be 0. + patches[0].Start2 -= paddingLength // Should be 0. + patches[0].Length1 += paddingLength + patches[0].Length2 += paddingLength + } else if paddingLength > len(patches[0].diffs[0].Text) { + // Grow first equality. + extraLength := paddingLength - len(patches[0].diffs[0].Text) + patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text + patches[0].Start1 -= extraLength + patches[0].Start2 -= extraLength + patches[0].Length1 += extraLength + patches[0].Length2 += extraLength + } + + // Add some padding on end of last diff. + last := len(patches) - 1 + if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual { + // Add nullPadding equality. + patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding}) + patches[last].Length1 += paddingLength + patches[last].Length2 += paddingLength + } else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) { + // Grow last equality. + lastDiff := patches[last].diffs[len(patches[last].diffs)-1] + extraLength := paddingLength - len(lastDiff.Text) + patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength] + patches[last].Length1 += extraLength + patches[last].Length2 += extraLength + } + + return nullPadding +} + +// PatchSplitMax looks through the patches and breaks up any which are longer than the maximum limit of the match algorithm. +// Intended to be called only from within patchApply. +func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch { + patchSize := dmp.MatchMaxBits + for x := 0; x < len(patches); x++ { + if patches[x].Length1 <= patchSize { + continue + } + bigpatch := patches[x] + // Remove the big old patch. + patches = append(patches[:x], patches[x+1:]...) + x-- + + Start1 := bigpatch.Start1 + Start2 := bigpatch.Start2 + precontext := "" + for len(bigpatch.diffs) != 0 { + // Create one of several smaller patches. + patch := Patch{} + empty := true + patch.Start1 = Start1 - len(precontext) + patch.Start2 = Start2 - len(precontext) + if len(precontext) != 0 { + patch.Length1 = len(precontext) + patch.Length2 = len(precontext) + patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext}) + } + for len(bigpatch.diffs) != 0 && patch.Length1 < patchSize-dmp.PatchMargin { + diffType := bigpatch.diffs[0].Type + diffText := bigpatch.diffs[0].Text + if diffType == DiffInsert { + // Insertions are harmless. + patch.Length2 += len(diffText) + Start2 += len(diffText) + patch.diffs = append(patch.diffs, bigpatch.diffs[0]) + bigpatch.diffs = bigpatch.diffs[1:] + empty = false + } else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize { + // This is a large deletion. Let it pass in one chunk. + patch.Length1 += len(diffText) + Start1 += len(diffText) + empty = false + patch.diffs = append(patch.diffs, Diff{diffType, diffText}) + bigpatch.diffs = bigpatch.diffs[1:] + } else { + // Deletion or equality. Only take as much as we can stomach. + diffText = diffText[:min(len(diffText), patchSize-patch.Length1-dmp.PatchMargin)] + + patch.Length1 += len(diffText) + Start1 += len(diffText) + if diffType == DiffEqual { + patch.Length2 += len(diffText) + Start2 += len(diffText) + } else { + empty = false + } + patch.diffs = append(patch.diffs, Diff{diffType, diffText}) + if diffText == bigpatch.diffs[0].Text { + bigpatch.diffs = bigpatch.diffs[1:] + } else { + bigpatch.diffs[0].Text = + bigpatch.diffs[0].Text[len(diffText):] + } + } + } + // Compute the head context for the next patch. + precontext = dmp.DiffText2(patch.diffs) + precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):] + + postcontext := "" + // Append the end context for this patch. + if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin { + postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin] + } else { + postcontext = dmp.DiffText1(bigpatch.diffs) + } + + if len(postcontext) != 0 { + patch.Length1 += len(postcontext) + patch.Length2 += len(postcontext) + if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual { + patch.diffs[len(patch.diffs)-1].Text += postcontext + } else { + patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext}) + } + } + if !empty { + x++ + patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...) + } + } + } + return patches +} + +// PatchToText takes a list of patches and returns a textual representation. +func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string { + var text bytes.Buffer + for _, aPatch := range patches { + _, _ = text.WriteString(aPatch.String()) + } + return text.String() +} + +// PatchFromText parses a textual representation of patches and returns a List of Patch objects. +func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) { + patches := []Patch{} + if len(textline) == 0 { + return patches, nil + } + text := strings.Split(textline, "\n") + textPointer := 0 + patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$") + + var patch Patch + var sign uint8 + var line string + for textPointer < len(text) { + + if !patchHeader.MatchString(text[textPointer]) { + return patches, errors.New("Invalid patch string: " + text[textPointer]) + } + + patch = Patch{} + m := patchHeader.FindStringSubmatch(text[textPointer]) + + patch.Start1, _ = strconv.Atoi(m[1]) + if len(m[2]) == 0 { + patch.Start1-- + patch.Length1 = 1 + } else if m[2] == "0" { + patch.Length1 = 0 + } else { + patch.Start1-- + patch.Length1, _ = strconv.Atoi(m[2]) + } + + patch.Start2, _ = strconv.Atoi(m[3]) + + if len(m[4]) == 0 { + patch.Start2-- + patch.Length2 = 1 + } else if m[4] == "0" { + patch.Length2 = 0 + } else { + patch.Start2-- + patch.Length2, _ = strconv.Atoi(m[4]) + } + textPointer++ + + for textPointer < len(text) { + if len(text[textPointer]) > 0 { + sign = text[textPointer][0] + } else { + textPointer++ + continue + } + + line = text[textPointer][1:] + line = strings.Replace(line, "+", "%2b", -1) + line, _ = url.QueryUnescape(line) + if sign == '-' { + // Deletion. + patch.diffs = append(patch.diffs, Diff{DiffDelete, line}) + } else if sign == '+' { + // Insertion. + patch.diffs = append(patch.diffs, Diff{DiffInsert, line}) + } else if sign == ' ' { + // Minor equality. + patch.diffs = append(patch.diffs, Diff{DiffEqual, line}) + } else if sign == '@' { + // Start of next patch. + break + } else { + // WTF? + return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line)) + } + textPointer++ + } + + patches = append(patches, patch) + } + return patches, nil +} diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go new file mode 100644 index 000000000..265f29cc7 --- /dev/null +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2016 The go-diff authors. All rights reserved. +// https://github.com/sergi/go-diff +// See the included LICENSE file for license details. +// +// go-diff is a Go implementation of Google's Diff, Match, and Patch library +// Original library is Copyright (c) 2006 Google Inc. +// http://code.google.com/p/google-diff-match-patch/ + +package diffmatchpatch + +import ( + "strings" + "unicode/utf8" +) + +// unescaper unescapes selected chars for compatibility with JavaScript's encodeURI. +// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be unescaped. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc. +var unescaper = strings.NewReplacer( + "%21", "!", "%7E", "~", "%27", "'", + "%28", "(", "%29", ")", "%3B", ";", + "%2F", "/", "%3F", "?", "%3A", ":", + "%40", "@", "%26", "&", "%3D", "=", + "%2B", "+", "%24", "$", "%2C", ",", "%23", "#", "%2A", "*") + +// indexOf returns the first index of pattern in str, starting at str[i]. +func indexOf(str string, pattern string, i int) int { + if i > len(str)-1 { + return -1 + } + if i <= 0 { + return strings.Index(str, pattern) + } + ind := strings.Index(str[i:], pattern) + if ind == -1 { + return -1 + } + return ind + i +} + +// lastIndexOf returns the last index of pattern in str, starting at str[i]. +func lastIndexOf(str string, pattern string, i int) int { + if i < 0 { + return -1 + } + if i >= len(str) { + return strings.LastIndex(str, pattern) + } + _, size := utf8.DecodeRuneInString(str[i:]) + return strings.LastIndex(str[:i+size], pattern) +} + +// runesIndexOf returns the index of pattern in target, starting at target[i]. +func runesIndexOf(target, pattern []rune, i int) int { + if i > len(target)-1 { + return -1 + } + if i <= 0 { + return runesIndex(target, pattern) + } + ind := runesIndex(target[i:], pattern) + if ind == -1 { + return -1 + } + return ind + i +} + +func runesEqual(r1, r2 []rune) bool { + if len(r1) != len(r2) { + return false + } + for i, c := range r1 { + if c != r2[i] { + return false + } + } + return true +} + +// runesIndex is the equivalent of strings.Index for rune slices. +func runesIndex(r1, r2 []rune) int { + last := len(r1) - len(r2) + for i := 0; i <= last; i++ { + if runesEqual(r1[i:i+len(r2)], r2) { + return i + } + } + return -1 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 1f4d65db5..a569dc394 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -31,6 +31,9 @@ github.com/DisposaBoy/JsonConfigReader # github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d ## explicit github.com/TomOnTime/utfutil +# github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 +## explicit +github.com/andreyvit/diff # github.com/aws/aws-sdk-go v1.29.1 ## explicit github.com/aws/aws-sdk-go/aws @@ -135,6 +138,8 @@ github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp # github.com/golang/snappy v0.0.1 github.com/golang/snappy +# github.com/google/go-cmp v0.4.0 +## explicit # github.com/google/go-github v17.0.0+incompatible ## explicit github.com/google/go-github/github @@ -204,8 +209,6 @@ github.com/jmespath/go-jmespath # github.com/kolo/xmlrpc v0.0.0-20150413191830-0826b98aaa29 ## explicit github.com/kolo/xmlrpc -# github.com/kr/pretty v0.1.0 -## explicit # github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 ## explicit # github.com/miekg/dns v1.1.27 @@ -265,6 +268,9 @@ github.com/robertkrimen/otto/underscore github.com/russross/blackfriday/v2 # github.com/ryanuber/go-glob v1.0.0 github.com/ryanuber/go-glob +# github.com/sergi/go-diff v1.1.0 +## explicit +github.com/sergi/go-diff/diffmatchpatch # github.com/shurcooL/sanitized_anchor_name v1.0.0 github.com/shurcooL/sanitized_anchor_name # github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a @@ -330,7 +336,6 @@ golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pkcs12 golang.org/x/crypto/pkcs12/internal/rc2 # golang.org/x/mod v0.2.0 -## explicit golang.org/x/mod/module golang.org/x/mod/semver # golang.org/x/net v0.0.0-20200226121028-0de0cce0169b @@ -383,7 +388,6 @@ golang.org/x/tools/internal/gocommand golang.org/x/tools/internal/gopathwalk golang.org/x/tools/internal/imports # golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 -## explicit golang.org/x/xerrors golang.org/x/xerrors/internal # google.golang.org/api v0.17.0 @@ -448,8 +452,6 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -# gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 -## explicit # gopkg.in/ini.v1 v1.42.0 ## explicit gopkg.in/ini.v1