[
  {
    "path": ".codecov.yml",
    "content": "coverage:\n  status:\n    project:\n      default:\n        target: 40%\n        threshold: null\n    patch: false\n    changes: false\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n    groups:\n      all:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "Thanks for you pull request, do note the following:\n\n- If your PR introduces backward incompatible changes it will very likely not be merged.\n\n- We support the last two major Go versions, if your PR uses features from a too new Go version, it\n  will not be merged.\n\n- If this text is not replaced (deleted, or preferably replaced with something sensible), your PR will be closed.\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: \"Code scanning - action\"\n\non:\n  push:\n    branches: [master, ]\n  pull_request:\n    branches: [master]\n  schedule:\n    - cron: '0 23 * * 5'\n\njobs:\n  CodeQL-Build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v3\n      with:\n        fetch-depth: 2\n\n    - run: git checkout HEAD^2\n      if: ${{ github.event_name == 'pull_request' }}\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v2\n\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v2\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v2\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\non: [push, pull_request]\njobs:\n\n  build:\n    name: Build and Test\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        go: [ 1.22.x, 1.23.x, 1.24.x ]\n    steps:\n\n    - name: Set up Go\n      uses: actions/setup-go@v3\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Check out code\n      uses: actions/checkout@v3\n\n    - name: Build\n      run: go build -v ./...\n\n    - name: Test\n      run: go test -v ./...\n"
  },
  {
    "path": ".gitignore",
    "content": "*.6\ntags\ntest.out\na.out\n"
  },
  {
    "path": "AUTHORS",
    "content": "Miek Gieben <miek@miek.nl>\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "* @miekg @tmthrgd\n"
  },
  {
    "path": "CONTRIBUTORS",
    "content": "Alex A. Skinner\nAndrew Tunnell-Jones\nAsk Bjørn Hansen\nDave Cheney\nDusty Wilson\nMarek Majkowski\nPeter van Dijk\nOmri Bahumi\nAlex Sergeyev\nJames Hartig\n"
  },
  {
    "path": "COPYRIGHT",
    "content": "Copyright 2009 The Go Authors. All rights reserved. Use of this source code\nis governed by a BSD-style license that can be found in the LICENSE file.\nExtensions of the original work are copyright (c) 2011 Miek Gieben\n\nCopyright 2011 Miek Gieben. All rights reserved. Use of this source code is\ngoverned by a BSD-style license that can be found in the LICENSE file.\n\nCopyright 2014 CloudFlare. All rights reserved. Use of this source code is\ngoverned by a BSD-style license that can be found in the LICENSE file.\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile.fuzz",
    "content": "# Makefile for fuzzing\n#\n# Use go-fuzz and needs the tools installed.\n# See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/\n#\n# Installing go-fuzz:\n# $ make -f Makefile.fuzz get\n# Installs:\n# * github.com/dvyukov/go-fuzz/go-fuzz\n# * get github.com/dvyukov/go-fuzz/go-fuzz-build\n\nall: build\n\n.PHONY: build\nbuild:\n\tgo-fuzz-build -tags fuzz github.com/miekg/dns\n\n.PHONY: build-newrr\nbuild-newrr:\n\tgo-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns\n\n.PHONY: fuzz\nfuzz:\n\tgo-fuzz -bin=dns-fuzz.zip -workdir=fuzz\n\n.PHONY: get\nget:\n\tgo get github.com/dvyukov/go-fuzz/go-fuzz\n\tgo get github.com/dvyukov/go-fuzz/go-fuzz-build\n\n.PHONY: clean\nclean:\n\trm *-fuzz.zip\n"
  },
  {
    "path": "Makefile.release",
    "content": "# Makefile for releasing.\n#\n# The release is controlled from version.go. The version found there is\n# used to tag the git repo, we're not building any artifacts so there is nothing\n# to upload to github.\n#\n# * Up the version in version.go\n# * Run: make -f Makefile.release release\n#   * will *commit* your change with 'Release $VERSION'\n#   * push to github\n#\n\ndefine GO\n//+build ignore\n\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/miekg/dns\"\n)\n\nfunc main() {\n\tfmt.Println(dns.Version.String())\n}\nendef\n\n$(file > version_release.go,$(GO))\nVERSION:=$(shell go run version_release.go)\nTAG=\"v$(VERSION)\"\n\nall:\n\t@echo Use the \\'release\\' target to start a release $(VERSION)\n\trm -f version_release.go\n\n.PHONY: release\nrelease: commit push\n\t@echo Released $(VERSION)\n\trm -f version_release.go\n\n.PHONY: commit\ncommit:\n\t@echo Committing release $(VERSION)\n\tgit commit -am\"Release $(VERSION)\"\n\tgit tag $(TAG)\n\n.PHONY: push\npush:\n\t@echo Pushing release $(VERSION) to master\n\tgit push --tags\n\tgit push\n"
  },
  {
    "path": "README.md",
    "content": "[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns)\n[![Code Coverage](https://img.shields.io/codecov/c/github/miekg/dns/master.svg)](https://codecov.io/github/miekg/dns?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns)\n[![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns)\n\nDNS version 2 is now available at <https://codeberg.org/miekg/dns>. This version should be 2x faster across\nthe board. Further optimizations welcome.\n\nThe version here will only see specific fixes and nothing else. At some point this repo will be archived.\n\n# Alternative (more granular) approach to a DNS library\n\n> Less is more.\n\nComplete and usable DNS library. All Resource Records are supported, including the DNSSEC types.\nIt follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there\nisn't a convenience function for it. Server side and client side programming is supported, i.e. you\ncan build servers and resolvers with it.\n\nWe try to keep the \"master\" branch as sane as possible and at the bleeding edge of standards,\navoiding breaking changes wherever reasonable. We support the last two versions of Go.\n\n# Goals\n\n- KISS;\n- Fast;\n- Small API. If it's easy to code in Go, don't make a function for it.\n\n# Users\n\nA not-so-up-to-date-list-that-may-be-actually-current:\n\n- https://github.com/coredns/coredns\n- https://github.com/abh/geodns\n- https://github.com/baidu/bfe\n- http://www.statdns.com/\n- http://www.dnsinspect.com/\n- https://github.com/chuangbo/jianbing-dictionary-dns\n- http://www.dns-lg.com/\n- https://github.com/fcambus/rrda\n- https://github.com/kenshinx/godns\n- https://github.com/skynetservices/skydns\n- https://github.com/hashicorp/consul\n- https://github.com/DevelopersPL/godnsagent\n- https://github.com/duedil-ltd/discodns\n- https://github.com/StalkR/dns-reverse-proxy\n- https://github.com/tianon/rawdns\n- https://mesosphere.github.io/mesos-dns/\n- https://github.com/fcambus/statzone\n- https://github.com/benschw/dns-clb-go\n- https://github.com/corny/dnscheck for <http://public-dns.info/>\n- https://github.com/miekg/unbound\n- https://github.com/miekg/exdns\n- https://dnslookup.org\n- https://github.com/looterz/grimd\n- https://github.com/phamhongviet/serf-dns\n- https://github.com/mehrdadrad/mylg\n- https://github.com/bamarni/dockness\n- https://github.com/fffaraz/microdns\n- https://github.com/ipdcode/hades <https://jd.com>\n- https://github.com/StackExchange/dnscontrol/\n- https://www.dnsperf.com/\n- https://dnssectest.net/\n- https://github.com/oif/apex\n- https://github.com/jedisct1/dnscrypt-proxy (migrated to v2)\n- https://github.com/jedisct1/rpdns\n- https://github.com/xor-gate/sshfp\n- https://github.com/rs/dnstrace\n- https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss))\n- https://render.com\n- https://github.com/peterzen/goresolver\n- https://github.com/folbricht/routedns\n- https://domainr.com/\n- https://zonedb.org/\n- https://router7.org/\n- https://github.com/fortio/dnsping\n- https://github.com/Luzilla/dnsbl_exporter\n- https://github.com/bodgit/tsig\n- https://github.com/v2fly/v2ray-core (test only)\n- https://kuma.io/\n- https://www.misaka.io/services/dns\n- https://ping.sx/dig\n- https://fleetdeck.io/\n- https://github.com/markdingo/autoreverse\n- https://github.com/slackhq/nebula\n- https://addr.tools/\n- https://dnscheck.tools/\n- https://github.com/egbakou/domainverifier\n- https://github.com/semihalev/sdns\n- https://github.com/wintbiit/NineDNS\n- https://linuxcontainers.org/incus/\n- https://ifconfig.es\n- https://github.com/zmap/zdns\n- https://framagit.org/bortzmeyer/check-soa\n- https://github.com/jkerdreux-imt/owns\n\nSend pull request if you want to be listed here.\n\n# Features\n\n- UDP/TCP queries, IPv4 and IPv6\n- RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported\n- Fast\n- Server side programming (mimicking the net/http package)\n- Client side programming\n- DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519\n- EDNS0, NSID, Cookies\n- AXFR/IXFR\n- TSIG, SIG(0)\n- DNS over TLS (DoT): encrypted connection between client and server over TCP\n- DNS name compression\n\nHave fun!\n\nMiek Gieben - 2010-2012 - <miek@miek.nl>\nDNS Authors 2012-\n\n# Building\n\nThis library uses Go modules and uses semantic versioning. Building is done with the `go` tool, so\nthe following should work:\n\n    go get github.com/miekg/dns\n    go build github.com/miekg/dns\n\n## Examples\n\nA short \"how to use the API\" is at the beginning of doc.go (this also will show when you call `godoc\ngithub.com/miekg/dns`).\n\nExample programs can be found in the `github.com/miekg/exdns` repository.\n\n## Supported RFCs\n\n_all of them_\n\n- 103{4,5} - DNS standard\n- 1183 - ISDN, X25 and other deprecated records\n- 1348 - NSAP record (removed the record)\n- 1982 - Serial Arithmetic\n- 1876 - LOC record\n- 1995 - IXFR\n- 1996 - DNS notify\n- 2136 - DNS Update (dynamic updates)\n- 2181 - RRset definition - there is no RRset type though, just []RR\n- 2537 - RSAMD5 DNS keys\n- 2065 - DNSSEC (updated in later RFCs)\n- 2671 - EDNS record\n- 2782 - SRV record\n- 2845 - TSIG record\n- 2915 - NAPTR record\n- 2929 - DNS IANA Considerations\n- 3110 - RSASHA1 DNS keys\n- 3123 - APL record\n- 3225 - DO bit (DNSSEC OK)\n- 340{1,2,3} - NAPTR record\n- 3445 - Limiting the scope of (DNS)KEY\n- 3596 - AAAA record\n- 3597 - Unknown RRs\n- 4025 - A Method for Storing IPsec Keying Material in DNS\n- 403{3,4,5} - DNSSEC + validation functions\n- 4255 - SSHFP record\n- 4343 - Case insensitivity\n- 4408 - SPF record\n- 4509 - SHA256 Hash in DS\n- 4592 - Wildcards in the DNS\n- 4635 - HMAC SHA TSIG\n- 4701 - DHCID\n- 4892 - id.server\n- 5001 - NSID\n- 5155 - NSEC3 record\n- 5205 - HIP record\n- 5702 - SHA2 in the DNS\n- 5936 - AXFR\n- 5966 - TCP implementation recommendations\n- 6605 - ECDSA\n- 6725 - IANA Registry Update\n- 6742 - ILNP DNS\n- 6840 - Clarifications and Implementation Notes for DNS Security\n- 6844 - CAA record\n- 6891 - EDNS0 update\n- 6895 - DNS IANA considerations\n- 6944 - DNSSEC DNSKEY Algorithm Status\n- 6975 - Algorithm Understanding in DNSSEC\n- 7043 - EUI48/EUI64 records\n- 7314 - DNS (EDNS) EXPIRE Option\n- 7477 - CSYNC RR\n- 7828 - edns-tcp-keepalive EDNS0 Option\n- 7553 - URI record\n- 7858 - DNS over TLS: Initiation and Performance Considerations\n- 7871 - EDNS0 Client Subnet\n- 7873 - Domain Name System (DNS) Cookies\n- 8080 - EdDSA for DNSSEC\n- 8490 - DNS Stateful Operations\n- 8499 - DNS Terminology\n- 8659 - DNS Certification Authority Authorization (CAA) Resource Record\n- 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery\n- 8914 - Extended DNS Errors\n- 8976 - Message Digest for DNS Zones (ZONEMD RR)\n- 9460 - Service Binding and Parameter Specification via the DNS\n- 9461 - Service Binding Mapping for DNS Servers\n- 9462 - Discovery of Designated Resolvers\n- 9460 - SVCB and HTTPS Records\n- 9567 - DNS Error Reporting\n- 9606 - DNS Resolver Information\n- 9660 - DNS Zone Version (ZONEVERSION) Option\n- Draft - Compact Denial of Existence in DNSSEC\n\n## Loosely Based Upon\n\n- ldns - <https://nlnetlabs.nl/projects/ldns/about/>\n- NSD - <https://nlnetlabs.nl/projects/nsd/about/>\n- Net::DNS - <http://www.net-dns.org/>\n- GRONG - <https://github.com/bortzmeyer/grong>\n"
  },
  {
    "path": "acceptfunc.go",
    "content": "package dns\n\n// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError.\n// It returns a MsgAcceptAction to indicate what should happen with the message.\ntype MsgAcceptFunc func(dh Header) MsgAcceptAction\n\n// DefaultMsgAcceptFunc checks the request and will reject if:\n//\n// * isn't a request (don't respond in that case)\n//\n// * opcode isn't OpcodeQuery or OpcodeNotify\n//\n// * does not have exactly 1 question in the question section\n//\n// * has more than 1 RR in the Answer section\n//\n// * has more than 0 RRs in the Authority section\n//\n// * has more than 2 RRs in the Additional section\nvar DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc\n\n// MsgAcceptAction represents the action to be taken.\ntype MsgAcceptAction int\n\n// Allowed returned values from a MsgAcceptFunc.\nconst (\n\tMsgAccept               MsgAcceptAction = iota // Accept the message\n\tMsgReject                                      // Reject the message with a RcodeFormatError\n\tMsgIgnore                                      // Ignore the error and send nothing back.\n\tMsgRejectNotImplemented                        // Reject the message with a RcodeNotImplemented\n)\n\nfunc defaultMsgAcceptFunc(dh Header) MsgAcceptAction {\n\tif isResponse := dh.Bits&_QR != 0; isResponse {\n\t\treturn MsgIgnore\n\t}\n\n\t// Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs.\n\topcode := int(dh.Bits>>11) & 0xF\n\tif opcode != OpcodeQuery && opcode != OpcodeNotify {\n\t\treturn MsgRejectNotImplemented\n\t}\n\n\tif dh.Qdcount != 1 {\n\t\treturn MsgReject\n\t}\n\t// NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11.\n\tif dh.Ancount > 1 {\n\t\treturn MsgReject\n\t}\n\t// IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3.\n\tif dh.Nscount > 1 {\n\t\treturn MsgReject\n\t}\n\tif dh.Arcount > 2 {\n\t\treturn MsgReject\n\t}\n\treturn MsgAccept\n}\n"
  },
  {
    "path": "acceptfunc_test.go",
    "content": "package dns\n\nimport (\n\t\"encoding/binary\"\n\t\"net\"\n\t\"testing\"\n)\n\nfunc TestAcceptNotify(t *testing.T) {\n\tHandleFunc(\"example.org.\", handleNotify)\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetNotify(\"example.org.\")\n\t// Set a SOA hint in the answer section, this is allowed according to RFC 1996.\n\tsoa, _ := NewRR(\"example.org. IN SOA sns.dns.icann.org. noc.dns.icann.org. 2018112827 7200 3600 1209600 3600\")\n\tm.Answer = []RR{soa}\n\n\tc := new(Client)\n\tresp, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n\tif resp.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"expected %s, got %s\", RcodeToString[RcodeSuccess], RcodeToString[resp.Rcode])\n\t}\n}\n\nfunc handleNotify(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\tw.WriteMsg(m)\n}\n\nfunc TestInvalidMsg(t *testing.T) {\n\tHandleFunc(\"example.org.\", func(ResponseWriter, *Msg) {\n\t\tt.Fatal(\"the handler must not be called in any of these tests\")\n\t})\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\ts.MsgAcceptFunc = func(dh Header) MsgAcceptAction {\n\t\tswitch dh.Id {\n\t\tcase 0x0001:\n\t\t\treturn MsgAccept\n\t\tcase 0x0002:\n\t\t\treturn MsgReject\n\t\tcase 0x0003:\n\t\t\treturn MsgIgnore\n\t\tcase 0x0004:\n\t\t\treturn MsgRejectNotImplemented\n\t\tdefault:\n\t\t\tt.Errorf(\"unexpected ID %x\", dh.Id)\n\t\t\treturn -1\n\t\t}\n\t}\n\n\tinvalidErrors := make(chan error)\n\ts.MsgInvalidFunc = func(m []byte, err error) {\n\t\tinvalidErrors <- err\n\t}\n\n\tc, err := net.Dial(\"tcp\", addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"cannot connect to test server: %v\", err)\n\t}\n\n\twrite := func(m []byte) {\n\t\tvar length [2]byte\n\t\tbinary.BigEndian.PutUint16(length[:], uint16(len(m)))\n\t\t_, err := c.Write(length[:])\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"length write failed: %v\", err)\n\t\t}\n\t\t_, err = c.Write(m)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"content write failed: %v\", err)\n\t\t}\n\t}\n\n\t/* Message is too short, so there is no header to accept or reject. */\n\n\ttooShortMessage := make([]byte, 11)\n\ttooShortMessage[1] = 0x3 // ID = 3, would be ignored if it were parsable.\n\n\twrite(tooShortMessage)\n\t// Expect an error to be reported.\n\t<-invalidErrors\n\n\t/* Message is accepted but is actually invalid. */\n\n\tbadMessage := make([]byte, 13)\n\tbadMessage[1] = 0x1 // ID = 1, Accept.\n\tbadMessage[5] = 1   // QDCOUNT = 1\n\tbadMessage[12] = 99 // Bad question section.  Invalid!\n\n\twrite(badMessage)\n\t// Expect an error to be reported.\n\t<-invalidErrors\n\n\t/* Message is rejected before it can be determined to be invalid. */\n\n\tclose(invalidErrors) // A call to InvalidMsgFunc would panic due to the closed chan.\n\n\tbadMessage[1] = 0x2 // ID = 2, Reject\n\twrite(badMessage)\n\n\tbadMessage[1] = 0x3 // ID = 3, Ignore\n\twrite(badMessage)\n\n\tbadMessage[1] = 0x4 // ID = 4, RejectNotImplemented\n\twrite(badMessage)\n}\n"
  },
  {
    "path": "client.go",
    "content": "package dns\n\n// A client implementation.\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst (\n\tdnsTimeout     time.Duration = 2 * time.Second\n\ttcpIdleTimeout time.Duration = 8 * time.Second\n)\n\nfunc isPacketConn(c net.Conn) bool {\n\tif _, ok := c.(net.PacketConn); !ok {\n\t\treturn false\n\t}\n\n\tif ua, ok := c.LocalAddr().(*net.UnixAddr); ok {\n\t\treturn ua.Net == \"unixgram\" || ua.Net == \"unixpacket\"\n\t}\n\n\treturn true\n}\n\n// A Conn represents a connection to a DNS server.\ntype Conn struct {\n\tnet.Conn                         // a net.Conn holding the connection\n\tUDPSize        uint16            // minimum receive buffer for UDP messages\n\tTsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)\n\tTsigProvider   TsigProvider      // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.\n\ttsigRequestMAC string\n}\n\nfunc (co *Conn) tsigProvider() TsigProvider {\n\tif co.TsigProvider != nil {\n\t\treturn co.TsigProvider\n\t}\n\t// tsigSecretProvider will return ErrSecret if co.TsigSecret is nil.\n\treturn tsigSecretProvider(co.TsigSecret)\n}\n\n// A Client defines parameters for a DNS client.\ntype Client struct {\n\tNet       string      // if \"tcp\" or \"tcp-tls\" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is \"\" for UDP)\n\tUDPSize   uint16      // minimum receive buffer for UDP messages\n\tTLSConfig *tls.Config // TLS connection configuration\n\tDialer    *net.Dialer // a net.Dialer used to set local address, timeouts and more\n\t// Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,\n\t// WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and\n\t// Client.Dialer) or context.Context.Deadline (see ExchangeContext)\n\tTimeout      time.Duration\n\tDialTimeout  time.Duration     // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero\n\tReadTimeout  time.Duration     // net.Conn.SetReadDeadline value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero\n\tWriteTimeout time.Duration     // net.Conn.SetWriteDeadline value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero\n\tTsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)\n\tTsigProvider TsigProvider      // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.\n\n\t// SingleInflight previously serialised multiple concurrent queries for the\n\t// same Qname, Qtype and Qclass to ensure only one would be in flight at a\n\t// time.\n\t//\n\t// Deprecated: This is a no-op. Callers should implement their own in flight\n\t// query caching if needed. See github.com/miekg/dns/issues/1449.\n\tSingleInflight bool\n}\n\n// Exchange performs a synchronous UDP query. It sends the message m to the address\n// contained in a and waits for a reply. Exchange does not retry a failed query, nor\n// will it fall back to TCP in case of truncation.\n// See client.Exchange for more information on setting larger buffer sizes.\nfunc Exchange(m *Msg, a string) (r *Msg, err error) {\n\tclient := Client{Net: \"udp\"}\n\tr, _, err = client.Exchange(m, a)\n\treturn r, err\n}\n\nfunc (c *Client) dialTimeout() time.Duration {\n\tif c.Timeout != 0 {\n\t\treturn c.Timeout\n\t}\n\tif c.DialTimeout != 0 {\n\t\treturn c.DialTimeout\n\t}\n\treturn dnsTimeout\n}\n\nfunc (c *Client) readTimeout() time.Duration {\n\tif c.Timeout != 0 {\n\t\treturn c.Timeout\n\t}\n\tif c.ReadTimeout != 0 {\n\t\treturn c.ReadTimeout\n\t}\n\treturn dnsTimeout\n}\n\nfunc (c *Client) writeTimeout() time.Duration {\n\tif c.Timeout != 0 {\n\t\treturn c.Timeout\n\t}\n\tif c.WriteTimeout != 0 {\n\t\treturn c.WriteTimeout\n\t}\n\treturn dnsTimeout\n}\n\n// Dial connects to the address on the named network.\nfunc (c *Client) Dial(address string) (conn *Conn, err error) {\n\treturn c.DialContext(context.Background(), address)\n}\n\n// DialContext connects to the address on the named network, with a context.Context.\nfunc (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, err error) {\n\t// create a new dialer with the appropriate timeout\n\tvar d net.Dialer\n\tif c.Dialer == nil {\n\t\td = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())}\n\t} else {\n\t\td = *c.Dialer\n\t}\n\n\tnetwork := c.Net\n\tif network == \"\" {\n\t\tnetwork = \"udp\"\n\t}\n\n\tuseTLS := strings.HasPrefix(network, \"tcp\") && strings.HasSuffix(network, \"-tls\")\n\n\tconn = new(Conn)\n\tif useTLS {\n\t\tnetwork = strings.TrimSuffix(network, \"-tls\")\n\n\t\ttlsDialer := tls.Dialer{\n\t\t\tNetDialer: &d,\n\t\t\tConfig:    c.TLSConfig,\n\t\t}\n\t\tconn.Conn, err = tlsDialer.DialContext(ctx, network, address)\n\t} else {\n\t\tconn.Conn, err = d.DialContext(ctx, network, address)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn.UDPSize = c.UDPSize\n\treturn conn, nil\n}\n\n// Exchange performs a synchronous query. It sends the message m to the address\n// contained in a and waits for a reply. Basic use pattern with a *dns.Client:\n//\n//\tc := new(dns.Client)\n//\tin, rtt, err := c.Exchange(message, \"127.0.0.1:53\")\n//\n// Exchange does not retry a failed query, nor will it fall back to TCP in\n// case of truncation.\n// It is up to the caller to create a message that allows for larger responses to be\n// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger\n// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit\n// of 512 bytes\n// To specify a local address or a timeout, the caller has to set the `Client.Dialer`\n// attribute appropriately\nfunc (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) {\n\tco, err := c.Dial(address)\n\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\tdefer co.Close()\n\treturn c.ExchangeWithConn(m, co)\n}\n\n// ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection\n// that will be used instead of creating a new one.\n// Usage pattern with a *dns.Client:\n//\n//\tc := new(dns.Client)\n//\t// connection management logic goes here\n//\n//\tconn := c.Dial(address)\n//\tin, rtt, err := c.ExchangeWithConn(message, conn)\n//\n// This allows users of the library to implement their own connection management,\n// as opposed to Exchange, which will always use new connections and incur the added overhead\n// that entails when using \"tcp\" and especially \"tcp-tls\" clients.\nfunc (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) {\n\treturn c.ExchangeWithConnContext(context.Background(), m, conn)\n}\n\n// ExchangeWithConnContext has the same behaviour as ExchangeWithConn and\n// additionally obeys deadlines from the passed Context.\nfunc (c *Client) ExchangeWithConnContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {\n\topt := m.IsEdns0()\n\t// If EDNS0 is used use that for size.\n\tif opt != nil && opt.UDPSize() >= MinMsgSize {\n\t\tco.UDPSize = opt.UDPSize()\n\t}\n\t// Otherwise use the client's configured UDP size.\n\tif opt == nil && c.UDPSize >= MinMsgSize {\n\t\tco.UDPSize = c.UDPSize\n\t}\n\n\t// write with the appropriate write timeout\n\tt := time.Now()\n\twriteDeadline := t.Add(c.getTimeoutForRequest(c.writeTimeout()))\n\treadDeadline := t.Add(c.getTimeoutForRequest(c.readTimeout()))\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tif deadline.Before(writeDeadline) {\n\t\t\twriteDeadline = deadline\n\t\t}\n\t\tif deadline.Before(readDeadline) {\n\t\t\treadDeadline = deadline\n\t\t}\n\t}\n\tco.SetWriteDeadline(writeDeadline)\n\tco.SetReadDeadline(readDeadline)\n\n\tco.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider\n\n\tif err = co.WriteMsg(m); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tif isPacketConn(co.Conn) {\n\t\tfor {\n\t\t\tr, err = co.ReadMsg()\n\t\t\t// Ignore replies with mismatched IDs because they might be\n\t\t\t// responses to earlier queries that timed out.\n\t\t\tif err != nil || r.Id == m.Id {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t} else {\n\t\tr, err = co.ReadMsg()\n\t\tif err == nil && r.Id != m.Id {\n\t\t\terr = ErrId\n\t\t}\n\t}\n\trtt = time.Since(t)\n\treturn r, rtt, err\n}\n\n// ReadMsg reads a message from the connection co.\n// If the received message contains a TSIG record the transaction signature\n// is verified. This method always tries to return the message, however if an\n// error is returned there are no guarantees that the returned message is a\n// valid representation of the packet read.\nfunc (co *Conn) ReadMsg() (*Msg, error) {\n\tp, err := co.ReadMsgHeader(nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tm := new(Msg)\n\tif err := m.Unpack(p); err != nil {\n\t\t// If an error was returned, we still want to allow the user to use\n\t\t// the message, but naively they can just check err if they don't want\n\t\t// to use an erroneous message\n\t\treturn m, err\n\t}\n\tif t := m.IsTsig(); t != nil {\n\t\t// Need to work on the original message p, as that was used to calculate the tsig.\n\t\terr = TsigVerifyWithProvider(p, co.tsigProvider(), co.tsigRequestMAC, false)\n\t}\n\treturn m, err\n}\n\n// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).\n// Returns message as a byte slice to be parsed with Msg.Unpack later on.\n// Note that error handling on the message body is not possible as only the header is parsed.\nfunc (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {\n\tvar (\n\t\tp   []byte\n\t\tn   int\n\t\terr error\n\t)\n\n\tif isPacketConn(co.Conn) {\n\t\tif co.UDPSize > MinMsgSize {\n\t\t\tp = make([]byte, co.UDPSize)\n\t\t} else {\n\t\t\tp = make([]byte, MinMsgSize)\n\t\t}\n\t\tn, err = co.Read(p)\n\t} else {\n\t\tvar length uint16\n\t\tif err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tp = make([]byte, length)\n\t\tn, err = io.ReadFull(co.Conn, p)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t} else if n < headerSize {\n\t\treturn nil, ErrShortRead\n\t}\n\n\tp = p[:n]\n\tif hdr != nil {\n\t\tdh, _, err := unpackMsgHdr(p, 0)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\t*hdr = dh\n\t}\n\treturn p, err\n}\n\n// Read implements the net.Conn read method.\nfunc (co *Conn) Read(p []byte) (n int, err error) {\n\tif co.Conn == nil {\n\t\treturn 0, ErrConnEmpty\n\t}\n\n\tif isPacketConn(co.Conn) {\n\t\t// UDP connection\n\t\treturn co.Conn.Read(p)\n\t}\n\n\tvar length uint16\n\tif err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {\n\t\treturn 0, err\n\t}\n\tif int(length) > len(p) {\n\t\treturn 0, io.ErrShortBuffer\n\t}\n\n\treturn io.ReadFull(co.Conn, p[:length])\n}\n\n// WriteMsg sends a message through the connection co.\n// If the message m contains a TSIG record the transaction\n// signature is calculated.\nfunc (co *Conn) WriteMsg(m *Msg) (err error) {\n\tvar out []byte\n\tif t := m.IsTsig(); t != nil {\n\t\t// Set tsigRequestMAC for the next read, although only used in zone transfers.\n\t\tout, co.tsigRequestMAC, err = TsigGenerateWithProvider(m, co.tsigProvider(), co.tsigRequestMAC, false)\n\t} else {\n\t\tout, err = m.Pack()\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = co.Write(out)\n\treturn err\n}\n\n// Write implements the net.Conn Write method.\nfunc (co *Conn) Write(p []byte) (int, error) {\n\tif len(p) > MaxMsgSize {\n\t\treturn 0, &Error{err: \"message too large\"}\n\t}\n\n\tif isPacketConn(co.Conn) {\n\t\treturn co.Conn.Write(p)\n\t}\n\n\tmsg := make([]byte, 2+len(p))\n\tbinary.BigEndian.PutUint16(msg, uint16(len(p)))\n\tcopy(msg[2:], p)\n\treturn co.Conn.Write(msg)\n}\n\n// Return the appropriate timeout for a specific request\nfunc (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration {\n\tvar requestTimeout time.Duration\n\tif c.Timeout != 0 {\n\t\trequestTimeout = c.Timeout\n\t} else {\n\t\trequestTimeout = timeout\n\t}\n\t// net.Dialer.Timeout has priority if smaller than the timeouts computed so\n\t// far\n\tif c.Dialer != nil && c.Dialer.Timeout != 0 {\n\t\tif c.Dialer.Timeout < requestTimeout {\n\t\t\trequestTimeout = c.Dialer.Timeout\n\t\t}\n\t}\n\treturn requestTimeout\n}\n\n// Dial connects to the address on the named network.\nfunc Dial(network, address string) (conn *Conn, err error) {\n\tconn = new(Conn)\n\tconn.Conn, err = net.Dial(network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn conn, nil\n}\n\n// ExchangeContext performs a synchronous UDP query, like Exchange. It\n// additionally obeys deadlines from the passed Context.\nfunc ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {\n\tclient := Client{Net: \"udp\"}\n\tr, _, err = client.ExchangeContext(ctx, m, a)\n\t// ignoring rtt to leave the original ExchangeContext API unchanged, but\n\t// this function will go away\n\treturn r, err\n}\n\n// ExchangeConn performs a synchronous query. It sends the message m via the connection\n// c and waits for a reply. The connection c is not closed by ExchangeConn.\n// Deprecated: This function is going away, but can easily be mimicked:\n//\n//\tco := &dns.Conn{Conn: c} // c is your net.Conn\n//\tco.WriteMsg(m)\n//\tin, _  := co.ReadMsg()\n//\tco.Close()\nfunc ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {\n\tprintln(\"dns: ExchangeConn: this function is deprecated\")\n\tco := new(Conn)\n\tco.Conn = c\n\tif err = co.WriteMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\tr, err = co.ReadMsg()\n\tif err == nil && r.Id != m.Id {\n\t\terr = ErrId\n\t}\n\treturn r, err\n}\n\n// DialTimeout acts like Dial but takes a timeout.\nfunc DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {\n\tclient := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}\n\treturn client.Dial(address)\n}\n\n// DialWithTLS connects to the address on the named network with TLS.\nfunc DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {\n\tif !strings.HasSuffix(network, \"-tls\") {\n\t\tnetwork += \"-tls\"\n\t}\n\tclient := Client{Net: network, TLSConfig: tlsConfig}\n\treturn client.Dial(address)\n}\n\n// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.\nfunc DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {\n\tif !strings.HasSuffix(network, \"-tls\") {\n\t\tnetwork += \"-tls\"\n\t}\n\tclient := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}\n\treturn client.Dial(address)\n}\n\n// ExchangeContext acts like Exchange, but honors the deadline on the provided\n// context, if present. If there is both a context deadline and a configured\n// timeout on the client, the earliest of the two takes effect.\nfunc (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) {\n\tconn, err := c.DialContext(ctx, a)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\tdefer conn.Close()\n\n\treturn c.ExchangeWithConnContext(ctx, m, conn)\n}\n"
  },
  {
    "path": "client_test.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestIsPacketConn(t *testing.T) {\n\tt.Run(\"UDP\", func(t *testing.T) {\n\t\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t\t}\n\t\tdefer s.Shutdown()\n\t\tc, err := net.Dial(\"udp\", addrstr)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t\t}\n\t\tdefer c.Close()\n\t\tif !isPacketConn(c) {\n\t\t\tt.Error(\"UDP connection should be a packet conn\")\n\t\t}\n\t\tif !isPacketConn(struct{ *net.UDPConn }{c.(*net.UDPConn)}) {\n\t\t\tt.Error(\"UDP connection (wrapped type) should be a packet conn\")\n\t\t}\n\t})\n\n\tt.Run(\"TCP\", func(t *testing.T) {\n\t\ts, addrstr, _, err := RunLocalTCPServer(\":0\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t\t}\n\t\tdefer s.Shutdown()\n\t\tc, err := net.Dial(\"tcp\", addrstr)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t\t}\n\t\tdefer c.Close()\n\t\tif isPacketConn(c) {\n\t\t\tt.Error(\"TCP connection should not be a packet conn\")\n\t\t}\n\t\tif isPacketConn(struct{ *net.TCPConn }{c.(*net.TCPConn)}) {\n\t\t\tt.Error(\"TCP connection (wrapped type) should not be a packet conn\")\n\t\t}\n\t})\n\n\tt.Run(\"Unix datagram\", func(t *testing.T) {\n\t\ts, addrstr, _, err := RunLocalUnixGramServer(tempFile(t, \"unixgram.sock\"))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t\t}\n\t\tdefer s.Shutdown()\n\t\tc, err := net.Dial(\"unixgram\", addrstr)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t\t}\n\t\tdefer c.Close()\n\t\tif !isPacketConn(c) {\n\t\t\tt.Error(\"Unix datagram connection should be a packet conn\")\n\t\t}\n\t\tif !isPacketConn(struct{ *net.UnixConn }{c.(*net.UnixConn)}) {\n\t\t\tt.Error(\"Unix datagram connection (wrapped type) should be a packet conn\")\n\t\t}\n\t})\n\n\tt.Run(\"Unix Seqpacket\", func(t *testing.T) {\n\t\tshutChan, addrstr, err := RunLocalUnixSeqPacketServer(tempFile(t, \"unixpacket.sock\"))\n\t\tif err != nil {\n\t\t\tif errors.Is(err, syscall.EPROTONOSUPPORT) {\n\t\t\t\tt.Skip(\"unix seqpacket not supported on this OS\")\n\t\t\t}\n\t\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t\t}\n\n\t\tdefer func() {\n\t\t\tshutChan <- &struct{}{}\n\t\t}()\n\t\tc, err := net.Dial(\"unixpacket\", addrstr)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t\t}\n\t\tdefer c.Close()\n\t\tif !isPacketConn(c) {\n\t\t\tt.Error(\"Unix datagram connection should be a packet conn\")\n\t\t}\n\t\tif !isPacketConn(struct{ *net.UnixConn }{c.(*net.UnixConn)}) {\n\t\t\tt.Error(\"Unix datagram connection (wrapped type) should be a packet conn\")\n\t\t}\n\t})\n\n\tt.Run(\"Unix stream\", func(t *testing.T) {\n\t\ts, addrstr, _, err := RunLocalUnixServer(tempFile(t, \"unixstream.sock\"))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t\t}\n\t\tdefer s.Shutdown()\n\t\tc, err := net.Dial(\"unix\", addrstr)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t\t}\n\t\tdefer c.Close()\n\t\tif isPacketConn(c) {\n\t\t\tt.Error(\"Unix stream connection should not be a packet conn\")\n\t\t}\n\t\tif isPacketConn(struct{ *net.UnixConn }{c.(*net.UnixConn)}) {\n\t\t\tt.Error(\"Unix stream connection (wrapped type) should not be a packet conn\")\n\t\t}\n\t})\n}\n\nfunc TestDialUDP(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := new(Client)\n\tconn, err := c.Dial(addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t}\n\tif conn == nil {\n\t\tt.Fatalf(\"conn is nil\")\n\t}\n}\n\nfunc TestClientSync(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := new(Client)\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %v\", err)\n\t}\n\tif r == nil {\n\t\tt.Fatal(\"response is nil\")\n\t}\n\tif r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n\t// And now with plain Exchange().\n\tr, err = Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n\tif r == nil || r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n}\n\nfunc TestClientLocalAddress(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServerEchoAddrPort)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := new(Client)\n\tladdr := net.UDPAddr{IP: net.ParseIP(\"0.0.0.0\"), Port: 12345, Zone: \"\"}\n\tc.Dialer = &net.Dialer{LocalAddr: &laddr}\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %v\", err)\n\t}\n\tif r != nil && r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n\tif len(r.Extra) != 1 {\n\t\tt.Fatalf(\"failed to get additional answers\\n%v\", r)\n\t}\n\ttxt := r.Extra[0].(*TXT)\n\tif txt == nil {\n\t\tt.Errorf(\"invalid TXT response\\n%v\", txt)\n\t}\n\tif len(txt.Txt) != 1 || !strings.Contains(txt.Txt[0], \":12345\") {\n\t\tt.Errorf(\"invalid TXT response\\n%v\", txt.Txt)\n\t}\n}\n\nfunc TestClientTLSSyncV4(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\tconfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\ts, addrstr, _, err := RunLocalTLSServer(\":0\", &config)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := new(Client)\n\n\t// test tcp-tls\n\tc.Net = \"tcp-tls\"\n\tc.TLSConfig = &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t}\n\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %v\", err)\n\t}\n\tif r == nil {\n\t\tt.Fatal(\"response is nil\")\n\t}\n\tif r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n\n\t// test tcp4-tls\n\tc.Net = \"tcp4-tls\"\n\tc.TLSConfig = &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t}\n\n\tr, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %v\", err)\n\t}\n\tif r == nil {\n\t\tt.Fatal(\"response is nil\")\n\t}\n\tif r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n}\n\nfunc isNetworkTimeout(err error) bool {\n\t// TODO: when Go 1.14 support is dropped, do this: https://golang.org/doc/go1.15#net\n\tvar netError net.Error\n\treturn errors.As(err, &netError) && netError.Timeout()\n}\n\nfunc TestClientSyncBadID(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServerBadID)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\t// Test with client.Exchange, the plain Exchange function is just a wrapper, so\n\t// we don't need to test that separately.\n\tc := &Client{\n\t\tTimeout: 10 * time.Millisecond,\n\t}\n\tif _, _, err := c.Exchange(m, addrstr); err == nil || !isNetworkTimeout(err) {\n\t\tt.Errorf(\"query did not time out\")\n\t}\n}\n\nfunc TestClientSyncBadThenGoodID(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServerBadThenGoodID)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := new(Client)\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n\tif r.Id != m.Id {\n\t\tt.Errorf(\"failed to get response with expected Id\")\n\t}\n}\n\nfunc TestClientSyncTCPBadID(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServerBadID)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := &Client{\n\t\tNet: \"tcp\",\n\t}\n\tif _, _, err := c.Exchange(m, addrstr); err != ErrId {\n\t\tt.Errorf(\"did not find a bad Id\")\n\t}\n}\n\nfunc TestClientEDNS0(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeDNSKEY)\n\n\tm.SetEdns0(2048, true)\n\n\tc := new(Client)\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %v\", err)\n\t}\n\n\tif r != nil && r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get a valid answer\\n%v\", r)\n\t}\n}\n\n// Validates the transmission and parsing of local EDNS0 options.\nfunc TestClientEDNS0Local(t *testing.T) {\n\toptStr1 := \"1979:0x0707\"\n\toptStr2 := strconv.Itoa(EDNS0LOCALSTART) + \":0x0601\"\n\n\thandler := func(w ResponseWriter, req *Msg) {\n\t\tm := new(Msg)\n\t\tm.SetReply(req)\n\n\t\tm.Extra = make([]RR, 1, 2)\n\t\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello local edns\"}}\n\n\t\t// If the local options are what we expect, then reflect them back.\n\t\tec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String()\n\t\tec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String()\n\t\tif ec1 == optStr1 && ec2 == optStr2 {\n\t\t\tm.Extra = append(m.Extra, req.Extra[0])\n\t\t}\n\n\t\tw.WriteMsg(m)\n\t}\n\n\tHandleFunc(\"miek.nl.\", handler)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\n\t// Add two local edns options to the query.\n\tec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}}\n\tec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}}\n\to := &OPT{Hdr: RR_Header{Name: \".\", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}}\n\tm.Extra = append(m.Extra, o)\n\n\tc := new(Client)\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %s\", err)\n\t}\n\n\tif r == nil {\n\t\tt.Fatal(\"response is nil\")\n\t}\n\tif r.Rcode != RcodeSuccess {\n\t\tt.Fatal(\"failed to get a valid answer\")\n\t}\n\n\ttxt := r.Extra[0].(*TXT).Txt[0]\n\tif txt != \"Hello local edns\" {\n\t\tt.Error(\"Unexpected result for miek.nl\", txt, \"!= Hello local edns\")\n\t}\n\n\t// Validate the local options in the reply.\n\tgot := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String()\n\tif got != optStr1 {\n\t\tt.Errorf(\"failed to get local edns0 answer; got %s, expected %s\", got, optStr1)\n\t}\n\n\tgot = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String()\n\tif got != optStr2 {\n\t\tt.Errorf(\"failed to get local edns0 answer; got %s, expected %s\", got, optStr2)\n\t}\n}\n\nfunc TestClientConn(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\t// This uses TCP just to make it slightly different than TestClientSync\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tcn, err := Dial(\"tcp\", addrstr)\n\tif err != nil {\n\t\tt.Errorf(\"failed to dial %s: %v\", addrstr, err)\n\t}\n\n\terr = cn.WriteMsg(m)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n\tr, err := cn.ReadMsg()\n\tif err != nil {\n\t\tt.Errorf(\"failed to get a valid answer: %v\", err)\n\t}\n\tif r == nil || r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n\n\terr = cn.WriteMsg(m)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n\th := new(Header)\n\tbuf, err := cn.ReadMsgHeader(h)\n\tif buf == nil {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n\tif err != nil {\n\t\tt.Errorf(\"failed to get a valid answer: %v\", err)\n\t}\n\tif int(h.Bits&0xF) != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer in ReadMsgHeader\\n%v\", r)\n\t}\n\tif h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {\n\t\tt.Errorf(\"expected to have question and additional in response; got something else: %+v\", h)\n\t}\n\tif err = r.Unpack(buf); err != nil {\n\t\tt.Errorf(\"unable to unpack message fully: %v\", err)\n\t}\n}\n\nfunc TestClientConnWriteSinglePacket(t *testing.T) {\n\tc := &countingConn{}\n\tconn := Conn{\n\t\tConn: c,\n\t}\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\terr := conn.WriteMsg(m)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to write: %v\", err)\n\t}\n\n\tif c.writes != 1 {\n\t\tt.Fatalf(\"incorrect number of Write calls\")\n\t}\n}\n\nfunc TestTruncatedMsg(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSRV)\n\tcnt := 10\n\tfor i := 0; i < cnt; i++ {\n\t\tr := &SRV{\n\t\t\tHdr:    RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0},\n\t\t\tPort:   uint16(i + 8000),\n\t\t\tTarget: \"target.miek.nl.\",\n\t\t}\n\t\tm.Answer = append(m.Answer, r)\n\n\t\tre := &A{\n\t\t\tHdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0},\n\t\t\tA:   net.ParseIP(fmt.Sprintf(\"127.0.0.%d\", i)).To4(),\n\t\t}\n\t\tm.Extra = append(m.Extra, re)\n\t}\n\tbuf, err := m.Pack()\n\tif err != nil {\n\t\tt.Errorf(\"failed to pack: %v\", err)\n\t}\n\n\tr := new(Msg)\n\tif err = r.Unpack(buf); err != nil {\n\t\tt.Errorf(\"unable to unpack message: %v\", err)\n\t}\n\tif len(r.Answer) != cnt {\n\t\tt.Errorf(\"answer count after regular unpack doesn't match: %d\", len(r.Answer))\n\t}\n\tif len(r.Extra) != cnt {\n\t\tt.Errorf(\"extra count after regular unpack doesn't match: %d\", len(r.Extra))\n\t}\n\n\tm.Truncated = true\n\tbuf, err = m.Pack()\n\tif err != nil {\n\t\tt.Errorf(\"failed to pack truncated message: %v\", err)\n\t}\n\n\tr = new(Msg)\n\tif err = r.Unpack(buf); err != nil {\n\t\tt.Errorf(\"failed to unpack truncated message: %v\", err)\n\t}\n\tif !r.Truncated {\n\t\tt.Errorf(\"truncated message wasn't unpacked as truncated\")\n\t}\n\tif len(r.Answer) != cnt {\n\t\tt.Errorf(\"answer count after truncated unpack doesn't match: %d\", len(r.Answer))\n\t}\n\tif len(r.Extra) != cnt {\n\t\tt.Errorf(\"extra count after truncated unpack doesn't match: %d\", len(r.Extra))\n\t}\n\n\t// Now we want to remove almost all of the extra records\n\t// We're going to loop over the extra to get the count of the size of all\n\t// of them\n\toff := 0\n\tbuf1 := make([]byte, m.Len())\n\tfor i := 0; i < len(m.Extra); i++ {\n\t\toff, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to pack extra: %v\", err)\n\t\t}\n\t}\n\n\t// Remove all of the extra bytes but 10 bytes from the end of buf\n\toff -= 10\n\tbuf1 = buf[:len(buf)-off]\n\n\tr = new(Msg)\n\tif err = r.Unpack(buf1); err == nil {\n\t\tt.Error(\"cutoff message should have failed to unpack\")\n\t}\n\t// r's header might be still usable.\n\tif !r.Truncated {\n\t\tt.Error(\"truncated cutoff message wasn't unpacked as truncated\")\n\t}\n\tif len(r.Answer) != cnt {\n\t\tt.Errorf(\"answer count after cutoff unpack doesn't match: %d\", len(r.Answer))\n\t}\n\tif len(r.Extra) != 0 {\n\t\tt.Errorf(\"extra count after cutoff unpack is not zero: %d\", len(r.Extra))\n\t}\n\n\t// Now we want to remove almost all of the answer records too\n\tbuf1 = make([]byte, m.Len())\n\tas := 0\n\tfor i := 0; i < len(m.Extra); i++ {\n\t\toff1 := off\n\t\toff, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)\n\t\tas = off - off1\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to pack extra: %v\", err)\n\t\t}\n\t}\n\n\t// Keep exactly one answer left\n\t// This should still cause Answer to be nil\n\toff -= as\n\tbuf1 = buf[:len(buf)-off]\n\n\tr = new(Msg)\n\tif err = r.Unpack(buf1); err == nil {\n\t\tt.Error(\"cutoff message should have failed to unpack\")\n\t}\n\tif !r.Truncated {\n\t\tt.Error(\"truncated cutoff message wasn't unpacked as truncated\")\n\t}\n\tif len(r.Answer) != 0 {\n\t\tt.Errorf(\"answer count after second cutoff unpack is not zero: %d\", len(r.Answer))\n\t}\n\n\t// Now leave only 1 byte of the question\n\t// Since the header is always 12 bytes, we just need to keep 13\n\tbuf1 = buf[:13]\n\n\tr = new(Msg)\n\terr = r.Unpack(buf1)\n\tif err == nil {\n\t\tt.Errorf(\"error should be nil after question cutoff unpack: %v\", err)\n\t}\n\n\t// Finally, if we only have the header, we don't return an error.\n\tbuf1 = buf[:12]\n\n\tr = new(Msg)\n\tif err = r.Unpack(buf1); err != nil {\n\t\tt.Errorf(\"from header-only unpack should not return an error: %v\", err)\n\t}\n}\n\nfunc TestTimeout(t *testing.T) {\n\t// Set up a dummy UDP server that won't respond\n\taddr, err := net.ResolveUDPAddr(\"udp\", \":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to resolve local udp address: %v\", err)\n\t}\n\tconn, err := net.ListenUDP(\"udp\", addr)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer conn.Close()\n\taddrstr := conn.LocalAddr().String()\n\n\t// Message to send\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\n\trunTest := func(name string, exchange func(m *Msg, addr string, timeout time.Duration) (*Msg, time.Duration, error)) {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tstart := time.Now()\n\n\t\t\ttimeout := time.Millisecond\n\t\t\tallowable := timeout + 10*time.Millisecond\n\n\t\t\t_, _, err := exchange(m, addrstr, timeout)\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"no timeout using Client.%s\", name)\n\t\t\t}\n\n\t\t\tlength := time.Since(start)\n\t\t\tif length > allowable {\n\t\t\t\tt.Errorf(\"exchange took longer %v than specified Timeout %v\", length, allowable)\n\t\t\t}\n\t\t})\n\t}\n\trunTest(\"Exchange\", func(m *Msg, addr string, timeout time.Duration) (*Msg, time.Duration, error) {\n\t\tc := &Client{Timeout: timeout}\n\t\treturn c.Exchange(m, addr)\n\t})\n\trunTest(\"ExchangeContext\", func(m *Msg, addr string, timeout time.Duration) (*Msg, time.Duration, error) {\n\t\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\t\tdefer cancel()\n\n\t\treturn new(Client).ExchangeContext(ctx, m, addrstr)\n\t})\n}\n\nfunc TestExchangeWithConn(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tc := new(Client)\n\tconn, err := c.Dial(addrstr)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to dial: %v\", err)\n\t}\n\n\tr, _, err := c.ExchangeWithConn(m, conn)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to exchange: %v\", err)\n\t}\n\tif r == nil {\n\t\tt.Fatal(\"response is nil\")\n\t}\n\tif r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"failed to get an valid answer\\n%v\", r)\n\t}\n}\n"
  },
  {
    "path": "clientconfig.go",
    "content": "package dns\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// ClientConfig wraps the contents of the /etc/resolv.conf file.\ntype ClientConfig struct {\n\tServers  []string // servers to use\n\tSearch   []string // suffixes to append to local name\n\tPort     string   // what port to use\n\tNdots    int      // number of dots in name to trigger absolute lookup\n\tTimeout  int      // seconds before giving up on packet\n\tAttempts int      // lost packets before giving up on server, not used in the package dns\n}\n\n// ClientConfigFromFile parses a resolv.conf(5) like file and returns\n// a *ClientConfig.\nfunc ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {\n\tfile, err := os.Open(resolvconf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\treturn ClientConfigFromReader(file)\n}\n\n// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument\nfunc ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {\n\tc := new(ClientConfig)\n\tscanner := bufio.NewScanner(resolvconf)\n\tc.Servers = make([]string, 0)\n\tc.Search = make([]string, 0)\n\tc.Port = \"53\"\n\tc.Ndots = 1\n\tc.Timeout = 5\n\tc.Attempts = 2\n\n\tfor scanner.Scan() {\n\t\tif err := scanner.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tline := scanner.Text()\n\t\tf := strings.Fields(line)\n\t\tif len(f) < 1 {\n\t\t\tcontinue\n\t\t}\n\t\tswitch f[0] {\n\t\tcase \"nameserver\": // add one name server\n\t\t\tif len(f) > 1 {\n\t\t\t\t// One more check: make sure server name is\n\t\t\t\t// just an IP address.  Otherwise we need DNS\n\t\t\t\t// to look it up.\n\t\t\t\tname := f[1]\n\t\t\t\tc.Servers = append(c.Servers, name)\n\t\t\t}\n\n\t\tcase \"domain\": // set search path to just this domain\n\t\t\tif len(f) > 1 {\n\t\t\t\tc.Search = make([]string, 1)\n\t\t\t\tc.Search[0] = f[1]\n\t\t\t} else {\n\t\t\t\tc.Search = make([]string, 0)\n\t\t\t}\n\n\t\tcase \"search\": // set search path to given servers\n\t\t\tc.Search = cloneSlice(f[1:])\n\n\t\tcase \"options\": // magic options\n\t\t\tfor _, s := range f[1:] {\n\t\t\t\tswitch {\n\t\t\t\tcase len(s) >= 6 && s[:6] == \"ndots:\":\n\t\t\t\t\tn, _ := strconv.Atoi(s[6:])\n\t\t\t\t\tif n < 0 {\n\t\t\t\t\t\tn = 0\n\t\t\t\t\t} else if n > 15 {\n\t\t\t\t\t\tn = 15\n\t\t\t\t\t}\n\t\t\t\t\tc.Ndots = n\n\t\t\t\tcase len(s) >= 8 && s[:8] == \"timeout:\":\n\t\t\t\t\tn, _ := strconv.Atoi(s[8:])\n\t\t\t\t\tif n < 1 {\n\t\t\t\t\t\tn = 1\n\t\t\t\t\t}\n\t\t\t\t\tc.Timeout = n\n\t\t\t\tcase len(s) >= 9 && s[:9] == \"attempts:\":\n\t\t\t\t\tn, _ := strconv.Atoi(s[9:])\n\t\t\t\t\tif n < 1 {\n\t\t\t\t\t\tn = 1\n\t\t\t\t\t}\n\t\t\t\t\tc.Attempts = n\n\t\t\t\tcase s == \"rotate\":\n\t\t\t\t\t/* not imp */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn c, nil\n}\n\n// NameList returns all of the names that should be queried based on the\n// config. It is based off of go's net/dns name building, but it does not\n// check the length of the resulting names.\nfunc (c *ClientConfig) NameList(name string) []string {\n\t// if this domain is already fully qualified, no append needed.\n\tif IsFqdn(name) {\n\t\treturn []string{name}\n\t}\n\n\t// Check to see if the name has more labels than Ndots. Do this before making\n\t// the domain fully qualified.\n\thasNdots := CountLabel(name) > c.Ndots\n\t// Make the domain fully qualified.\n\tname = Fqdn(name)\n\n\t// Make a list of names based off search.\n\tnames := []string{}\n\n\t// If name has enough dots, try that first.\n\tif hasNdots {\n\t\tnames = append(names, name)\n\t}\n\tfor _, s := range c.Search {\n\t\tnames = append(names, Fqdn(name+s))\n\t}\n\t// If we didn't have enough dots, try after suffixes.\n\tif !hasNdots {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n"
  },
  {
    "path": "clientconfig_test.go",
    "content": "package dns\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\nconst normal string = `\n# Comment\ndomain somedomain.com\nnameserver 10.28.10.2\nnameserver 11.28.10.1\n`\n\nconst missingNewline string = `\ndomain somedomain.com\nnameserver 10.28.10.2\nnameserver 11.28.10.1` // <- NOTE: NO newline.\n\nfunc testConfig(t *testing.T, data string) {\n\tcc, err := ClientConfigFromReader(strings.NewReader(data))\n\tif err != nil {\n\t\tt.Errorf(\"error parsing resolv.conf: %v\", err)\n\t}\n\tif l := len(cc.Servers); l != 2 {\n\t\tt.Errorf(\"incorrect number of nameservers detected: %d\", l)\n\t}\n\tif l := len(cc.Search); l != 1 {\n\t\tt.Errorf(\"domain directive not parsed correctly: %v\", cc.Search)\n\t} else {\n\t\tif cc.Search[0] != \"somedomain.com\" {\n\t\t\tt.Errorf(\"domain is unexpected: %v\", cc.Search[0])\n\t\t}\n\t}\n}\n\nfunc TestNameserver(t *testing.T)          { testConfig(t, normal) }\nfunc TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }\n\nfunc TestNdots(t *testing.T) {\n\tndotsVariants := map[string]int{\n\t\t\"options ndots:0\":  0,\n\t\t\"options ndots:1\":  1,\n\t\t\"options ndots:15\": 15,\n\t\t\"options ndots:16\": 15,\n\t\t\"options ndots:-1\": 0,\n\t\t\"\":                 1,\n\t}\n\n\tfor data := range ndotsVariants {\n\t\tcc, err := ClientConfigFromReader(strings.NewReader(data))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error parsing resolv.conf: %v\", err)\n\t\t}\n\t\tif cc.Ndots != ndotsVariants[data] {\n\t\t\tt.Errorf(\"Ndots not properly parsed: (Expected: %d / Was: %d)\", ndotsVariants[data], cc.Ndots)\n\t\t}\n\t}\n}\n\nfunc TestClientConfigFromReaderAttempts(t *testing.T) {\n\ttestCases := []struct {\n\t\tdata     string\n\t\texpected int\n\t}{\n\t\t{data: \"options attempts:0\", expected: 1},\n\t\t{data: \"options attempts:1\", expected: 1},\n\t\t{data: \"options attempts:15\", expected: 15},\n\t\t{data: \"options attempts:16\", expected: 16},\n\t\t{data: \"options attempts:-1\", expected: 1},\n\t\t{data: \"options attempt:\", expected: 2},\n\t}\n\n\tfor _, test := range testCases {\n\t\ttest := test\n\t\tt.Run(strings.Replace(test.data, \":\", \" \", -1), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcc, err := ClientConfigFromReader(strings.NewReader(test.data))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"error parsing resolv.conf: %v\", err)\n\t\t\t}\n\t\t\tif cc.Attempts != test.expected {\n\t\t\t\tt.Errorf(\"A attempts not properly parsed: (Expected: %d / Was: %d)\", test.expected, cc.Attempts)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadFromFile(t *testing.T) {\n\ttempDir := t.TempDir()\n\n\tpath := filepath.Join(tempDir, \"resolv.conf\")\n\tif err := os.WriteFile(path, []byte(normal), 0o644); err != nil {\n\t\tt.Fatalf(\"writeFile: %v\", err)\n\t}\n\tcc, err := ClientConfigFromFile(path)\n\tif err != nil {\n\t\tt.Errorf(\"error parsing resolv.conf: %v\", err)\n\t}\n\tif l := len(cc.Servers); l != 2 {\n\t\tt.Errorf(\"incorrect number of nameservers detected: %d\", l)\n\t}\n\tif l := len(cc.Search); l != 1 {\n\t\tt.Errorf(\"domain directive not parsed correctly: %v\", cc.Search)\n\t} else {\n\t\tif cc.Search[0] != \"somedomain.com\" {\n\t\t\tt.Errorf(\"domain is unexpected: %v\", cc.Search[0])\n\t\t}\n\t}\n}\n\nfunc TestNameListNdots1(t *testing.T) {\n\tcfg := ClientConfig{\n\t\tNdots: 1,\n\t}\n\t// fqdn should be only result returned\n\tnames := cfg.NameList(\"miek.nl.\")\n\tif len(names) != 1 {\n\t\tt.Errorf(\"NameList returned != 1 names: %v\", names)\n\t} else if names[0] != \"miek.nl.\" {\n\t\tt.Errorf(\"NameList didn't return sent fqdn domain: %v\", names[0])\n\t}\n\n\tcfg.Search = []string{\n\t\t\"test\",\n\t}\n\t// Sent domain has NDots and search\n\tnames = cfg.NameList(\"miek.nl\")\n\tif len(names) != 2 {\n\t\tt.Errorf(\"NameList returned != 2 names: %v\", names)\n\t} else if names[0] != \"miek.nl.\" {\n\t\tt.Errorf(\"NameList didn't return sent domain first: %v\", names[0])\n\t} else if names[1] != \"miek.nl.test.\" {\n\t\tt.Errorf(\"NameList didn't return search last: %v\", names[1])\n\t}\n}\n\nfunc TestNameListNdots2(t *testing.T) {\n\tcfg := ClientConfig{\n\t\tNdots: 2,\n\t}\n\n\t// Sent domain has less than NDots and search\n\tcfg.Search = []string{\n\t\t\"test\",\n\t}\n\tnames := cfg.NameList(\"miek.nl\")\n\n\tif len(names) != 2 {\n\t\tt.Errorf(\"NameList returned != 2 names: %v\", names)\n\t} else if names[0] != \"miek.nl.test.\" {\n\t\tt.Errorf(\"NameList didn't return search first: %v\", names[0])\n\t} else if names[1] != \"miek.nl.\" {\n\t\tt.Errorf(\"NameList didn't return sent domain last: %v\", names[1])\n\t}\n}\n\nfunc TestNameListNdots0(t *testing.T) {\n\tcfg := ClientConfig{\n\t\tNdots: 0,\n\t}\n\tcfg.Search = []string{\n\t\t\"test\",\n\t}\n\t// Sent domain has less than NDots and search\n\tnames := cfg.NameList(\"miek\")\n\tif len(names) != 2 {\n\t\tt.Errorf(\"NameList returned != 2 names: %v\", names)\n\t} else if names[0] != \"miek.\" {\n\t\tt.Errorf(\"NameList didn't return search first: %v\", names[0])\n\t} else if names[1] != \"miek.test.\" {\n\t\tt.Errorf(\"NameList didn't return sent domain last: %v\", names[1])\n\t}\n}\n"
  },
  {
    "path": "dane.go",
    "content": "package dns\n\nimport (\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n\t\"errors\"\n)\n\n// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.\nfunc CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {\n\tswitch matchingType {\n\tcase 0:\n\t\tswitch selector {\n\t\tcase 0:\n\t\t\treturn hex.EncodeToString(cert.Raw), nil\n\t\tcase 1:\n\t\t\treturn hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil\n\t\t}\n\tcase 1:\n\t\th := sha256.New()\n\t\tswitch selector {\n\t\tcase 0:\n\t\t\th.Write(cert.Raw)\n\t\t\treturn hex.EncodeToString(h.Sum(nil)), nil\n\t\tcase 1:\n\t\t\th.Write(cert.RawSubjectPublicKeyInfo)\n\t\t\treturn hex.EncodeToString(h.Sum(nil)), nil\n\t\t}\n\tcase 2:\n\t\th := sha512.New()\n\t\tswitch selector {\n\t\tcase 0:\n\t\t\th.Write(cert.Raw)\n\t\t\treturn hex.EncodeToString(h.Sum(nil)), nil\n\t\tcase 1:\n\t\t\th.Write(cert.RawSubjectPublicKeyInfo)\n\t\t\treturn hex.EncodeToString(h.Sum(nil)), nil\n\t\t}\n\t}\n\treturn \"\", errors.New(\"dns: bad MatchingType or Selector\")\n}\n"
  },
  {
    "path": "defaults.go",
    "content": "package dns\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nconst hexDigit = \"0123456789abcdef\"\n\n// Everything is assumed in ClassINET.\n\n// SetReply creates a reply message from a request message.\nfunc (dns *Msg) SetReply(request *Msg) *Msg {\n\tdns.Id = request.Id\n\tdns.Response = true\n\tdns.Opcode = request.Opcode\n\tif dns.Opcode == OpcodeQuery {\n\t\tdns.RecursionDesired = request.RecursionDesired // Copy rd bit\n\t\tdns.CheckingDisabled = request.CheckingDisabled // Copy cd bit\n\t}\n\tdns.Rcode = RcodeSuccess\n\tif len(request.Question) > 0 {\n\t\tdns.Question = []Question{request.Question[0]}\n\t}\n\treturn dns\n}\n\n// SetQuestion creates a question message, it sets the Question\n// section, generates an Id and sets the RecursionDesired (RD)\n// bit to true.\nfunc (dns *Msg) SetQuestion(z string, t uint16) *Msg {\n\tdns.Id = Id()\n\tdns.RecursionDesired = true\n\tdns.Question = make([]Question, 1)\n\tdns.Question[0] = Question{z, t, ClassINET}\n\treturn dns\n}\n\n// SetNotify creates a notify message, it sets the Question\n// section, generates an Id and sets the Authoritative (AA)\n// bit to true.\nfunc (dns *Msg) SetNotify(z string) *Msg {\n\tdns.Opcode = OpcodeNotify\n\tdns.Authoritative = true\n\tdns.Id = Id()\n\tdns.Question = make([]Question, 1)\n\tdns.Question[0] = Question{z, TypeSOA, ClassINET}\n\treturn dns\n}\n\n// SetRcode creates an error message suitable for the request.\nfunc (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {\n\tdns.SetReply(request)\n\tdns.Rcode = rcode\n\treturn dns\n}\n\n// SetRcodeFormatError creates a message with FormError set.\nfunc (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {\n\tdns.Rcode = RcodeFormatError\n\tdns.Opcode = OpcodeQuery\n\tdns.Response = true\n\tdns.Authoritative = false\n\tdns.Id = request.Id\n\treturn dns\n}\n\n// SetUpdate makes the message a dynamic update message. It\n// sets the ZONE section to: z, TypeSOA, ClassINET.\nfunc (dns *Msg) SetUpdate(z string) *Msg {\n\tdns.Id = Id()\n\tdns.Response = false\n\tdns.Opcode = OpcodeUpdate\n\tdns.Compress = false // BIND9 cannot handle compression\n\tdns.Question = make([]Question, 1)\n\tdns.Question[0] = Question{z, TypeSOA, ClassINET}\n\treturn dns\n}\n\n// SetIxfr creates message for requesting an IXFR.\nfunc (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {\n\tdns.Id = Id()\n\tdns.Question = make([]Question, 1)\n\tdns.Ns = make([]RR, 1)\n\ts := new(SOA)\n\ts.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}\n\ts.Serial = serial\n\ts.Ns = ns\n\ts.Mbox = mbox\n\tdns.Question[0] = Question{z, TypeIXFR, ClassINET}\n\tdns.Ns[0] = s\n\treturn dns\n}\n\n// SetAxfr creates message for requesting an AXFR.\nfunc (dns *Msg) SetAxfr(z string) *Msg {\n\tdns.Id = Id()\n\tdns.Question = make([]Question, 1)\n\tdns.Question[0] = Question{z, TypeAXFR, ClassINET}\n\treturn dns\n}\n\n// SetTsig appends a TSIG RR to the message.\n// This is only a skeleton TSIG RR that is added as the last RR in the\n// additional section. The TSIG is calculated when the message is being send.\nfunc (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {\n\tt := new(TSIG)\n\tt.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}\n\tt.Algorithm = algo\n\tt.Fudge = fudge\n\tt.TimeSigned = uint64(timesigned)\n\tt.OrigId = dns.Id\n\tdns.Extra = append(dns.Extra, t)\n\treturn dns\n}\n\n// SetEdns0 appends a EDNS0 OPT RR to the message.\n// TSIG should always the last RR in a message.\nfunc (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {\n\te := new(OPT)\n\te.Hdr.Name = \".\"\n\te.Hdr.Rrtype = TypeOPT\n\te.SetUDPSize(udpsize)\n\tif do {\n\t\te.SetDo()\n\t}\n\tdns.Extra = append(dns.Extra, e)\n\treturn dns\n}\n\n// IsTsig checks if the message has a TSIG record as the last record\n// in the additional section. It returns the TSIG record found or nil.\nfunc (dns *Msg) IsTsig() *TSIG {\n\tif len(dns.Extra) > 0 {\n\t\tif dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {\n\t\t\treturn dns.Extra[len(dns.Extra)-1].(*TSIG)\n\t\t}\n\t}\n\treturn nil\n}\n\n// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0\n// record in the additional section will do. It returns the OPT record\n// found or nil.\nfunc (dns *Msg) IsEdns0() *OPT {\n\t// RFC 6891, Section 6.1.1 allows the OPT record to appear\n\t// anywhere in the additional record section, but it's usually at\n\t// the end so start there.\n\tfor i := len(dns.Extra) - 1; i >= 0; i-- {\n\t\tif dns.Extra[i].Header().Rrtype == TypeOPT {\n\t\t\treturn dns.Extra[i].(*OPT)\n\t\t}\n\t}\n\treturn nil\n}\n\n// popEdns0 is like IsEdns0, but it removes the record from the message.\nfunc (dns *Msg) popEdns0() *OPT {\n\t// RFC 6891, Section 6.1.1 allows the OPT record to appear\n\t// anywhere in the additional record section, but it's usually at\n\t// the end so start there.\n\tfor i := len(dns.Extra) - 1; i >= 0; i-- {\n\t\tif dns.Extra[i].Header().Rrtype == TypeOPT {\n\t\t\topt := dns.Extra[i].(*OPT)\n\t\t\tdns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...)\n\t\t\treturn opt\n\t\t}\n\t}\n\treturn nil\n}\n\n// IsDomainName checks if s is a valid domain name, it returns the number of\n// labels and true, when a domain name is valid.  Note that non fully qualified\n// domain name is considered valid, in this case the last label is counted in\n// the number of labels.  When false is returned the number of labels is not\n// defined.  Also note that this function is extremely liberal; almost any\n// string is a valid domain name as the DNS is 8 bit protocol. It checks if each\n// label fits in 63 characters and that the entire name will fit into the 255\n// octet wire format limit.\nfunc IsDomainName(s string) (labels int, ok bool) {\n\t// XXX: The logic in this function was copied from packDomainName and\n\t// should be kept in sync with that function.\n\n\tconst lenmsg = 256\n\n\tif len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata.\n\t\treturn 0, false\n\t}\n\n\ts = Fqdn(s)\n\n\t// Each dot ends a segment of the name. Except for escaped dots (\\.), which\n\t// are normal dots.\n\n\tvar (\n\t\toff    int\n\t\tbegin  int\n\t\twasDot bool\n\t\tescape bool\n\t)\n\tfor i := 0; i < len(s); i++ {\n\t\tswitch s[i] {\n\t\tcase '\\\\':\n\t\t\tescape = !escape\n\t\t\tif off+1 > lenmsg {\n\t\t\t\treturn labels, false\n\t\t\t}\n\n\t\t\t// check for \\DDD\n\t\t\tif isDDD(s[i+1:]) {\n\t\t\t\ti += 3\n\t\t\t\tbegin += 3\n\t\t\t} else {\n\t\t\t\ti++\n\t\t\t\tbegin++\n\t\t\t}\n\n\t\t\twasDot = false\n\t\tcase '.':\n\t\t\tescape = false\n\t\t\tif i == 0 && len(s) > 1 {\n\t\t\t\t// leading dots are not legal except for the root zone\n\t\t\t\treturn labels, false\n\t\t\t}\n\n\t\t\tif wasDot {\n\t\t\t\t// two dots back to back is not legal\n\t\t\t\treturn labels, false\n\t\t\t}\n\t\t\twasDot = true\n\n\t\t\tlabelLen := i - begin\n\t\t\tif labelLen >= 1<<6 { // top two bits of length must be clear\n\t\t\t\treturn labels, false\n\t\t\t}\n\n\t\t\t// off can already (we're in a loop) be bigger than lenmsg\n\t\t\t// this happens when a name isn't fully qualified\n\t\t\toff += 1 + labelLen\n\t\t\tif off > lenmsg {\n\t\t\t\treturn labels, false\n\t\t\t}\n\n\t\t\tlabels++\n\t\t\tbegin = i + 1\n\t\tdefault:\n\t\t\tescape = false\n\t\t\twasDot = false\n\t\t}\n\t}\n\tif escape {\n\t\treturn labels, false\n\t}\n\treturn labels, true\n}\n\n// IsSubDomain checks if child is indeed a child of the parent. If child and parent\n// are the same domain true is returned as well.\nfunc IsSubDomain(parent, child string) bool {\n\t// Entire child is contained in parent\n\treturn CompareDomainName(parent, child) == CountLabel(parent)\n}\n\n// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.\n// The checking is performed on the binary payload.\nfunc IsMsg(buf []byte) error {\n\t// Header\n\tif len(buf) < headerSize {\n\t\treturn errors.New(\"dns: bad message header\")\n\t}\n\t// Header: Opcode\n\t// TODO(miek): more checks here, e.g. check all header bits.\n\treturn nil\n}\n\n// IsFqdn checks if a domain name is fully qualified.\nfunc IsFqdn(s string) bool {\n\t// Check for (and remove) a trailing dot, returning if there isn't one.\n\tif s == \"\" || s[len(s)-1] != '.' {\n\t\treturn false\n\t}\n\ts = s[:len(s)-1]\n\n\t// If we don't have an escape sequence before the final dot, we know it's\n\t// fully qualified and can return here.\n\tif s == \"\" || s[len(s)-1] != '\\\\' {\n\t\treturn true\n\t}\n\n\t// Otherwise we have to check if the dot is escaped or not by checking if\n\t// there are an odd or even number of escape sequences before the dot.\n\ti := strings.LastIndexFunc(s, func(r rune) bool {\n\t\treturn r != '\\\\'\n\t})\n\treturn (len(s)-i)%2 != 0\n}\n\n// IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181.\n// This means the RRs need to have the same type, name, and class.\nfunc IsRRset(rrset []RR) bool {\n\tif len(rrset) == 0 {\n\t\treturn false\n\t}\n\n\tbaseH := rrset[0].Header()\n\tfor _, rr := range rrset[1:] {\n\t\tcurH := rr.Header()\n\t\tif curH.Rrtype != baseH.Rrtype || curH.Class != baseH.Class || curH.Name != baseH.Name {\n\t\t\t// Mismatch between the records, so this is not a valid rrset for\n\t\t\t// signing/verifying\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// Fqdn return the fully qualified domain name from s.\n// If s is already fully qualified, it behaves as the identity function.\nfunc Fqdn(s string) string {\n\tif IsFqdn(s) {\n\t\treturn s\n\t}\n\treturn s + \".\"\n}\n\n// CanonicalName returns the domain name in canonical form. A name in canonical\n// form is lowercase and fully qualified. Only US-ASCII letters are affected. See\n// Section 6.2 in RFC 4034.\nfunc CanonicalName(s string) string {\n\treturn strings.Map(func(r rune) rune {\n\t\tif r >= 'A' && r <= 'Z' {\n\t\t\tr += 'a' - 'A'\n\t\t}\n\t\treturn r\n\t}, Fqdn(s))\n}\n\n// Copied from the official Go code.\n\n// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP\n// address suitable for reverse DNS (PTR) record lookups or an error if it fails\n// to parse the IP address.\nfunc ReverseAddr(addr string) (arpa string, err error) {\n\tip := net.ParseIP(addr)\n\tif ip == nil {\n\t\treturn \"\", &Error{err: \"unrecognized address: \" + addr}\n\t}\n\tif v4 := ip.To4(); v4 != nil {\n\t\tbuf := make([]byte, 0, net.IPv4len*4+len(\"in-addr.arpa.\"))\n\t\t// Add it, in reverse, to the buffer\n\t\tfor i := len(v4) - 1; i >= 0; i-- {\n\t\t\tbuf = strconv.AppendInt(buf, int64(v4[i]), 10)\n\t\t\tbuf = append(buf, '.')\n\t\t}\n\t\t// Append \"in-addr.arpa.\" and return (buf already has the final .)\n\t\tbuf = append(buf, \"in-addr.arpa.\"...)\n\t\treturn string(buf), nil\n\t}\n\t// Must be IPv6\n\tbuf := make([]byte, 0, net.IPv6len*4+len(\"ip6.arpa.\"))\n\t// Add it, in reverse, to the buffer\n\tfor i := len(ip) - 1; i >= 0; i-- {\n\t\tv := ip[i]\n\t\tbuf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.')\n\t}\n\t// Append \"ip6.arpa.\" and return (buf already has the final .)\n\tbuf = append(buf, \"ip6.arpa.\"...)\n\treturn string(buf), nil\n}\n\n// String returns the string representation for the type t.\nfunc (t Type) String() string {\n\tif t1, ok := TypeToString[uint16(t)]; ok {\n\t\treturn t1\n\t}\n\treturn \"TYPE\" + strconv.Itoa(int(t))\n}\n\n// String returns the string representation for the class c.\nfunc (c Class) String() string {\n\tif s, ok := ClassToString[uint16(c)]; ok {\n\t\t// Only emit mnemonics when they are unambiguous, specially ANY is in both.\n\t\tif _, ok := StringToType[s]; !ok {\n\t\t\treturn s\n\t\t}\n\t}\n\treturn \"CLASS\" + strconv.Itoa(int(c))\n}\n\n// String returns the string representation for the name n.\nfunc (n Name) String() string {\n\treturn sprintName(string(n))\n}\n"
  },
  {
    "path": "dns.go",
    "content": "package dns\n\nimport (\n\t\"encoding/hex\"\n\t\"strconv\"\n)\n\nconst (\n\tyear68     = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.\n\tdefaultTtl = 3600    // Default internal TTL.\n\n\t// DefaultMsgSize is the standard default for messages larger than 512 bytes.\n\tDefaultMsgSize = 4096\n\t// MinMsgSize is the minimal size of a DNS packet.\n\tMinMsgSize = 512\n\t// MaxMsgSize is the largest possible DNS packet.\n\tMaxMsgSize = 65535\n)\n\n// Error represents a DNS error.\ntype Error struct{ err string }\n\nfunc (e *Error) Error() string {\n\tif e == nil {\n\t\treturn \"dns: <nil>\"\n\t}\n\treturn \"dns: \" + e.err\n}\n\n// An RR represents a resource record.\ntype RR interface {\n\t// Header returns the header of an resource record. The header contains\n\t// everything up to the rdata.\n\tHeader() *RR_Header\n\t// String returns the text representation of the resource record.\n\tString() string\n\n\t// copy returns a copy of the RR\n\tcopy() RR\n\n\t// len returns the length (in octets) of the compressed or uncompressed RR in wire format.\n\t//\n\t// If compression is nil, the uncompressed size will be returned, otherwise the compressed\n\t// size will be returned and domain names will be added to the map for future compression.\n\tlen(off int, compression map[string]struct{}) int\n\n\t// pack packs the records RDATA into wire format. The header will\n\t// already have been packed into msg.\n\tpack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error)\n\n\t// unpack unpacks an RR from wire format.\n\t//\n\t// This will only be called on a new and empty RR type with only the header populated. It\n\t// will only be called if the record's RDATA is non-empty.\n\tunpack(msg []byte, off int) (off1 int, err error)\n\n\t// parse parses an RR from zone file format.\n\t//\n\t// This will only be called on a new and empty RR type with only the header populated.\n\tparse(c *zlexer, origin string) *ParseError\n\n\t// isDuplicate returns whether the two RRs are duplicates.\n\tisDuplicate(r2 RR) bool\n}\n\n// RR_Header is the header all DNS resource records share.\ntype RR_Header struct {\n\tName     string `dns:\"cdomain-name\"`\n\tRrtype   uint16\n\tClass    uint16\n\tTtl      uint32\n\tRdlength uint16 // Length of data after header.\n}\n\n// Header returns itself. This is here to make RR_Header implements the RR interface.\nfunc (h *RR_Header) Header() *RR_Header { return h }\n\n// Just to implement the RR interface.\nfunc (h *RR_Header) copy() RR { return nil }\n\nfunc (h *RR_Header) String() string {\n\tvar s string\n\n\tif h.Rrtype == TypeOPT {\n\t\ts = \";\"\n\t\t// and maybe other things\n\t}\n\n\ts += sprintName(h.Name) + \"\\t\"\n\ts += strconv.FormatInt(int64(h.Ttl), 10) + \"\\t\"\n\ts += Class(h.Class).String() + \"\\t\"\n\ts += Type(h.Rrtype).String() + \"\\t\"\n\treturn s\n}\n\nfunc (h *RR_Header) len(off int, compression map[string]struct{}) int {\n\tl := domainNameLen(h.Name, off, compression, true)\n\tl += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)\n\treturn l\n}\n\nfunc (h *RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\t// RR_Header has no RDATA to pack.\n\treturn off, nil\n}\n\nfunc (h *RR_Header) unpack(msg []byte, off int) (int, error) {\n\tpanic(\"dns: internal error: unpack should never be called on RR_Header\")\n}\n\nfunc (h *RR_Header) parse(c *zlexer, origin string) *ParseError {\n\tpanic(\"dns: internal error: parse should never be called on RR_Header\")\n}\n\n// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.\nfunc (rr *RFC3597) ToRFC3597(r RR) error {\n\tbuf := make([]byte, Len(r))\n\theaderEnd, off, err := packRR(r, buf, 0, compressionMap{}, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuf = buf[:off]\n\n\t*rr = RFC3597{Hdr: *r.Header()}\n\trr.Hdr.Rdlength = uint16(off - headerEnd)\n\n\tif noRdata(rr.Hdr) {\n\t\treturn nil\n\t}\n\n\t_, err = rr.unpack(buf, headerEnd)\n\treturn err\n}\n\n// fromRFC3597 converts an unknown RR representation from RFC 3597 to the known RR type.\nfunc (rr *RFC3597) fromRFC3597(r RR) error {\n\thdr := r.Header()\n\t*hdr = rr.Hdr\n\n\t// Can't overflow uint16 as the length of Rdata is validated in (*RFC3597).parse.\n\t// We can only get here when rr was constructed with that method.\n\thdr.Rdlength = uint16(hex.DecodedLen(len(rr.Rdata)))\n\n\tif noRdata(*hdr) {\n\t\t// Dynamic update.\n\t\treturn nil\n\t}\n\n\t// rr.pack requires an extra allocation and a copy so we just decode Rdata\n\t// manually, it's simpler anyway.\n\tmsg, err := hex.DecodeString(rr.Rdata)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = r.unpack(msg, 0)\n\treturn err\n}\n"
  },
  {
    "path": "dns_bench_test.go",
    "content": "package dns\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n)\n\nfunc BenchmarkMsgLength(b *testing.B) {\n\tb.StopTimer()\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tmsg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmsg.Len()\n\t}\n}\n\nfunc BenchmarkMsgLengthNoCompression(b *testing.B) {\n\tb.StopTimer()\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\treturn msg\n\t}\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tmsg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmsg.Len()\n\t}\n}\n\nfunc BenchmarkMsgLengthPack(b *testing.B) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tmsg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = msg.Pack()\n\t}\n}\n\nfunc BenchmarkMsgLengthMassive(b *testing.B) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\tconst name1 = \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tanswer := []RR{rrMx, rrMx}\n\tfor i := 0; i < 128; i++ {\n\t\trrA := testRR(fmt.Sprintf(\"example%03d.something%03delse.org. 2311 IN A 127.0.0.1\", i/32, i%32))\n\t\tanswer = append(answer, rrA)\n\t}\n\tanswer = append(answer, rrMx, rrMx)\n\tmsg := makeMsg(name1, answer, nil, nil)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmsg.Len()\n\t}\n}\n\nfunc BenchmarkMsgLengthOnlyQuestion(b *testing.B) {\n\tmsg := new(Msg)\n\tmsg.SetQuestion(Fqdn(\"12345678901234567890123456789012345.12345678.123.\"), TypeANY)\n\tmsg.Compress = true\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmsg.Len()\n\t}\n}\n\nfunc BenchmarkMsgLengthEscapedName(b *testing.B) {\n\tmsg := new(Msg)\n\tmsg.SetQuestion(`\\1\\2\\3\\4\\5\\6\\7\\8\\9\\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\0\\1\\2\\3\\4\\5.\\1\\2\\3\\4\\5\\6\\7\\8.\\1\\2\\3.`, TypeANY)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmsg.Len()\n\t}\n}\n\nfunc BenchmarkPackDomainName(b *testing.B) {\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\tbuf := make([]byte, len(name1)+1)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = PackDomainName(name1, buf, 0, nil, false)\n\t}\n}\n\nfunc BenchmarkUnpackDomainName(b *testing.B) {\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\tbuf := make([]byte, len(name1)+1)\n\t_, _ = PackDomainName(name1, buf, 0, nil, false)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackDomainName(buf, 0)\n\t}\n}\n\nfunc BenchmarkUnpackDomainNameUnprintable(b *testing.B) {\n\tname1 := \"\\x02\\x02\\x02\\x025\\x02\\x02\\x02\\x02.12345678.123.\"\n\tbuf := make([]byte, len(name1)+1)\n\t_, _ = PackDomainName(name1, buf, 0, nil, false)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackDomainName(buf, 0)\n\t}\n}\n\nfunc BenchmarkUnpackDomainNameLongest(b *testing.B) {\n\tbuf := make([]byte, len(longestDomain)+1)\n\tn, err := PackDomainName(longestDomain, buf, 0, nil, false)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tif n != maxDomainNameWireOctets {\n\t\tb.Fatalf(\"name wrong size in wire format, expected %d, got %d\", maxDomainNameWireOctets, n)\n\t}\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackDomainName(buf, 0)\n\t}\n}\n\nfunc BenchmarkUnpackDomainNameLongestUnprintable(b *testing.B) {\n\tbuf := make([]byte, len(longestUnprintableDomain)+1)\n\tn, err := PackDomainName(longestUnprintableDomain, buf, 0, nil, false)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tif n != maxDomainNameWireOctets {\n\t\tb.Fatalf(\"name wrong size in wire format, expected %d, got %d\", maxDomainNameWireOctets, n)\n\t}\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackDomainName(buf, 0)\n\t}\n}\n\nfunc BenchmarkCopy(b *testing.B) {\n\tb.ReportAllocs()\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeA)\n\trr := testRR(\"miek.nl. 2311 IN A 127.0.0.1\")\n\tm.Answer = []RR{rr}\n\trr = testRR(\"miek.nl. 2311 IN NS 127.0.0.1\")\n\tm.Ns = []RR{rr}\n\trr = testRR(\"miek.nl. 2311 IN A 127.0.0.1\")\n\tm.Extra = []RR{rr}\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tm.Copy()\n\t}\n}\n\nfunc BenchmarkPackA(b *testing.B) {\n\ta := &A{Hdr: RR_Header{Name: \".\", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}\n\n\tbuf := make([]byte, Len(a))\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = PackRR(a, buf, 0, nil, false)\n\t}\n}\n\nfunc BenchmarkUnpackA(b *testing.B) {\n\ta := &A{Hdr: RR_Header{Name: \".\", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}\n\n\tbuf := make([]byte, Len(a))\n\tPackRR(a, buf, 0, nil, false)\n\ta = nil\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackRR(buf, 0)\n\t}\n}\n\nfunc BenchmarkPackMX(b *testing.B) {\n\tm := &MX{Hdr: RR_Header{Name: \".\", Rrtype: TypeA, Class: ClassANY}, Mx: \"mx.miek.nl.\"}\n\n\tbuf := make([]byte, Len(m))\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = PackRR(m, buf, 0, nil, false)\n\t}\n}\n\nfunc BenchmarkUnpackMX(b *testing.B) {\n\tm := &MX{Hdr: RR_Header{Name: \".\", Rrtype: TypeA, Class: ClassANY}, Mx: \"mx.miek.nl.\"}\n\n\tbuf := make([]byte, Len(m))\n\tPackRR(m, buf, 0, nil, false)\n\tm = nil\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackRR(buf, 0)\n\t}\n}\n\nfunc BenchmarkPackAAAAA(b *testing.B) {\n\taaaa := testRR(\". IN AAAA ::1\")\n\n\tbuf := make([]byte, Len(aaaa))\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = PackRR(aaaa, buf, 0, nil, false)\n\t}\n}\n\nfunc BenchmarkUnpackAAAA(b *testing.B) {\n\taaaa := testRR(\". IN AAAA ::1\")\n\n\tbuf := make([]byte, Len(aaaa))\n\tPackRR(aaaa, buf, 0, nil, false)\n\taaaa = nil\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, _ = UnpackRR(buf, 0)\n\t}\n}\n\nfunc BenchmarkPackMsg(b *testing.B) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tmsg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)\n\tbuf := make([]byte, 512)\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = msg.PackBuffer(buf)\n\t}\n}\n\nfunc BenchmarkPackMsgMassive(b *testing.B) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\tconst name1 = \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tanswer := []RR{rrMx, rrMx}\n\tfor i := 0; i < 128; i++ {\n\t\trrA := testRR(fmt.Sprintf(\"example%03d.something%03delse.org. 2311 IN A 127.0.0.1\", i/32, i%32))\n\t\tanswer = append(answer, rrA)\n\t}\n\tanswer = append(answer, rrMx, rrMx)\n\tmsg := makeMsg(name1, answer, nil, nil)\n\tbuf := make([]byte, 512)\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = msg.PackBuffer(buf)\n\t}\n}\n\nfunc BenchmarkPackMsgOnlyQuestion(b *testing.B) {\n\tmsg := new(Msg)\n\tmsg.SetQuestion(Fqdn(\"12345678901234567890123456789012345.12345678.123.\"), TypeANY)\n\tmsg.Compress = true\n\tbuf := make([]byte, 512)\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = msg.PackBuffer(buf)\n\t}\n}\n\nfunc BenchmarkUnpackMsg(b *testing.B) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\tmsg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)\n\tmsgBuf, _ := msg.Pack()\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_ = msg.Unpack(msgBuf)\n\t}\n}\n\nfunc BenchmarkIdGeneration(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_ = id()\n\t}\n}\n\nfunc BenchmarkReverseAddr(b *testing.B) {\n\tb.Run(\"IP4\", func(b *testing.B) {\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\taddr, err := ReverseAddr(\"192.0.2.1\")\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t\tif expect := \"1.2.0.192.in-addr.arpa.\"; addr != expect {\n\t\t\t\tb.Fatalf(\"invalid reverse address, expected %q, got %q\", expect, addr)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"IP6\", func(b *testing.B) {\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\taddr, err := ReverseAddr(\"2001:db8::68\")\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t\tif expect := \"8.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.\"; addr != expect {\n\t\t\t\tb.Fatalf(\"invalid reverse address, expected %q, got %q\", expect, addr)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "dns_test.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"net\"\n\t\"testing\"\n)\n\nfunc TestPackUnpack(t *testing.T) {\n\tout := new(Msg)\n\tout.Answer = make([]RR, 1)\n\tkey := &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1}\n\tkey.Hdr = RR_Header{Name: \"miek.nl.\", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}\n\tkey.PublicKey = \"AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ\"\n\n\tout.Answer[0] = key\n\tmsg, err := out.Pack()\n\tif err != nil {\n\t\tt.Error(\"failed to pack msg with DNSKEY\")\n\t}\n\tin := new(Msg)\n\tif in.Unpack(msg) != nil {\n\t\tt.Error(\"failed to unpack msg with DNSKEY\")\n\t}\n\n\tsig := &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2,\n\t\tOrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: \"miek.nl.\",\n\t\tSignature: \"AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ\"}\n\tsig.Hdr = RR_Header{Name: \"miek.nl.\", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}\n\n\tout.Answer[0] = sig\n\tmsg, err = out.Pack()\n\tif err != nil {\n\t\tt.Error(\"failed to pack msg with RRSIG\")\n\t}\n\n\tif in.Unpack(msg) != nil {\n\t\tt.Error(\"failed to unpack msg with RRSIG\")\n\t}\n}\n\nfunc TestPackUnpack2(t *testing.T) {\n\tm := new(Msg)\n\tm.Extra = make([]RR, 1)\n\tm.Answer = make([]RR, 1)\n\tdom := \"miek.nl.\"\n\trr := new(A)\n\trr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}\n\trr.A = net.IPv4(127, 0, 0, 1)\n\n\tx := new(TXT)\n\tx.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}\n\tx.Txt = []string{\"heelalaollo\"}\n\n\tm.Extra[0] = x\n\tm.Answer[0] = rr\n\t_, err := m.Pack()\n\tif err != nil {\n\t\tt.Error(\"Packing failed: \", err)\n\t\treturn\n\t}\n}\n\nfunc TestPackUnpack3(t *testing.T) {\n\tm := new(Msg)\n\tm.Extra = make([]RR, 2)\n\tm.Answer = make([]RR, 1)\n\tdom := \"miek.nl.\"\n\trr := new(A)\n\trr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}\n\trr.A = net.IPv4(127, 0, 0, 1)\n\n\tx1 := new(TXT)\n\tx1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}\n\tx1.Txt = []string{}\n\n\tx2 := new(TXT)\n\tx2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}\n\tx2.Txt = []string{\"heelalaollo\"}\n\n\tm.Extra[0] = x1\n\tm.Extra[1] = x2\n\tm.Answer[0] = rr\n\tb, err := m.Pack()\n\tif err != nil {\n\t\tt.Error(\"packing failed: \", err)\n\t\treturn\n\t}\n\n\tvar unpackMsg Msg\n\terr = unpackMsg.Unpack(b)\n\tif err != nil {\n\t\tt.Error(\"unpacking failed\")\n\t\treturn\n\t}\n}\n\nfunc TestBailiwick(t *testing.T) {\n\tyes := map[string]string{\n\t\t\"miek1.nl\": \"miek1.nl\",\n\t\t\"miek.nl\":  \"ns.miek.nl\",\n\t\t\".\":        \"miek.nl\",\n\t}\n\tfor parent, child := range yes {\n\t\tif !IsSubDomain(parent, child) {\n\t\t\tt.Errorf(\"%s should be child of %s\", child, parent)\n\t\t\tt.Errorf(\"comparelabels %d\", CompareDomainName(parent, child))\n\t\t\tt.Errorf(\"lenlabels %d %d\", CountLabel(parent), CountLabel(child))\n\t\t}\n\t}\n\tno := map[string]string{\n\t\t\"www.miek.nl\":  \"ns.miek.nl\",\n\t\t\"m\\\\.iek.nl\":   \"ns.miek.nl\",\n\t\t\"w\\\\.iek.nl\":   \"w.iek.nl\",\n\t\t\"p\\\\\\\\.iek.nl\": \"ns.p.iek.nl\", // p\\\\.iek.nl , literal \\ in domain name\n\t\t\"miek.nl\":      \".\",\n\t}\n\tfor parent, child := range no {\n\t\tif IsSubDomain(parent, child) {\n\t\t\tt.Errorf(\"%s should not be child of %s\", child, parent)\n\t\t\tt.Errorf(\"comparelabels %d\", CompareDomainName(parent, child))\n\t\t\tt.Errorf(\"lenlabels %d %d\", CountLabel(parent), CountLabel(child))\n\t\t}\n\t}\n}\n\nfunc TestPackNAPTR(t *testing.T) {\n\tfor _, n := range []string{\n\t\t`apple.com. IN NAPTR   100 50 \"se\" \"SIP+D2U\" \"\" _sip._udp.apple.com.`,\n\t\t`apple.com. IN NAPTR   90 50 \"se\" \"SIP+D2T\" \"\" _sip._tcp.apple.com.`,\n\t\t`apple.com. IN NAPTR   50 50 \"se\" \"SIPS+D2T\" \"\" _sips._tcp.apple.com.`,\n\t} {\n\t\trr := testRR(n)\n\t\tmsg := make([]byte, Len(rr))\n\t\tif off, err := PackRR(rr, msg, 0, nil, false); err != nil {\n\t\t\tt.Errorf(\"packing failed: %v\", err)\n\t\t\tt.Errorf(\"length %d, need more than %d\", Len(rr), off)\n\t\t}\n\t}\n}\n\nfunc TestToRFC3597(t *testing.T) {\n\ta := testRR(\"miek.nl. IN A 10.0.1.1\")\n\tx := new(RFC3597)\n\tx.ToRFC3597(a)\n\tif x.String() != `miek.nl.\t3600\tCLASS1\tTYPE1\t\\# 4 0a000101` {\n\t\tt.Errorf(\"string mismatch, got: %s\", x)\n\t}\n\n\tb := testRR(\"miek.nl. IN MX 10 mx.miek.nl.\")\n\tx.ToRFC3597(b)\n\tif x.String() != `miek.nl.\t3600\tCLASS1\tTYPE15\t\\# 14 000a026d78046d69656b026e6c00` {\n\t\tt.Errorf(\"string mismatch, got: %s\", x)\n\t}\n}\n\nfunc TestNoRdataPack(t *testing.T) {\n\tdata := make([]byte, 1024)\n\tfor typ, fn := range TypeToRR {\n\t\tr := fn()\n\t\t*r.Header() = RR_Header{Name: \"miek.nl.\", Rrtype: typ, Class: ClassINET, Ttl: 16}\n\t\t_, err := PackRR(r, data, 0, nil, false)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to pack RR with zero rdata: %s: %v\", TypeToString[typ], err)\n\t\t}\n\t}\n}\n\nfunc TestNoRdataUnpack(t *testing.T) {\n\tdata := make([]byte, 1024)\n\tfor typ, fn := range TypeToRR {\n\t\tif typ == TypeSOA || typ == TypeTSIG || typ == TypeTKEY {\n\t\t\t// SOA, TSIG will not be seen (like this) in dyn. updates?\n\t\t\t// TKEY requires length fields to be present for the Key and OtherData fields\n\t\t\tcontinue\n\t\t}\n\t\tr := fn()\n\t\t*r.Header() = RR_Header{Name: \"miek.nl.\", Rrtype: typ, Class: ClassINET, Ttl: 16}\n\t\toff, err := PackRR(r, data, 0, nil, false)\n\t\tif err != nil {\n\t\t\t// Should always works, TestNoDataPack should have caught this\n\t\t\tt.Errorf(\"failed to pack RR: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif _, _, err := UnpackRR(data[:off], 0); err != nil {\n\t\t\tt.Errorf(\"failed to unpack RR with zero rdata: %s: %v\", TypeToString[typ], err)\n\t\t}\n\t}\n}\n\nfunc TestRdataOverflow(t *testing.T) {\n\trr := new(RFC3597)\n\trr.Hdr.Name = \".\"\n\trr.Hdr.Class = ClassINET\n\trr.Hdr.Rrtype = 65280\n\trr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF))\n\tbuf := make([]byte, 0xFFFF*2)\n\tif _, err := PackRR(rr, buf, 0, nil, false); err != nil {\n\t\tt.Fatalf(\"maximum size rrdata pack failed: %v\", err)\n\t}\n\trr.Rdata += \"00\"\n\tif _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata {\n\t\tt.Fatalf(\"oversize rrdata pack didn't return ErrRdata - instead: %v\", err)\n\t}\n}\n\nfunc TestCopy(t *testing.T) {\n\trr := testRR(\"miek.nl. 2311 IN A 127.0.0.1\") // Weird TTL to avoid catching TTL\n\trr1 := Copy(rr)\n\tif rr.String() != rr1.String() {\n\t\tt.Fatalf(\"Copy() failed %s != %s\", rr.String(), rr1.String())\n\t}\n}\n\nfunc TestMsgCopy(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeA)\n\trr := testRR(\"miek.nl. 2311 IN A 127.0.0.1\")\n\tm.Answer = []RR{rr}\n\trr = testRR(\"miek.nl. 2311 IN NS 127.0.0.1\")\n\tm.Ns = []RR{rr}\n\n\tm1 := m.Copy()\n\tif m.String() != m1.String() {\n\t\tt.Fatalf(\"Msg.Copy() failed %s != %s\", m.String(), m1.String())\n\t}\n\n\tm1.Answer[0] = testRR(\"somethingelse.nl. 2311 IN A 127.0.0.1\")\n\tif m.String() == m1.String() {\n\t\tt.Fatalf(\"Msg.Copy() failed; change to copy changed template %s\", m.String())\n\t}\n\n\trr = testRR(\"miek.nl. 2311 IN A 127.0.0.2\")\n\tm1.Answer = append(m1.Answer, rr)\n\tif m1.Ns[0].String() == m1.Answer[1].String() {\n\t\tt.Fatalf(\"Msg.Copy() failed; append changed underlying array %s\", m1.Ns[0].String())\n\t}\n}\n\nfunc TestMsgPackBuffer(t *testing.T) {\n\tvar testMessages = []string{\n\t\t// news.ycombinator.com.in.escapemg.com.\tIN\tA, response\n\t\t\"586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10\",\n\n\t\t// news.ycombinator.com.in.escapemg.com.\tIN\tA, question\n\t\t\"586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001\",\n\n\t\t\"398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001\",\n\t}\n\n\tfor i, hexData := range testMessages {\n\t\t// we won't fail the decoding of the hex\n\t\tinput, _ := hex.DecodeString(hexData)\n\t\tm := new(Msg)\n\t\tif err := m.Unpack(input); err != nil {\n\t\t\tt.Errorf(\"packet %d failed to unpack\", i)\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\n// Make sure we can decode a TKEY packet from the string, modify the RR, and then pack it again.\nfunc TestTKEY(t *testing.T) {\n\t// An example TKEY RR captured.  There is no known accepted standard text format for a TKEY\n\t// record so we do this from a hex string instead of from a text readable string.\n\ttkeyStr := \"0737362d6d732d370932322d3332633233332463303439663961662d633065612d313165372d363839362d6463333937396666656666640000f900ff0000000000d2086773732d747369670059fd01f359fe53730003000000b8a181b53081b2a0030a0100a10b06092a864882f712010202a2819d04819a60819706092a864886f71201020202006f8187308184a003020105a10302010fa2783076a003020112a26f046db29b1b1d2625da3b20b49dafef930dd1e9aad335e1c5f45dcd95e0005d67a1100f3e573d70506659dbed064553f1ab890f68f65ae10def0dad5b423b39f240ebe666f2886c5fe03819692d29182bbed87b83e1f9d16b7334ec16a3c4fc5ad4a990088e0be43f0c6957916f5fe60000\"\n\ttkeyBytes, err := hex.DecodeString(tkeyStr)\n\tif err != nil {\n\t\tt.Fatal(\"unable to decode TKEY string \", err)\n\t}\n\t// Decode the RR\n\trr, tkeyLen, unPackErr := UnpackRR(tkeyBytes, 0)\n\tif unPackErr != nil {\n\t\tt.Fatal(\"unable to decode TKEY RR\", unPackErr)\n\t}\n\t// Make sure it's a TKEY record\n\tif rr.Header().Rrtype != TypeTKEY {\n\t\tt.Fatal(\"Unable to decode TKEY\")\n\t}\n\t// Make sure we get back the same length\n\tif Len(rr) != len(tkeyBytes) {\n\t\tt.Fatalf(\"Lengths don't match %d != %d\", Len(rr), len(tkeyBytes))\n\t}\n\t// make space for it with some fudge room\n\tmsg := make([]byte, tkeyLen+1000)\n\toffset, packErr := PackRR(rr, msg, 0, nil, false)\n\tif packErr != nil {\n\t\tt.Fatal(\"unable to pack TKEY RR\", packErr)\n\t}\n\tif offset != len(tkeyBytes) {\n\t\tt.Fatalf(\"mismatched TKEY RR size %d != %d\", len(tkeyBytes), offset)\n\t}\n\tif !bytes.Equal(tkeyBytes, msg[0:offset]) {\n\t\tt.Fatal(\"mismatched TKEY data after rewriting bytes\")\n\t}\n\n\t// Now add some bytes to this and make sure we can encode OtherData properly\n\ttkey := rr.(*TKEY)\n\ttkey.OtherData = \"abcd\"\n\ttkey.OtherLen = 2\n\toffset, packErr = PackRR(tkey, msg, 0, nil, false)\n\tif packErr != nil {\n\t\tt.Fatal(\"unable to pack TKEY RR after modification\", packErr)\n\t}\n\tif offset != len(tkeyBytes)+2 {\n\t\tt.Fatalf(\"mismatched TKEY RR size %d != %d\", offset, len(tkeyBytes)+2)\n\t}\n\n\t// Make sure we can parse our string output\n\ttkey.Hdr.Class = ClassINET // https://github.com/miekg/dns/issues/577\n\t_, newError := NewRR(tkey.String())\n\tif newError != nil {\n\t\tt.Fatalf(\"unable to parse TKEY string: %s\", newError)\n\t}\n}\n\nvar (\n\tsinkBool   bool\n\tsinkString string\n)\n\nfunc BenchmarkIsFQDN(b *testing.B) {\n\tb.Run(\"no_dot\", func(b *testing.B) {\n\t\tvar r bool\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tr = IsFqdn(\"www.google.com\")\n\t\t}\n\t\tsinkBool = r\n\t})\n\tb.Run(\"unescaped\", func(b *testing.B) {\n\t\tvar r bool\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tr = IsFqdn(\"www.google.com.\")\n\t\t}\n\t\tsinkBool = r\n\t})\n\tb.Run(\"escaped\", func(b *testing.B) {\n\t\tvar r bool\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tr = IsFqdn(`www.google.com\\\\\\\\\\\\\\\\.`)\n\t\t}\n\t\tsinkBool = r\n\t})\n}\n\nfunc BenchmarkFQDN(b *testing.B) {\n\tb.Run(\"is_fqdn\", func(b *testing.B) {\n\t\tvar r string\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tr = Fqdn(\"www.google.com.\")\n\t\t}\n\t\tsinkString = r\n\t})\n\tb.Run(\"not_fqdn\", func(b *testing.B) {\n\t\tvar r string\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tr = Fqdn(\"www.google.com\")\n\t\t}\n\t\tsinkString = r\n\t})\n}\n"
  },
  {
    "path": "dnssec.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t_ \"crypto/sha1\"   // need its init function\n\t_ \"crypto/sha256\" // need its init function\n\t_ \"crypto/sha512\" // need its init function\n\t\"encoding/asn1\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"math/big\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n)\n\n// DNSSEC encryption algorithm codes.\nconst (\n\t_ uint8 = iota\n\tRSAMD5\n\tDH\n\tDSA\n\t_ // Skip 4, RFC 6725, section 2.1\n\tRSASHA1\n\tDSANSEC3SHA1\n\tRSASHA1NSEC3SHA1\n\tRSASHA256\n\t_ // Skip 9, RFC 6725, section 2.1\n\tRSASHA512\n\t_ // Skip 11, RFC 6725, section 2.1\n\tECCGOST\n\tECDSAP256SHA256\n\tECDSAP384SHA384\n\tED25519\n\tED448\n\tINDIRECT   uint8 = 252\n\tPRIVATEDNS uint8 = 253 // Private (experimental keys)\n\tPRIVATEOID uint8 = 254\n)\n\n// AlgorithmToString is a map of algorithm IDs to algorithm names.\nvar AlgorithmToString = map[uint8]string{\n\tRSAMD5:           \"RSAMD5\",\n\tDH:               \"DH\",\n\tDSA:              \"DSA\",\n\tRSASHA1:          \"RSASHA1\",\n\tDSANSEC3SHA1:     \"DSA-NSEC3-SHA1\",\n\tRSASHA1NSEC3SHA1: \"RSASHA1-NSEC3-SHA1\",\n\tRSASHA256:        \"RSASHA256\",\n\tRSASHA512:        \"RSASHA512\",\n\tECCGOST:          \"ECC-GOST\",\n\tECDSAP256SHA256:  \"ECDSAP256SHA256\",\n\tECDSAP384SHA384:  \"ECDSAP384SHA384\",\n\tED25519:          \"ED25519\",\n\tED448:            \"ED448\",\n\tINDIRECT:         \"INDIRECT\",\n\tPRIVATEDNS:       \"PRIVATEDNS\",\n\tPRIVATEOID:       \"PRIVATEOID\",\n}\n\n// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.\n// For newer algorithm that do their own hashing (i.e. ED25519) the returned value\n// is 0, implying no (external) hashing should occur. The non-exported identityHash is then\n// used.\nvar AlgorithmToHash = map[uint8]crypto.Hash{\n\tRSAMD5:           crypto.MD5, // Deprecated in RFC 6725\n\tDSA:              crypto.SHA1,\n\tRSASHA1:          crypto.SHA1,\n\tRSASHA1NSEC3SHA1: crypto.SHA1,\n\tRSASHA256:        crypto.SHA256,\n\tECDSAP256SHA256:  crypto.SHA256,\n\tECDSAP384SHA384:  crypto.SHA384,\n\tRSASHA512:        crypto.SHA512,\n\tED25519:          0,\n}\n\n// DNSSEC hashing algorithm codes.\nconst (\n\t_      uint8 = iota\n\tSHA1         // RFC 4034\n\tSHA256       // RFC 4509\n\tGOST94       // RFC 5933\n\tSHA384       // Experimental\n\tSHA512       // Experimental\n)\n\n// HashToString is a map of hash IDs to names.\nvar HashToString = map[uint8]string{\n\tSHA1:   \"SHA1\",\n\tSHA256: \"SHA256\",\n\tGOST94: \"GOST94\",\n\tSHA384: \"SHA384\",\n\tSHA512: \"SHA512\",\n}\n\n// DNSKEY flag values.\nconst (\n\tSEP    = 1\n\tREVOKE = 1 << 7\n\tZONE   = 1 << 8\n)\n\n// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.\ntype rrsigWireFmt struct {\n\tTypeCovered uint16\n\tAlgorithm   uint8\n\tLabels      uint8\n\tOrigTtl     uint32\n\tExpiration  uint32\n\tInception   uint32\n\tKeyTag      uint16\n\tSignerName  string `dns:\"domain-name\"`\n\t/* No Signature */\n}\n\n// Used for converting DNSKEY's rdata to wirefmt.\ntype dnskeyWireFmt struct {\n\tFlags     uint16\n\tProtocol  uint8\n\tAlgorithm uint8\n\tPublicKey string `dns:\"base64\"`\n\t/* Nothing is left out */\n}\n\n// KeyTag calculates the keytag (or key-id) of the DNSKEY.\nfunc (k *DNSKEY) KeyTag() uint16 {\n\tif k == nil {\n\t\treturn 0\n\t}\n\tvar keytag int\n\tswitch k.Algorithm {\n\tcase RSAMD5:\n\t\t// This algorithm has been deprecated, but keep this key-tag calculation.\n\t\t// Look at the bottom two bytes of the modules, which the last item in the pubkey.\n\t\t// See https://www.rfc-editor.org/errata/eid193 .\n\t\tmodulus, _ := fromBase64([]byte(k.PublicKey))\n\t\tif len(modulus) > 1 {\n\t\t\tx := binary.BigEndian.Uint16(modulus[len(modulus)-3:])\n\t\t\tkeytag = int(x)\n\t\t}\n\tdefault:\n\t\tkeywire := new(dnskeyWireFmt)\n\t\tkeywire.Flags = k.Flags\n\t\tkeywire.Protocol = k.Protocol\n\t\tkeywire.Algorithm = k.Algorithm\n\t\tkeywire.PublicKey = k.PublicKey\n\t\twire := make([]byte, DefaultMsgSize)\n\t\tn, err := packKeyWire(keywire, wire)\n\t\tif err != nil {\n\t\t\treturn 0\n\t\t}\n\t\twire = wire[:n]\n\t\tfor i, v := range wire {\n\t\t\tif i&1 != 0 {\n\t\t\t\tkeytag += int(v) // must be larger than uint32\n\t\t\t} else {\n\t\t\t\tkeytag += int(v) << 8\n\t\t\t}\n\t\t}\n\t\tkeytag += keytag >> 16 & 0xFFFF\n\t\tkeytag &= 0xFFFF\n\t}\n\treturn uint16(keytag)\n}\n\n// ToDS converts a DNSKEY record to a DS record.\nfunc (k *DNSKEY) ToDS(h uint8) *DS {\n\tif k == nil {\n\t\treturn nil\n\t}\n\tds := new(DS)\n\tds.Hdr.Name = k.Hdr.Name\n\tds.Hdr.Class = k.Hdr.Class\n\tds.Hdr.Rrtype = TypeDS\n\tds.Hdr.Ttl = k.Hdr.Ttl\n\tds.Algorithm = k.Algorithm\n\tds.DigestType = h\n\tds.KeyTag = k.KeyTag()\n\n\tkeywire := new(dnskeyWireFmt)\n\tkeywire.Flags = k.Flags\n\tkeywire.Protocol = k.Protocol\n\tkeywire.Algorithm = k.Algorithm\n\tkeywire.PublicKey = k.PublicKey\n\twire := make([]byte, DefaultMsgSize)\n\tn, err := packKeyWire(keywire, wire)\n\tif err != nil {\n\t\treturn nil\n\t}\n\twire = wire[:n]\n\n\towner := make([]byte, 255)\n\toff, err1 := PackDomainName(CanonicalName(k.Hdr.Name), owner, 0, nil, false)\n\tif err1 != nil {\n\t\treturn nil\n\t}\n\towner = owner[:off]\n\t// RFC4034:\n\t// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);\n\t// \"|\" denotes concatenation\n\t// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.\n\n\tvar hash crypto.Hash\n\tswitch h {\n\tcase SHA1:\n\t\thash = crypto.SHA1\n\tcase SHA256:\n\t\thash = crypto.SHA256\n\tcase SHA384:\n\t\thash = crypto.SHA384\n\tcase SHA512:\n\t\thash = crypto.SHA512\n\tdefault:\n\t\treturn nil\n\t}\n\n\ts := hash.New()\n\ts.Write(owner)\n\ts.Write(wire)\n\tds.Digest = hex.EncodeToString(s.Sum(nil))\n\treturn ds\n}\n\n// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.\nfunc (k *DNSKEY) ToCDNSKEY() *CDNSKEY {\n\tc := &CDNSKEY{DNSKEY: *k}\n\tc.Hdr = k.Hdr\n\tc.Hdr.Rrtype = TypeCDNSKEY\n\treturn c\n}\n\n// ToCDS converts a DS record to a CDS record.\nfunc (d *DS) ToCDS() *CDS {\n\tc := &CDS{DS: *d}\n\tc.Hdr = d.Hdr\n\tc.Hdr.Rrtype = TypeCDS\n\treturn c\n}\n\n// Sign signs an RRSet. The signature needs to be filled in with the values:\n// Inception, Expiration, KeyTag, SignerName and Algorithm.  The rest is copied\n// from the RRset. Sign returns a non-nill error when the signing went OK.\n// There is no check if RRSet is a proper (RFC 2181) RRSet.  If OrigTTL is non\n// zero, it is used as-is, otherwise the TTL of the RRset is used as the\n// OrigTTL.\nfunc (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {\n\th0 := rrset[0].Header()\n\trr.Hdr.Rrtype = TypeRRSIG\n\trr.Hdr.Name = h0.Name\n\trr.Hdr.Class = h0.Class\n\tif rr.OrigTtl == 0 { // If set don't override\n\t\trr.OrigTtl = h0.Ttl\n\t}\n\trr.TypeCovered = h0.Rrtype\n\trr.Labels = uint8(CountLabel(h0.Name))\n\n\tif strings.HasPrefix(h0.Name, \"*\") {\n\t\trr.Labels-- // wildcard, remove from label count\n\t}\n\n\treturn rr.signAsIs(k, rrset)\n}\n\nfunc (rr *RRSIG) signAsIs(k crypto.Signer, rrset []RR) error {\n\tif k == nil {\n\t\treturn ErrPrivKey\n\t}\n\t// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set\n\tif rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {\n\t\treturn ErrKey\n\t}\n\n\tsigwire := new(rrsigWireFmt)\n\tsigwire.TypeCovered = rr.TypeCovered\n\tsigwire.Algorithm = rr.Algorithm\n\tsigwire.Labels = rr.Labels\n\tsigwire.OrigTtl = rr.OrigTtl\n\tsigwire.Expiration = rr.Expiration\n\tsigwire.Inception = rr.Inception\n\tsigwire.KeyTag = rr.KeyTag\n\t// For signing, lowercase this name\n\tsigwire.SignerName = CanonicalName(rr.SignerName)\n\n\t// Create the desired binary blob\n\tsigndata := make([]byte, DefaultMsgSize)\n\tn, err := packSigWire(sigwire, signdata)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsigndata = signdata[:n]\n\twire, err := rawSignatureData(rrset, rr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\th, cryptohash, err := hashFromAlgorithm(rr.Algorithm)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch rr.Algorithm {\n\tcase RSAMD5, DSA, DSANSEC3SHA1:\n\t\t// See RFC 6944.\n\t\treturn ErrAlg\n\tdefault:\n\t\th.Write(signdata)\n\t\th.Write(wire)\n\n\t\tsignature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\trr.Signature = toBase64(signature)\n\t\treturn nil\n\t}\n}\n\nfunc sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {\n\tsignature, err := k.Sign(rand.Reader, hashed, hash)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch alg {\n\tcase RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, ED25519:\n\t\treturn signature, nil\n\tcase ECDSAP256SHA256, ECDSAP384SHA384:\n\t\tecdsaSignature := &struct {\n\t\t\tR, S *big.Int\n\t\t}{}\n\t\tif _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar intlen int\n\t\tswitch alg {\n\t\tcase ECDSAP256SHA256:\n\t\t\tintlen = 32\n\t\tcase ECDSAP384SHA384:\n\t\t\tintlen = 48\n\t\t}\n\n\t\tsignature := intToBytes(ecdsaSignature.R, intlen)\n\t\tsignature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)\n\t\treturn signature, nil\n\tdefault:\n\t\treturn nil, ErrAlg\n\t}\n}\n\n// Verify validates an RRSet with the signature and key. This is only the\n// cryptographic test, the signature validity period must be checked separately.\n// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.\n// It also checks that the Zone Key bit (RFC 4034 2.1.1) is set on the DNSKEY\n// and that the Protocol field is set to 3 (RFC 4034 2.1.2).\nfunc (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {\n\t// First the easy checks\n\tif !IsRRset(rrset) {\n\t\treturn ErrRRset\n\t}\n\tif rr.KeyTag != k.KeyTag() {\n\t\treturn ErrKey\n\t}\n\tif rr.Hdr.Class != k.Hdr.Class {\n\t\treturn ErrKey\n\t}\n\tif rr.Algorithm != k.Algorithm {\n\t\treturn ErrKey\n\t}\n\n\tsignerName := CanonicalName(rr.SignerName)\n\tif !equal(signerName, k.Hdr.Name) {\n\t\treturn ErrKey\n\t}\n\n\tif k.Protocol != 3 {\n\t\treturn ErrKey\n\t}\n\t// RFC 4034 2.1.1 If bit 7 has value 0, then the DNSKEY record holds some\n\t// other type of DNS public key and MUST NOT be used to verify RRSIGs that\n\t// cover RRsets.\n\tif k.Flags&ZONE == 0 {\n\t\treturn ErrKey\n\t}\n\n\t// IsRRset checked that we have at least one RR and that the RRs in\n\t// the set have consistent type, class, and name. Also check that type,\n\t// class and name matches the RRSIG record.\n\t// Also checks RFC 4035 5.3.1 the number of labels in the RRset owner\n\t// name MUST be greater than or equal to the value in the RRSIG RR's Labels field.\n\t// RFC 4035 5.3.1 Signer's Name MUST be the name of the zone that [contains the RRset].\n\t// Since we don't have SOA info, checking suffix may be the best we can do...?\n\tif h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class ||\n\t\th0.Rrtype != rr.TypeCovered ||\n\t\tuint8(CountLabel(h0.Name)) < rr.Labels ||\n\t\t!equal(h0.Name, rr.Hdr.Name) ||\n\t\t!strings.HasSuffix(CanonicalName(h0.Name), signerName) {\n\n\t\treturn ErrRRset\n\t}\n\n\t// RFC 4035 5.3.2.  Reconstructing the Signed Data\n\t// Copy the sig, except the rrsig data\n\tsigwire := new(rrsigWireFmt)\n\tsigwire.TypeCovered = rr.TypeCovered\n\tsigwire.Algorithm = rr.Algorithm\n\tsigwire.Labels = rr.Labels\n\tsigwire.OrigTtl = rr.OrigTtl\n\tsigwire.Expiration = rr.Expiration\n\tsigwire.Inception = rr.Inception\n\tsigwire.KeyTag = rr.KeyTag\n\tsigwire.SignerName = signerName\n\t// Create the desired binary blob\n\tsigneddata := make([]byte, DefaultMsgSize)\n\tn, err := packSigWire(sigwire, signeddata)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsigneddata = signeddata[:n]\n\twire, err := rawSignatureData(rrset, rr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsigbuf := rr.sigBuf() // Get the binary signature data\n\t// TODO(miek)\n\t// remove the domain name and assume its ours?\n\t// if rr.Algorithm == PRIVATEDNS { // PRIVATEOID\n\t// }\n\n\th, cryptohash, err := hashFromAlgorithm(rr.Algorithm)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch rr.Algorithm {\n\tcase RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:\n\t\t// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??\n\t\tpubkey := k.publicKeyRSA() // Get the key\n\t\tif pubkey == nil {\n\t\t\treturn ErrKey\n\t\t}\n\n\t\th.Write(signeddata)\n\t\th.Write(wire)\n\t\treturn rsa.VerifyPKCS1v15(pubkey, cryptohash, h.Sum(nil), sigbuf)\n\n\tcase ECDSAP256SHA256, ECDSAP384SHA384:\n\t\tpubkey := k.publicKeyECDSA()\n\t\tif pubkey == nil {\n\t\t\treturn ErrKey\n\t\t}\n\n\t\t// Split sigbuf into the r and s coordinates\n\t\tr := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])\n\t\ts := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])\n\n\t\th.Write(signeddata)\n\t\th.Write(wire)\n\t\tif ecdsa.Verify(pubkey, h.Sum(nil), r, s) {\n\t\t\treturn nil\n\t\t}\n\t\treturn ErrSig\n\n\tcase ED25519:\n\t\tpubkey := k.publicKeyED25519()\n\t\tif pubkey == nil {\n\t\t\treturn ErrKey\n\t\t}\n\n\t\tif ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) {\n\t\t\treturn nil\n\t\t}\n\t\treturn ErrSig\n\n\tdefault:\n\t\treturn ErrAlg\n\t}\n}\n\n// ValidityPeriod uses RFC1982 serial arithmetic to calculate\n// if a signature period is valid. If t is the zero time, the\n// current time is taken other t is. Returns true if the signature\n// is valid at the given time, otherwise returns false.\nfunc (rr *RRSIG) ValidityPeriod(t time.Time) bool {\n\tvar utc int64\n\tif t.IsZero() {\n\t\tutc = time.Now().UTC().Unix()\n\t} else {\n\t\tutc = t.UTC().Unix()\n\t}\n\tmodi := (int64(rr.Inception) - utc) / year68\n\tmode := (int64(rr.Expiration) - utc) / year68\n\tti := int64(rr.Inception) + modi*year68\n\tte := int64(rr.Expiration) + mode*year68\n\treturn ti <= utc && utc <= te\n}\n\n// Return the signatures base64 encoding sigdata as a byte slice.\nfunc (rr *RRSIG) sigBuf() []byte {\n\tsigbuf, err := fromBase64([]byte(rr.Signature))\n\tif err != nil {\n\t\treturn nil\n\t}\n\treturn sigbuf\n}\n\n// publicKeyRSA returns the RSA public key from a DNSKEY record.\nfunc (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {\n\tkeybuf, err := fromBase64([]byte(k.PublicKey))\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tif len(keybuf) < 1+1+64 {\n\t\t// Exponent must be at least 1 byte and modulus at least 64\n\t\treturn nil\n\t}\n\n\t// RFC 2537/3110, section 2. RSA Public KEY Resource Records\n\t// Length is in the 0th byte, unless its zero, then it\n\t// it in bytes 1 and 2 and its a 16 bit number\n\texplen := uint16(keybuf[0])\n\tkeyoff := 1\n\tif explen == 0 {\n\t\texplen = uint16(keybuf[1])<<8 | uint16(keybuf[2])\n\t\tkeyoff = 3\n\t}\n\n\tif explen > 4 || explen == 0 || keybuf[keyoff] == 0 {\n\t\t// Exponent larger than supported by the crypto package,\n\t\t// empty, or contains prohibited leading zero.\n\t\treturn nil\n\t}\n\n\tmodoff := keyoff + int(explen)\n\tmodlen := len(keybuf) - modoff\n\tif modlen < 64 || modlen > 512 || keybuf[modoff] == 0 {\n\t\t// Modulus is too small, large, or contains prohibited leading zero.\n\t\treturn nil\n\t}\n\n\tpubkey := new(rsa.PublicKey)\n\n\tvar expo uint64\n\t// The exponent of length explen is between keyoff and modoff.\n\tfor _, v := range keybuf[keyoff:modoff] {\n\t\texpo <<= 8\n\t\texpo |= uint64(v)\n\t}\n\tif expo > 1<<31-1 {\n\t\t// Larger exponent than supported by the crypto package.\n\t\treturn nil\n\t}\n\n\tpubkey.E = int(expo)\n\tpubkey.N = new(big.Int).SetBytes(keybuf[modoff:])\n\treturn pubkey\n}\n\n// publicKeyECDSA returns the Curve public key from the DNSKEY record.\nfunc (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {\n\tkeybuf, err := fromBase64([]byte(k.PublicKey))\n\tif err != nil {\n\t\treturn nil\n\t}\n\tpubkey := new(ecdsa.PublicKey)\n\tswitch k.Algorithm {\n\tcase ECDSAP256SHA256:\n\t\tpubkey.Curve = elliptic.P256()\n\t\tif len(keybuf) != 64 {\n\t\t\t// wrongly encoded key\n\t\t\treturn nil\n\t\t}\n\tcase ECDSAP384SHA384:\n\t\tpubkey.Curve = elliptic.P384()\n\t\tif len(keybuf) != 96 {\n\t\t\t// Wrongly encoded key\n\t\t\treturn nil\n\t\t}\n\t}\n\tpubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2])\n\tpubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:])\n\treturn pubkey\n}\n\nfunc (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {\n\tkeybuf, err := fromBase64([]byte(k.PublicKey))\n\tif err != nil {\n\t\treturn nil\n\t}\n\tif len(keybuf) != ed25519.PublicKeySize {\n\t\treturn nil\n\t}\n\treturn keybuf\n}\n\ntype wireSlice [][]byte\n\nfunc (p wireSlice) Len() int      { return len(p) }\nfunc (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\nfunc (p wireSlice) Less(i, j int) bool {\n\t_, ioff, _ := UnpackDomainName(p[i], 0)\n\t_, joff, _ := UnpackDomainName(p[j], 0)\n\treturn bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0\n}\n\n// Return the raw signature data.\nfunc rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {\n\twires := make(wireSlice, len(rrset))\n\tfor i, r := range rrset {\n\t\tr1 := r.copy()\n\t\th := r1.Header()\n\t\th.Ttl = s.OrigTtl\n\t\tlabels := SplitDomainName(h.Name)\n\t\t// 6.2. Canonical RR Form. (4) - wildcards\n\t\tif len(labels) > int(s.Labels) {\n\t\t\t// Wildcard\n\t\t\th.Name = \"*.\" + strings.Join(labels[len(labels)-int(s.Labels):], \".\") + \".\"\n\t\t}\n\t\t// RFC 4034: 6.2.  Canonical RR Form. (2) - domain name to lowercase\n\t\th.Name = CanonicalName(h.Name)\n\t\t// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.\n\t\t//   NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,\n\t\t//   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,\n\t\t//   SRV, DNAME, A6\n\t\t//\n\t\t// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):\n\t\t//\tSection 6.2 of [RFC4034] also erroneously lists HINFO as a record\n\t\t//\tthat needs conversion to lowercase, and twice at that.  Since HINFO\n\t\t//\trecords contain no domain names, they are not subject to case\n\t\t//\tconversion.\n\t\tswitch x := r1.(type) {\n\t\tcase *NS:\n\t\t\tx.Ns = CanonicalName(x.Ns)\n\t\tcase *MD:\n\t\t\tx.Md = CanonicalName(x.Md)\n\t\tcase *MF:\n\t\t\tx.Mf = CanonicalName(x.Mf)\n\t\tcase *CNAME:\n\t\t\tx.Target = CanonicalName(x.Target)\n\t\tcase *SOA:\n\t\t\tx.Ns = CanonicalName(x.Ns)\n\t\t\tx.Mbox = CanonicalName(x.Mbox)\n\t\tcase *MB:\n\t\t\tx.Mb = CanonicalName(x.Mb)\n\t\tcase *MG:\n\t\t\tx.Mg = CanonicalName(x.Mg)\n\t\tcase *MR:\n\t\t\tx.Mr = CanonicalName(x.Mr)\n\t\tcase *PTR:\n\t\t\tx.Ptr = CanonicalName(x.Ptr)\n\t\tcase *MINFO:\n\t\t\tx.Rmail = CanonicalName(x.Rmail)\n\t\t\tx.Email = CanonicalName(x.Email)\n\t\tcase *MX:\n\t\t\tx.Mx = CanonicalName(x.Mx)\n\t\tcase *RP:\n\t\t\tx.Mbox = CanonicalName(x.Mbox)\n\t\t\tx.Txt = CanonicalName(x.Txt)\n\t\tcase *AFSDB:\n\t\t\tx.Hostname = CanonicalName(x.Hostname)\n\t\tcase *RT:\n\t\t\tx.Host = CanonicalName(x.Host)\n\t\tcase *SIG:\n\t\t\tx.SignerName = CanonicalName(x.SignerName)\n\t\tcase *PX:\n\t\t\tx.Map822 = CanonicalName(x.Map822)\n\t\t\tx.Mapx400 = CanonicalName(x.Mapx400)\n\t\tcase *NAPTR:\n\t\t\tx.Replacement = CanonicalName(x.Replacement)\n\t\tcase *KX:\n\t\t\tx.Exchanger = CanonicalName(x.Exchanger)\n\t\tcase *SRV:\n\t\t\tx.Target = CanonicalName(x.Target)\n\t\tcase *DNAME:\n\t\t\tx.Target = CanonicalName(x.Target)\n\t\t}\n\t\t// 6.2. Canonical RR Form. (5) - origTTL\n\t\twire := make([]byte, Len(r1)+1) // +1 to be safe(r)\n\t\toff, err1 := PackRR(r1, wire, 0, nil, false)\n\t\tif err1 != nil {\n\t\t\treturn nil, err1\n\t\t}\n\t\twire = wire[:off]\n\t\twires[i] = wire\n\t}\n\tsort.Sort(wires)\n\tfor i, wire := range wires {\n\t\tif i > 0 && bytes.Equal(wire, wires[i-1]) {\n\t\t\tcontinue\n\t\t}\n\t\tbuf = append(buf, wire...)\n\t}\n\treturn buf, nil\n}\n\nfunc packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {\n\t// copied from zmsg.go RRSIG packing\n\toff, err := packUint16(sw.TypeCovered, msg, 0)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(sw.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(sw.Labels, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(sw.OrigTtl, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(sw.Expiration, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(sw.Inception, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(sw.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = PackDomainName(sw.SignerName, msg, off, nil, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {\n\t// copied from zmsg.go DNSKEY packing\n\toff, err := packUint16(dw.Flags, msg, 0)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(dw.Protocol, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(dw.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(dw.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n"
  },
  {
    "path": "dnssec_keygen.go",
    "content": "package dns\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"math/big\"\n)\n\n// Generate generates a DNSKEY of the given bit size.\n// The public part is put inside the DNSKEY record.\n// The Algorithm in the key must be set as this will define\n// what kind of DNSKEY will be generated.\n// The ECDSA algorithms imply a fixed keysize, in that case\n// bits should be set to the size of the algorithm.\nfunc (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {\n\tswitch k.Algorithm {\n\tcase RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:\n\t\tif bits < 512 || bits > 4096 {\n\t\t\treturn nil, ErrKeySize\n\t\t}\n\tcase RSASHA512:\n\t\tif bits < 1024 || bits > 4096 {\n\t\t\treturn nil, ErrKeySize\n\t\t}\n\tcase ECDSAP256SHA256:\n\t\tif bits != 256 {\n\t\t\treturn nil, ErrKeySize\n\t\t}\n\tcase ECDSAP384SHA384:\n\t\tif bits != 384 {\n\t\t\treturn nil, ErrKeySize\n\t\t}\n\tcase ED25519:\n\t\tif bits != 256 {\n\t\t\treturn nil, ErrKeySize\n\t\t}\n\tdefault:\n\t\treturn nil, ErrAlg\n\t}\n\n\tswitch k.Algorithm {\n\tcase RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:\n\t\tpriv, err := rsa.GenerateKey(rand.Reader, bits)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tk.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)\n\t\treturn priv, nil\n\tcase ECDSAP256SHA256, ECDSAP384SHA384:\n\t\tvar c elliptic.Curve\n\t\tswitch k.Algorithm {\n\t\tcase ECDSAP256SHA256:\n\t\t\tc = elliptic.P256()\n\t\tcase ECDSAP384SHA384:\n\t\t\tc = elliptic.P384()\n\t\t}\n\t\tpriv, err := ecdsa.GenerateKey(c, rand.Reader)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tk.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)\n\t\treturn priv, nil\n\tcase ED25519:\n\t\tpub, priv, err := ed25519.GenerateKey(rand.Reader)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tk.setPublicKeyED25519(pub)\n\t\treturn priv, nil\n\tdefault:\n\t\treturn nil, ErrAlg\n\t}\n}\n\n// Set the public key (the value E and N)\nfunc (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool {\n\tif _E == 0 || _N == nil {\n\t\treturn false\n\t}\n\tbuf := exponentToBuf(_E)\n\tbuf = append(buf, _N.Bytes()...)\n\tk.PublicKey = toBase64(buf)\n\treturn true\n}\n\n// Set the public key for Elliptic Curves\nfunc (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool {\n\tif _X == nil || _Y == nil {\n\t\treturn false\n\t}\n\tvar intlen int\n\tswitch k.Algorithm {\n\tcase ECDSAP256SHA256:\n\t\tintlen = 32\n\tcase ECDSAP384SHA384:\n\t\tintlen = 48\n\t}\n\tk.PublicKey = toBase64(curveToBuf(_X, _Y, intlen))\n\treturn true\n}\n\n// Set the public key for Ed25519\nfunc (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool {\n\tif _K == nil {\n\t\treturn false\n\t}\n\tk.PublicKey = toBase64(_K)\n\treturn true\n}\n\n// Set the public key (the values E and N) for RSA\n// RFC 3110: Section 2. RSA Public KEY Resource Records\nfunc exponentToBuf(_E int) []byte {\n\tvar buf []byte\n\ti := big.NewInt(int64(_E)).Bytes()\n\tif len(i) < 256 {\n\t\tbuf = make([]byte, 1, 1+len(i))\n\t\tbuf[0] = uint8(len(i))\n\t} else {\n\t\tbuf = make([]byte, 3, 3+len(i))\n\t\tbuf[0] = 0\n\t\tbuf[1] = uint8(len(i) >> 8)\n\t\tbuf[2] = uint8(len(i))\n\t}\n\tbuf = append(buf, i...)\n\treturn buf\n}\n\n// Set the public key for X and Y for Curve. The two\n// values are just concatenated.\nfunc curveToBuf(_X, _Y *big.Int, intlen int) []byte {\n\tbuf := intToBytes(_X, intlen)\n\tbuf = append(buf, intToBytes(_Y, intlen)...)\n\treturn buf\n}\n"
  },
  {
    "path": "dnssec_keyscan.go",
    "content": "package dns\n\nimport (\n\t\"bufio\"\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/rsa\"\n\t\"io\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// NewPrivateKey returns a PrivateKey by parsing the string s.\n// s should be in the same form of the BIND private key files.\nfunc (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {\n\tif s == \"\" || s[len(s)-1] != '\\n' { // We need a closing newline\n\t\treturn k.ReadPrivateKey(strings.NewReader(s+\"\\n\"), \"\")\n\t}\n\treturn k.ReadPrivateKey(strings.NewReader(s), \"\")\n}\n\n// ReadPrivateKey reads a private key from the io.Reader q. The string file is\n// only used in error reporting.\n// The public key must be known, because some cryptographic algorithms embed\n// the public inside the privatekey.\nfunc (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {\n\tm, err := parseKey(q, file)\n\tif m == nil {\n\t\treturn nil, err\n\t}\n\tif _, ok := m[\"private-key-format\"]; !ok {\n\t\treturn nil, ErrPrivKey\n\t}\n\tif m[\"private-key-format\"] != \"v1.2\" && m[\"private-key-format\"] != \"v1.3\" {\n\t\treturn nil, ErrPrivKey\n\t}\n\t// TODO(mg): check if the pubkey matches the private key\n\talgoStr, _, _ := strings.Cut(m[\"algorithm\"], \" \")\n\talgo, err := strconv.ParseUint(algoStr, 10, 8)\n\tif err != nil {\n\t\treturn nil, ErrPrivKey\n\t}\n\tswitch uint8(algo) {\n\tcase RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:\n\t\tpriv, err := readPrivateKeyRSA(m)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpub := k.publicKeyRSA()\n\t\tif pub == nil {\n\t\t\treturn nil, ErrKey\n\t\t}\n\t\tpriv.PublicKey = *pub\n\t\treturn priv, nil\n\tcase ECDSAP256SHA256, ECDSAP384SHA384:\n\t\tpriv, err := readPrivateKeyECDSA(m)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpub := k.publicKeyECDSA()\n\t\tif pub == nil {\n\t\t\treturn nil, ErrKey\n\t\t}\n\t\tpriv.PublicKey = *pub\n\t\treturn priv, nil\n\tcase ED25519:\n\t\treturn readPrivateKeyED25519(m)\n\tdefault:\n\t\treturn nil, ErrAlg\n\t}\n}\n\n// Read a private key (file) string and create a public key. Return the private key.\nfunc readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {\n\tp := new(rsa.PrivateKey)\n\tp.Primes = []*big.Int{nil, nil}\n\tfor k, v := range m {\n\t\tswitch k {\n\t\tcase \"modulus\", \"publicexponent\", \"privateexponent\", \"prime1\", \"prime2\":\n\t\t\tv1, err := fromBase64([]byte(v))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tswitch k {\n\t\t\tcase \"modulus\":\n\t\t\t\tp.PublicKey.N = new(big.Int).SetBytes(v1)\n\t\t\tcase \"publicexponent\":\n\t\t\t\ti := new(big.Int).SetBytes(v1)\n\t\t\t\tp.PublicKey.E = int(i.Int64()) // int64 should be large enough\n\t\t\tcase \"privateexponent\":\n\t\t\t\tp.D = new(big.Int).SetBytes(v1)\n\t\t\tcase \"prime1\":\n\t\t\t\tp.Primes[0] = new(big.Int).SetBytes(v1)\n\t\t\tcase \"prime2\":\n\t\t\t\tp.Primes[1] = new(big.Int).SetBytes(v1)\n\t\t\t}\n\t\tcase \"exponent1\", \"exponent2\", \"coefficient\":\n\t\t\t// not used in Go (yet)\n\t\tcase \"created\", \"publish\", \"activate\":\n\t\t\t// not used in Go (yet)\n\t\t}\n\t}\n\treturn p, nil\n}\n\nfunc readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {\n\tp := new(ecdsa.PrivateKey)\n\tp.D = new(big.Int)\n\t// TODO: validate that the required flags are present\n\tfor k, v := range m {\n\t\tswitch k {\n\t\tcase \"privatekey\":\n\t\t\tv1, err := fromBase64([]byte(v))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tp.D.SetBytes(v1)\n\t\tcase \"created\", \"publish\", \"activate\":\n\t\t\t/* not used in Go (yet) */\n\t\t}\n\t}\n\treturn p, nil\n}\n\nfunc readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {\n\tvar p ed25519.PrivateKey\n\t// TODO: validate that the required flags are present\n\tfor k, v := range m {\n\t\tswitch k {\n\t\tcase \"privatekey\":\n\t\t\tp1, err := fromBase64([]byte(v))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif len(p1) != ed25519.SeedSize {\n\t\t\t\treturn nil, ErrPrivKey\n\t\t\t}\n\t\t\tp = ed25519.NewKeyFromSeed(p1)\n\t\tcase \"created\", \"publish\", \"activate\":\n\t\t\t/* not used in Go (yet) */\n\t\t}\n\t}\n\treturn p, nil\n}\n\n// parseKey reads a private key from r. It returns a map[string]string,\n// with the key-value pairs, or an error when the file is not correct.\nfunc parseKey(r io.Reader, file string) (map[string]string, error) {\n\tm := make(map[string]string)\n\tvar k string\n\n\tc := newKLexer(r)\n\n\tfor l, ok := c.Next(); ok; l, ok = c.Next() {\n\t\t// It should alternate\n\t\tswitch l.value {\n\t\tcase zKey:\n\t\t\tk = l.token\n\t\tcase zValue:\n\t\t\tif k == \"\" {\n\t\t\t\treturn nil, &ParseError{file: file, err: \"no private key seen\", lex: l}\n\t\t\t}\n\n\t\t\tm[strings.ToLower(k)] = l.token\n\t\t\tk = \"\"\n\t\t}\n\t}\n\n\t// Surface any read errors from r.\n\tif err := c.Err(); err != nil {\n\t\treturn nil, &ParseError{file: file, err: err.Error()}\n\t}\n\n\treturn m, nil\n}\n\ntype klexer struct {\n\tbr io.ByteReader\n\n\treadErr error\n\n\tline   int\n\tcolumn int\n\n\tkey bool\n\n\teol bool // end-of-line\n}\n\nfunc newKLexer(r io.Reader) *klexer {\n\tbr, ok := r.(io.ByteReader)\n\tif !ok {\n\t\tbr = bufio.NewReaderSize(r, 1024)\n\t}\n\n\treturn &klexer{\n\t\tbr: br,\n\n\t\tline: 1,\n\n\t\tkey: true,\n\t}\n}\n\nfunc (kl *klexer) Err() error {\n\tif kl.readErr == io.EOF {\n\t\treturn nil\n\t}\n\n\treturn kl.readErr\n}\n\n// readByte returns the next byte from the input\nfunc (kl *klexer) readByte() (byte, bool) {\n\tif kl.readErr != nil {\n\t\treturn 0, false\n\t}\n\n\tc, err := kl.br.ReadByte()\n\tif err != nil {\n\t\tkl.readErr = err\n\t\treturn 0, false\n\t}\n\n\t// delay the newline handling until the next token is delivered,\n\t// fixes off-by-one errors when reporting a parse error.\n\tif kl.eol {\n\t\tkl.line++\n\t\tkl.column = 0\n\t\tkl.eol = false\n\t}\n\n\tif c == '\\n' {\n\t\tkl.eol = true\n\t} else {\n\t\tkl.column++\n\t}\n\n\treturn c, true\n}\n\nfunc (kl *klexer) Next() (lex, bool) {\n\tvar (\n\t\tl lex\n\n\t\tstr strings.Builder\n\n\t\tcommt bool\n\t)\n\n\tfor x, ok := kl.readByte(); ok; x, ok = kl.readByte() {\n\t\tl.line, l.column = kl.line, kl.column\n\n\t\tswitch x {\n\t\tcase ':':\n\t\t\tif commt || !kl.key {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tkl.key = false\n\n\t\t\t// Next token is a space, eat it\n\t\t\tkl.readByte()\n\n\t\t\tl.value = zKey\n\t\t\tl.token = str.String()\n\t\t\treturn l, true\n\t\tcase ';':\n\t\t\tcommt = true\n\t\tcase '\\n':\n\t\t\tif commt {\n\t\t\t\t// Reset a comment\n\t\t\t\tcommt = false\n\t\t\t}\n\n\t\t\tif kl.key && str.Len() == 0 {\n\t\t\t\t// ignore empty lines\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tkl.key = true\n\n\t\t\tl.value = zValue\n\t\t\tl.token = str.String()\n\t\t\treturn l, true\n\t\tdefault:\n\t\t\tif commt {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tstr.WriteByte(x)\n\t\t}\n\t}\n\n\tif kl.readErr != nil && kl.readErr != io.EOF {\n\t\t// Don't return any tokens after a read error occurs.\n\t\treturn lex{value: zEOF}, false\n\t}\n\n\tif str.Len() > 0 {\n\t\t// Send remainder\n\t\tl.value = zValue\n\t\tl.token = str.String()\n\t\treturn l, true\n\t}\n\n\treturn lex{value: zEOF}, false\n}\n"
  },
  {
    "path": "dnssec_privkey.go",
    "content": "package dns\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/rsa\"\n\t\"math/big\"\n\t\"strconv\"\n)\n\nconst format = \"Private-key-format: v1.3\\n\"\n\nvar bigIntOne = big.NewInt(1)\n\n// PrivateKeyString converts a PrivateKey to a string. This string has the same\n// format as the private-key-file of BIND9 (Private-key-format: v1.3).\n// It needs some info from the key (the algorithm), so its a method of the DNSKEY.\n// It supports *rsa.PrivateKey, *ecdsa.PrivateKey and ed25519.PrivateKey.\nfunc (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {\n\talgorithm := strconv.Itoa(int(r.Algorithm))\n\talgorithm += \" (\" + AlgorithmToString[r.Algorithm] + \")\"\n\n\tswitch p := p.(type) {\n\tcase *rsa.PrivateKey:\n\t\tmodulus := toBase64(p.PublicKey.N.Bytes())\n\t\te := big.NewInt(int64(p.PublicKey.E))\n\t\tpublicExponent := toBase64(e.Bytes())\n\t\tprivateExponent := toBase64(p.D.Bytes())\n\t\tprime1 := toBase64(p.Primes[0].Bytes())\n\t\tprime2 := toBase64(p.Primes[1].Bytes())\n\t\t// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm\n\t\t// and from: http://code.google.com/p/go/issues/detail?id=987\n\t\tp1 := new(big.Int).Sub(p.Primes[0], bigIntOne)\n\t\tq1 := new(big.Int).Sub(p.Primes[1], bigIntOne)\n\t\texp1 := new(big.Int).Mod(p.D, p1)\n\t\texp2 := new(big.Int).Mod(p.D, q1)\n\t\tcoeff := new(big.Int).ModInverse(p.Primes[1], p.Primes[0])\n\n\t\texponent1 := toBase64(exp1.Bytes())\n\t\texponent2 := toBase64(exp2.Bytes())\n\t\tcoefficient := toBase64(coeff.Bytes())\n\n\t\treturn format +\n\t\t\t\"Algorithm: \" + algorithm + \"\\n\" +\n\t\t\t\"Modulus: \" + modulus + \"\\n\" +\n\t\t\t\"PublicExponent: \" + publicExponent + \"\\n\" +\n\t\t\t\"PrivateExponent: \" + privateExponent + \"\\n\" +\n\t\t\t\"Prime1: \" + prime1 + \"\\n\" +\n\t\t\t\"Prime2: \" + prime2 + \"\\n\" +\n\t\t\t\"Exponent1: \" + exponent1 + \"\\n\" +\n\t\t\t\"Exponent2: \" + exponent2 + \"\\n\" +\n\t\t\t\"Coefficient: \" + coefficient + \"\\n\"\n\n\tcase *ecdsa.PrivateKey:\n\t\tvar intlen int\n\t\tswitch r.Algorithm {\n\t\tcase ECDSAP256SHA256:\n\t\t\tintlen = 32\n\t\tcase ECDSAP384SHA384:\n\t\t\tintlen = 48\n\t\t}\n\t\tprivate := toBase64(intToBytes(p.D, intlen))\n\t\treturn format +\n\t\t\t\"Algorithm: \" + algorithm + \"\\n\" +\n\t\t\t\"PrivateKey: \" + private + \"\\n\"\n\n\tcase ed25519.PrivateKey:\n\t\tprivate := toBase64(p.Seed())\n\t\treturn format +\n\t\t\t\"Algorithm: \" + algorithm + \"\\n\" +\n\t\t\t\"PrivateKey: \" + private + \"\\n\"\n\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "dnssec_test.go",
    "content": "package dns\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/rsa\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc getSoa() *SOA {\n\tsoa := new(SOA)\n\tsoa.Hdr = RR_Header{\"miek.nl.\", TypeSOA, ClassINET, 14400, 0}\n\tsoa.Ns = \"open.nlnetlabs.nl.\"\n\tsoa.Mbox = \"miekg.atoom.net.\"\n\tsoa.Serial = 1293945905\n\tsoa.Refresh = 14400\n\tsoa.Retry = 3600\n\tsoa.Expire = 604800\n\tsoa.Minttl = 86400\n\treturn soa\n}\n\nfunc TestSecure(t *testing.T) {\n\tsoa := getSoa()\n\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = TypeSOA\n\tsig.Algorithm = RSASHA256\n\tsig.Labels = 2\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.OrigTtl = 14400\n\tsig.KeyTag = 12051\n\tsig.SignerName = \"miek.nl.\"\n\tsig.Signature = \"oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M=\"\n\n\tkey := new(DNSKEY)\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 14400\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tkey.PublicKey = \"AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz\"\n\n\t// It should validate. Period is checked separately, so this will keep on working\n\tif sig.Verify(key, []RR{soa}) != nil {\n\t\tt.Error(\"failure to validate\")\n\t}\n}\n\nfunc TestSignature(t *testing.T) {\n\tsig := new(RRSIG)\n\tsig.Hdr.Name = \"miek.nl.\"\n\tsig.Hdr.Class = ClassINET\n\tsig.Hdr.Ttl = 3600\n\tsig.TypeCovered = TypeDNSKEY\n\tsig.Algorithm = RSASHA1\n\tsig.Labels = 2\n\tsig.OrigTtl = 4000\n\tsig.Expiration = 1000 //Thu Jan  1 02:06:40 CET 1970\n\tsig.Inception = 800   //Thu Jan  1 01:13:20 CET 1970\n\tsig.KeyTag = 34641\n\tsig.SignerName = \"miek.nl.\"\n\tsig.Signature = \"AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ\"\n\n\t// Should not be valid\n\tif sig.ValidityPeriod(time.Now()) {\n\t\tt.Error(\"should not be valid\")\n\t}\n\n\tsig.Inception = 315565800   //Tue Jan  1 10:10:00 CET 1980\n\tsig.Expiration = 4102477800 //Fri Jan  1 10:10:00 CET 2100\n\tif !sig.ValidityPeriod(time.Now()) {\n\t\tt.Error(\"should be valid\")\n\t}\n}\n\nfunc TestSignVerify(t *testing.T) {\n\t// The record we want to sign\n\tsoa := new(SOA)\n\tsoa.Hdr = RR_Header{\"miek.nl.\", TypeSOA, ClassINET, 14400, 0}\n\tsoa.Ns = \"open.nlnetlabs.nl.\"\n\tsoa.Mbox = \"miekg.atoom.net.\"\n\tsoa.Serial = 1293945905\n\tsoa.Refresh = 14400\n\tsoa.Retry = 3600\n\tsoa.Expire = 604800\n\tsoa.Minttl = 86400\n\n\tsoa1 := new(SOA)\n\tsoa1.Hdr = RR_Header{\"*.miek.nl.\", TypeSOA, ClassINET, 14400, 0}\n\tsoa1.Ns = \"open.nlnetlabs.nl.\"\n\tsoa1.Mbox = \"miekg.atoom.net.\"\n\tsoa1.Serial = 1293945905\n\tsoa1.Refresh = 14400\n\tsoa1.Retry = 3600\n\tsoa1.Expire = 604800\n\tsoa1.Minttl = 86400\n\n\tsrv := new(SRV)\n\tsrv.Hdr = RR_Header{\"srv.miek.nl.\", TypeSRV, ClassINET, 14400, 0}\n\tsrv.Port = 1000\n\tsrv.Weight = 800\n\tsrv.Target = \"web1.miek.nl.\"\n\n\thinfo := &HINFO{\n\t\tHdr: RR_Header{\n\t\t\tName:   \"miek.nl.\",\n\t\t\tRrtype: TypeHINFO,\n\t\t\tClass:  ClassINET,\n\t\t\tTtl:    3789,\n\t\t},\n\t\tCpu: \"X\",\n\t\tOs:  \"Y\",\n\t}\n\n\t// With this key\n\tkey := new(DNSKEY)\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 14400\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tprivkey, err := key.Generate(1024)\n\tif err != nil {\n\t\tt.Fatal(\"failure to generate private key:\", err)\n\t}\n\n\t// Fill in the values of the Sig, before signing\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = soa.Hdr.Rrtype\n\tsig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3\n\tsig.OrigTtl = soa.Hdr.Ttl\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.KeyTag = key.KeyTag()   // Get the keyfrom the Key\n\tsig.SignerName = key.Hdr.Name\n\tsig.Algorithm = RSASHA256\n\n\tfor _, r := range []RR{soa, soa1, srv, hinfo} {\n\t\tif err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil {\n\t\t\tt.Error(\"failure to sign the record:\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif err := sig.Verify(key, []RR{r}); err != nil {\n\t\t\tt.Errorf(\"failure to validate: %s\", r.Header().Name)\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\n// Test if RRSIG.Verify() conforms to RFC 4035 Section 5.3.1\nfunc TestShouldNotVerifyInvalidSig(t *testing.T) {\n\t// The RRSIG RR and the RRset MUST have the same owner name\n\trrNameMismatch := getSoa()\n\trrNameMismatch.Hdr.Name = \"example.com.\"\n\n\t// ... and the same class\n\trrClassMismatch := getSoa()\n\trrClassMismatch.Hdr.Class = ClassCHAOS\n\n\t// The RRSIG RR's Type Covered field MUST equal the RRset's type.\n\trrTypeMismatch := getSoa()\n\trrTypeMismatch.Hdr.Rrtype = TypeA\n\n\t// The number of labels in the RRset owner name MUST be greater than\n\t// or equal to the value in the RRSIG RR's Labels field.\n\trrLabelLessThan := getSoa()\n\trrLabelLessThan.Hdr.Name = \"nl.\"\n\n\t// Time checks are done in ValidityPeriod\n\n\t// With this key\n\tkey := new(DNSKEY)\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 14400\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tprivkey, err := key.Generate(1024)\n\tif err != nil {\n\t\tt.Fatal(\"failure to generate private key:\", err)\n\t}\n\n\tnormalSoa := getSoa()\n\n\t// Fill in the normal values of the Sig, before signing\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = TypeSOA\n\tsig.Labels = uint8(CountLabel(normalSoa.Hdr.Name))\n\tsig.OrigTtl = normalSoa.Hdr.Ttl\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.KeyTag = key.KeyTag()   // Get the keyfrom the Key\n\tsig.SignerName = key.Hdr.Name\n\tsig.Algorithm = RSASHA256\n\n\tfor i, rr := range []RR{rrNameMismatch, rrClassMismatch, rrTypeMismatch, rrLabelLessThan} {\n\t\tif i != 0 { // Just for the rrNameMismatch case, we need the name to mismatch\n\t\t\tsig := sig.copy().(*RRSIG)\n\t\t\tsig.SignerName = rr.Header().Name\n\t\t\tsig.Hdr.Name = rr.Header().Name\n\t\t\tkey := key.copy().(*DNSKEY)\n\t\t\tkey.Hdr.Name = rr.Header().Name\n\t\t}\n\n\t\tif err := sig.signAsIs(privkey.(*rsa.PrivateKey), []RR{rr}); err != nil {\n\t\t\tt.Error(\"failure to sign the record:\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := sig.Verify(key, []RR{rr}); err == nil {\n\t\t\tt.Error(\"should not validate: \", rr)\n\t\t\tcontinue\n\t\t} else {\n\t\t\tt.Logf(\"expected failure: %v for RR name %s, class %d, type %d, rrsig labels %d\", err, rr.Header().Name, rr.Header().Class, rr.Header().Rrtype, CountLabel(rr.Header().Name))\n\t\t}\n\t}\n\n\t// The RRSIG RR's Signer's Name field MUST be the name of the zone that contains the RRset.\n\t// The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner name,\n\t// algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset.\n\tsigMismatchName := sig.copy().(*RRSIG)\n\tsigMismatchName.SignerName = \"example.com.\"\n\tsoaMismatchName := getSoa()\n\tsoaMismatchName.Hdr.Name = \"example.com.\"\n\tkeyMismatchName := key.copy().(*DNSKEY)\n\tkeyMismatchName.Hdr.Name = \"example.com.\"\n\tif err := sigMismatchName.signAsIs(privkey.(*rsa.PrivateKey), []RR{soaMismatchName}); err != nil {\n\t\tt.Error(\"failure to sign the record:\", err)\n\t} else if err := sigMismatchName.Verify(keyMismatchName, []RR{soaMismatchName}); err == nil {\n\t\tt.Error(\"should not validate: \", soaMismatchName, \", RRSIG's signer's name does not match the owner name\")\n\t} else {\n\t\tt.Logf(\"expected failure: %v for signer %s and owner %s\", err, sigMismatchName.SignerName, sigMismatchName.Hdr.Name)\n\t}\n\n\tsigMismatchAlgo := sig.copy().(*RRSIG)\n\tsigMismatchAlgo.Algorithm = RSASHA1\n\tsigMismatchKeyTag := sig.copy().(*RRSIG)\n\tsigMismatchKeyTag.KeyTag = 12345\n\tfor _, sigMismatch := range []*RRSIG{sigMismatchAlgo, sigMismatchKeyTag} {\n\t\tif err := sigMismatch.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil {\n\t\t\tt.Error(\"failure to sign the record:\", err)\n\t\t} else if err := sigMismatch.Verify(key, []RR{normalSoa}); err == nil {\n\t\t\tt.Error(\"should not validate: \", normalSoa)\n\t\t} else {\n\t\t\tt.Logf(\"expected failure: %v for signer %s algo %d keytag %d\", err, sigMismatch.SignerName, sigMismatch.Algorithm, sigMismatch.KeyTag)\n\t\t}\n\t}\n\n\t// The matching DNSKEY RR MUST have the Zone Flag bit (DNSKEY RDATA Flag bit 7) set.\n\tkeyZoneBitWrong := key.copy().(*DNSKEY)\n\tkeyZoneBitWrong.Flags = key.Flags &^ ZONE\n\tif err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil {\n\t\tt.Error(\"failure to sign the record:\", err)\n\t} else if err := sig.Verify(keyZoneBitWrong, []RR{normalSoa}); err == nil {\n\t\tt.Error(\"should not validate: \", normalSoa)\n\t} else {\n\t\tt.Logf(\"expected failure: %v for key flags %d\", err, keyZoneBitWrong.Flags)\n\t}\n}\n\nfunc Test65534(t *testing.T) {\n\tt6 := new(RFC3597)\n\tt6.Hdr = RR_Header{\"miek.nl.\", 65534, ClassINET, 14400, 0}\n\tt6.Rdata = \"505D870001\"\n\tkey := new(DNSKEY)\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 14400\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tprivkey, err := key.Generate(1024)\n\tif err != nil {\n\t\tt.Fatal(\"failure to generate private key:\", err)\n\t}\n\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = t6.Hdr.Rrtype\n\tsig.Labels = uint8(CountLabel(t6.Hdr.Name))\n\tsig.OrigTtl = t6.Hdr.Ttl\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.KeyTag = key.KeyTag()\n\tsig.SignerName = key.Hdr.Name\n\tsig.Algorithm = RSASHA256\n\tif err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil {\n\t\tt.Error(err)\n\t\tt.Error(\"failure to sign the TYPE65534 record\")\n\t}\n\tif err := sig.Verify(key, []RR{t6}); err != nil {\n\t\tt.Error(err)\n\t\tt.Errorf(\"failure to validate %s\", t6.Header().Name)\n\t}\n}\n\nfunc TestDnskey(t *testing.T) {\n\tpubkey, err := ReadRR(strings.NewReader(`\nmiek.nl.\tIN\tDNSKEY\t256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b}\n`), \"Kmiek.nl.+010+05240.key\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tprivStr := `Private-key-format: v1.3\nAlgorithm: 10 (RSASHA512)\nModulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs=\nPublicExponent: AQAB\nPrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE=\nPrime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ==\nPrime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw==\nExponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ==\nExponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw==\nCoefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ==\n`\n\tprivkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr),\n\t\t\"Kmiek.nl.+010+05240.private\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pubkey.(*DNSKEY).PublicKey != \"AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL\" {\n\t\tt.Error(\"pubkey is not what we've read\")\n\t}\n\tif pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr {\n\t\tt.Error(\"privkey is not what we've read\")\n\t\tt.Errorf(\"%v\", pubkey.(*DNSKEY).PrivateKeyString(privkey))\n\t}\n}\n\nfunc TestTag(t *testing.T) {\n\tkey := new(DNSKEY)\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 3600\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tkey.PublicKey = \"AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz\"\n\n\ttag := key.KeyTag()\n\tif tag != 12051 {\n\t\tt.Errorf(\"wrong key tag: %d for key %v\", tag, key)\n\t}\n}\n\nfunc TestKeyRSA(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping test in short mode.\")\n\t}\n\tkey := new(DNSKEY)\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 3600\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tpriv, err := key.Generate(1024)\n\tif err != nil {\n\t\tt.Fatal(\"failure to generate private key:\", err)\n\t}\n\n\tsoa := new(SOA)\n\tsoa.Hdr = RR_Header{\"miek.nl.\", TypeSOA, ClassINET, 14400, 0}\n\tsoa.Ns = \"open.nlnetlabs.nl.\"\n\tsoa.Mbox = \"miekg.atoom.net.\"\n\tsoa.Serial = 1293945905\n\tsoa.Refresh = 14400\n\tsoa.Retry = 3600\n\tsoa.Expire = 604800\n\tsoa.Minttl = 86400\n\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = TypeSOA\n\tsig.Algorithm = RSASHA256\n\tsig.Labels = 2\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.OrigTtl = soa.Hdr.Ttl\n\tsig.KeyTag = key.KeyTag()\n\tsig.SignerName = key.Hdr.Name\n\n\tif err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil {\n\t\tt.Error(\"failed to sign\")\n\t\treturn\n\t}\n\tif err := sig.Verify(key, []RR{soa}); err != nil {\n\t\tt.Error(\"failed to verify\")\n\t}\n}\n\nfunc TestKeyToDS(t *testing.T) {\n\tkey := new(DNSKEY)\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 3600\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = RSASHA256\n\tkey.PublicKey = \"AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz\"\n\n\tds := key.ToDS(SHA1)\n\tif strings.ToUpper(ds.Digest) != \"B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1\" {\n\t\tt.Errorf(\"wrong DS digest for SHA1\\n%v\", ds)\n\t}\n}\n\nfunc TestSignRSA(t *testing.T) {\n\tpub := \"miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ\"\n\n\tpriv := `Private-key-format: v1.3\nAlgorithm: 5 (RSASHA1)\nModulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k=\nPublicExponent: AQAB\nPrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk=\nPrime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw==\nPrime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw==\nExponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ==\nExponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw==\nCoefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw==\nCreated: 20110302104537\nPublish: 20110302104537\nActivate: 20110302104537`\n\n\txk := testRR(pub)\n\tk := xk.(*DNSKEY)\n\tp, err := k.NewPrivateKey(priv)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tswitch priv := p.(type) {\n\tcase *rsa.PrivateKey:\n\t\tif priv.PublicKey.E != 65537 {\n\t\t\tt.Error(\"exponenent should be 65537\")\n\t\t}\n\tdefault:\n\t\tt.Errorf(\"we should have read an RSA key: %v\", priv)\n\t}\n\tif k.KeyTag() != 37350 {\n\t\tt.Errorf(\"keytag should be 37350, got %d %v\", k.KeyTag(), k)\n\t}\n\n\tsoa := new(SOA)\n\tsoa.Hdr = RR_Header{\"miek.nl.\", TypeSOA, ClassINET, 14400, 0}\n\tsoa.Ns = \"open.nlnetlabs.nl.\"\n\tsoa.Mbox = \"miekg.atoom.net.\"\n\tsoa.Serial = 1293945905\n\tsoa.Refresh = 14400\n\tsoa.Retry = 3600\n\tsoa.Expire = 604800\n\tsoa.Minttl = 86400\n\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.KeyTag = k.KeyTag()\n\tsig.SignerName = k.Hdr.Name\n\tsig.Algorithm = k.Algorithm\n\n\tsig.Sign(p.(*rsa.PrivateKey), []RR{soa})\n\tif sig.Signature != \"D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=\" {\n\t\tt.Errorf(\"signature is not correct: %v\", sig)\n\t}\n}\n\nfunc TestSignVerifyECDSA(t *testing.T) {\n\tpub := `example.net. 3600 IN DNSKEY 257 3 14 (\n\txKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1\n\tw/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8\n\t/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`\n\tpriv := `Private-key-format: v1.2\nAlgorithm: 14 (ECDSAP384SHA384)\nPrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`\n\n\teckey := testRR(pub)\n\tprivkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t// TODO: Create separate test for this\n\tds := eckey.(*DNSKEY).ToDS(SHA384)\n\tif ds.KeyTag != 10771 {\n\t\tt.Fatal(\"wrong keytag on DS\")\n\t}\n\tif ds.Digest != \"72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6\" {\n\t\tt.Fatal(\"wrong DS Digest\")\n\t}\n\ta := testRR(\"www.example.net. 3600 IN A 192.0.2.1\")\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"example.net.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.Expiration, _ = StringToTime(\"20100909102025\")\n\tsig.Inception, _ = StringToTime(\"20100812102025\")\n\tsig.KeyTag = eckey.(*DNSKEY).KeyTag()\n\tsig.SignerName = eckey.(*DNSKEY).Hdr.Name\n\tsig.Algorithm = eckey.(*DNSKEY).Algorithm\n\n\tif sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil {\n\t\tt.Fatal(\"failure to sign the record\")\n\t}\n\n\tif err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {\n\t\tt.Fatalf(\"failure to validate:\\n%s\\n%s\\n%s\\n\\n%s\\n\\n%v\",\n\t\t\teckey.(*DNSKEY).String(),\n\t\t\ta.String(),\n\t\t\tsig.String(),\n\t\t\teckey.(*DNSKEY).PrivateKeyString(privkey),\n\t\t\terr,\n\t\t)\n\t}\n}\n\nfunc TestSignVerifyECDSA2(t *testing.T) {\n\tsrv1 := testRR(\"srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.\")\n\tsrv := srv1.(*SRV)\n\n\t// With this key\n\tkey := new(DNSKEY)\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 14400\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = ECDSAP256SHA256\n\tprivkey, err := key.Generate(256)\n\tif err != nil {\n\t\tt.Fatal(\"failure to generate key\")\n\t}\n\n\t// Fill in the values of the Sig, before signing\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = srv.Hdr.Rrtype\n\tsig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3\n\tsig.OrigTtl = srv.Hdr.Ttl\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.KeyTag = key.KeyTag()   // Get the keyfrom the Key\n\tsig.SignerName = key.Hdr.Name\n\tsig.Algorithm = ECDSAP256SHA256\n\n\tif sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil {\n\t\tt.Fatal(\"failure to sign the record\")\n\t}\n\n\terr = sig.Verify(key, []RR{srv})\n\tif err != nil {\n\t\tt.Errorf(\"failure to validate:\\n%s\\n%s\\n%s\\n\\n%s\\n\\n%v\",\n\t\t\tkey.String(),\n\t\t\tsrv.String(),\n\t\t\tsig.String(),\n\t\t\tkey.PrivateKeyString(privkey),\n\t\t\terr,\n\t\t)\n\t}\n}\n\nfunc TestSignVerifyEd25519(t *testing.T) {\n\tsrv1, err := NewRR(\"srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tsrv := srv1.(*SRV)\n\n\t// With this key\n\tkey := new(DNSKEY)\n\tkey.Hdr.Rrtype = TypeDNSKEY\n\tkey.Hdr.Name = \"miek.nl.\"\n\tkey.Hdr.Class = ClassINET\n\tkey.Hdr.Ttl = 14400\n\tkey.Flags = 256\n\tkey.Protocol = 3\n\tkey.Algorithm = ED25519\n\tprivkey, err := key.Generate(256)\n\tif err != nil {\n\t\tt.Fatal(\"failure to generate key\")\n\t}\n\n\t// Fill in the values of the Sig, before signing\n\tsig := new(RRSIG)\n\tsig.Hdr = RR_Header{\"miek.nl.\", TypeRRSIG, ClassINET, 14400, 0}\n\tsig.TypeCovered = srv.Hdr.Rrtype\n\tsig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3\n\tsig.OrigTtl = srv.Hdr.Ttl\n\tsig.Expiration = 1296534305 // date -u '+%s' -d\"2011-02-01 04:25:05\"\n\tsig.Inception = 1293942305  // date -u '+%s' -d\"2011-01-02 04:25:05\"\n\tsig.KeyTag = key.KeyTag()   // Get the keyfrom the Key\n\tsig.SignerName = key.Hdr.Name\n\tsig.Algorithm = ED25519\n\n\tif sig.Sign(privkey.(ed25519.PrivateKey), []RR{srv}) != nil {\n\t\tt.Fatal(\"failure to sign the record\")\n\t}\n\n\terr = sig.Verify(key, []RR{srv})\n\tif err != nil {\n\t\tt.Logf(\"failure to validate:\\n%s\\n%s\\n%s\\n\\n%s\\n\\n%v\",\n\t\t\tkey.String(),\n\t\t\tsrv.String(),\n\t\t\tsig.String(),\n\t\t\tkey.PrivateKeyString(privkey),\n\t\t\terr,\n\t\t)\n\t}\n}\n\n// Here the test vectors from the relevant RFCs are checked.\n// rfc6605 6.1\nfunc TestRFC6605P256(t *testing.T) {\n\texDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 (\n                 GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb\n                 krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )`\n\texPriv := `Private-key-format: v1.2\nAlgorithm: 13 (ECDSAP256SHA256)\nPrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`\n\trrDNSKEY := testRR(exDNSKEY)\n\tpriv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texDS := `example.net. 3600 IN DS 55648 13 2 (\n             b4c8c1fe2e7477127b27115656ad6256f424625bf5c1\n             e2770ce6d6e37df61d17 )`\n\trrDS := testRR(exDS)\n\tourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)\n\tif !reflect.DeepEqual(ourDS, rrDS.(*DS)) {\n\t\tt.Errorf(\"DS record differs:\\n%v\\n%v\", ourDS, rrDS.(*DS))\n\t}\n\n\texA := `www.example.net. 3600 IN A 192.0.2.1`\n\texRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 (\n                20100909100439 20100812100439 55648 example.net.\n                qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA\n                yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )`\n\trrA := testRR(exA)\n\trrRRSIG := testRR(exRRSIG)\n\tif err := rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {\n\t\tt.Errorf(\"failure to validate the spec RRSIG: %v\", err)\n\t}\n\n\tourRRSIG := &RRSIG{\n\t\tHdr: RR_Header{\n\t\t\tTtl: rrA.Header().Ttl,\n\t\t},\n\t\tKeyTag:     rrDNSKEY.(*DNSKEY).KeyTag(),\n\t\tSignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,\n\t\tAlgorithm:  rrDNSKEY.(*DNSKEY).Algorithm,\n\t}\n\tourRRSIG.Expiration, _ = StringToTime(\"20100909100439\")\n\tourRRSIG.Inception, _ = StringToTime(\"20100812100439\")\n\terr = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {\n\t\tt.Errorf(\"failure to validate our RRSIG: %v\", err)\n\t}\n\n\t// Signatures are randomized\n\trrRRSIG.(*RRSIG).Signature = \"\"\n\tourRRSIG.Signature = \"\"\n\tif !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {\n\t\tt.Fatalf(\"RRSIG record differs:\\n%v\\n%v\", ourRRSIG, rrRRSIG.(*RRSIG))\n\t}\n}\n\n// rfc6605 6.2\nfunc TestRFC6605P384(t *testing.T) {\n\texDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 (\n                 xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1\n                 w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8\n                 /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`\n\texPriv := `Private-key-format: v1.2\nAlgorithm: 14 (ECDSAP384SHA384)\nPrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`\n\trrDNSKEY := testRR(exDNSKEY)\n\tpriv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texDS := `example.net. 3600 IN DS 10771 14 4 (\n           72d7b62976ce06438e9c0bf319013cf801f09ecc84b8\n           d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94\n           6df983d6 )`\n\trrDS := testRR(exDS)\n\tourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)\n\tif !reflect.DeepEqual(ourDS, rrDS.(*DS)) {\n\t\tt.Fatalf(\"DS record differs:\\n%v\\n%v\", ourDS, rrDS.(*DS))\n\t}\n\n\texA := `www.example.net. 3600 IN A 192.0.2.1`\n\texRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 (\n           20100909102025 20100812102025 10771 example.net.\n           /L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP\n           95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz\n           WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )`\n\trrA := testRR(exA)\n\trrRRSIG := testRR(exRRSIG)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {\n\t\tt.Errorf(\"failure to validate the spec RRSIG: %v\", err)\n\t}\n\n\tourRRSIG := &RRSIG{\n\t\tHdr: RR_Header{\n\t\t\tTtl: rrA.Header().Ttl,\n\t\t},\n\t\tKeyTag:     rrDNSKEY.(*DNSKEY).KeyTag(),\n\t\tSignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,\n\t\tAlgorithm:  rrDNSKEY.(*DNSKEY).Algorithm,\n\t}\n\tourRRSIG.Expiration, _ = StringToTime(\"20100909102025\")\n\tourRRSIG.Inception, _ = StringToTime(\"20100812102025\")\n\terr = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {\n\t\tt.Errorf(\"failure to validate our RRSIG: %v\", err)\n\t}\n\n\t// Signatures are randomized\n\trrRRSIG.(*RRSIG).Signature = \"\"\n\tourRRSIG.Signature = \"\"\n\tif !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {\n\t\tt.Fatalf(\"RRSIG record differs:\\n%v\\n%v\", ourRRSIG, rrRRSIG.(*RRSIG))\n\t}\n}\n\n// rfc8080 6.1\nfunc TestRFC8080Ed25519Example1(t *testing.T) {\n\texDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (\n             l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )`\n\texPriv := `Private-key-format: v1.2\nAlgorithm: 15 (ED25519)\nPrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=`\n\trrDNSKEY, err := NewRR(exDNSKEY)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tpriv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texDS := `example.com. 3600 IN DS 3613 15 2 (\n             3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79\n             a304b )`\n\trrDS, err := NewRR(exDS)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)\n\tif !reflect.DeepEqual(ourDS, rrDS.(*DS)) {\n\t\tt.Fatalf(\"DS record differs:\\n%v\\n%v\", ourDS, rrDS.(*DS))\n\t}\n\n\texMX := `example.com. 3600 IN MX 10 mail.example.com.`\n\texRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (\n             1440021600 1438207200 3613 example.com. (\n             oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f\n             x8A4M3e23mRZ9VrbpMngwcrqNAg== ) )`\n\trrMX, err := NewRR(exMX)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\trrRRSIG, err := NewRR(exRRSIG)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {\n\t\tt.Errorf(\"failure to validate the spec RRSIG: %v\", err)\n\t}\n\n\tourRRSIG := &RRSIG{\n\t\tHdr: RR_Header{\n\t\t\tTtl: rrMX.Header().Ttl,\n\t\t},\n\t\tKeyTag:     rrDNSKEY.(*DNSKEY).KeyTag(),\n\t\tSignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,\n\t\tAlgorithm:  rrDNSKEY.(*DNSKEY).Algorithm,\n\t}\n\tourRRSIG.Expiration, _ = StringToTime(\"20150819220000\")\n\tourRRSIG.Inception, _ = StringToTime(\"20150729220000\")\n\terr = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {\n\t\tt.Errorf(\"failure to validate our RRSIG: %v\", err)\n\t}\n\n\tif !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {\n\t\tt.Fatalf(\"RRSIG record differs:\\n%v\\n%v\", ourRRSIG, rrRRSIG.(*RRSIG))\n\t}\n}\n\n// rfc8080 6.1\nfunc TestRFC8080Ed25519Example2(t *testing.T) {\n\texDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (\n             zPnZ/QwEe7S8C5SPz2OfS5RR40ATk2/rYnE9xHIEijs= )`\n\texPriv := `Private-key-format: v1.2\nAlgorithm: 15 (ED25519)\nPrivateKey: DSSF3o0s0f+ElWzj9E/Osxw8hLpk55chkmx0LYN5WiY=`\n\trrDNSKEY, err := NewRR(exDNSKEY)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tpriv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texDS := `example.com. 3600 IN DS 35217 15 2 (\n             401781b934e392de492ec77ae2e15d70f6575a1c0bc59c5275c04ebe80c\n             6614c )`\n\trrDS, err := NewRR(exDS)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)\n\tif !reflect.DeepEqual(ourDS, rrDS.(*DS)) {\n\t\tt.Fatalf(\"DS record differs:\\n%v\\n%v\", ourDS, rrDS.(*DS))\n\t}\n\n\texMX := `example.com. 3600 IN MX 10 mail.example.com.`\n\texRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (\n             1440021600 1438207200 35217 example.com. (\n             zXQ0bkYgQTEFyfLyi9QoiY6D8ZdYo4wyUhVioYZXFdT410QPRITQSqJSnzQ\n             oSm5poJ7gD7AQR0O7KuI5k2pcBg== ) )`\n\trrMX, err := NewRR(exMX)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\trrRRSIG, err := NewRR(exRRSIG)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {\n\t\tt.Errorf(\"failure to validate the spec RRSIG: %v\", err)\n\t}\n\n\tourRRSIG := &RRSIG{\n\t\tHdr: RR_Header{\n\t\t\tTtl: rrMX.Header().Ttl,\n\t\t},\n\t\tKeyTag:     rrDNSKEY.(*DNSKEY).KeyTag(),\n\t\tSignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,\n\t\tAlgorithm:  rrDNSKEY.(*DNSKEY).Algorithm,\n\t}\n\tourRRSIG.Expiration, _ = StringToTime(\"20150819220000\")\n\tourRRSIG.Inception, _ = StringToTime(\"20150729220000\")\n\terr = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {\n\t\tt.Errorf(\"failure to validate our RRSIG: %v\", err)\n\t}\n\n\tif !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {\n\t\tt.Fatalf(\"RRSIG record differs:\\n%v\\n%v\", ourRRSIG, rrRRSIG.(*RRSIG))\n\t}\n}\n\nfunc TestInvalidRRSet(t *testing.T) {\n\tgoodRecords := make([]RR, 2)\n\tgoodRecords[0] = &TXT{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tgoodRecords[1] = &TXT{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"_o/\"}}\n\n\t// Generate key\n\tkeyname := \"cloudflare.com.\"\n\tkey := &DNSKEY{\n\t\tHdr:       RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},\n\t\tAlgorithm: ECDSAP256SHA256,\n\t\tFlags:     ZONE,\n\t\tProtocol:  3,\n\t}\n\tprivatekey, err := key.Generate(256)\n\tif err != nil {\n\t\tt.Fatal(err.Error())\n\t}\n\n\t// Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm\n\tcurTime := time.Now()\n\tsignature := &RRSIG{\n\t\tInception:  uint32(curTime.Unix()),\n\t\tExpiration: uint32(curTime.Add(time.Hour).Unix()),\n\t\tKeyTag:     key.KeyTag(),\n\t\tSignerName: keyname,\n\t\tAlgorithm:  ECDSAP256SHA256,\n\t}\n\n\t// Inconsistent name between records\n\tbadRecords := make([]RR, 2)\n\tbadRecords[0] = &TXT{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tbadRecords[1] = &TXT{Hdr: RR_Header{Name: \"nama.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"_o/\"}}\n\n\tif IsRRset(badRecords) {\n\t\tt.Fatal(\"Record set with inconsistent names considered valid\")\n\t}\n\n\tbadRecords[0] = &TXT{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tbadRecords[1] = &A{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}\n\n\tif IsRRset(badRecords) {\n\t\tt.Fatal(\"Record set with inconsistent record types considered valid\")\n\t}\n\n\tbadRecords[0] = &TXT{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tbadRecords[1] = &TXT{Hdr: RR_Header{Name: \"name.cloudflare.com.\", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{\"_o/\"}}\n\n\tif IsRRset(badRecords) {\n\t\tt.Fatal(\"Record set with inconsistent record class considered valid\")\n\t}\n\n\t// Sign the good record set and then make sure verification fails on the bad record set\n\tif err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil {\n\t\tt.Fatal(\"Signing good records failed\")\n\t}\n\n\tif err := signature.Verify(key, badRecords); err != ErrRRset {\n\t\tt.Fatal(\"Verification did not return ErrRRset with inconsistent records\")\n\t}\n}\n\n// Issue #688 - RSA exponent unpacked in reverse\nfunc TestRsaExponentUnpack(t *testing.T) {\n\tzskRrDnskey, _ := NewRR(\"isc.org.                7200    IN      DNSKEY  256 3 5 AwEAAcdkaRUlsRD4gcF63PpPJJ1E6kOIb3yn/UHptVsPEQtEbgJ2y20O eix4unpwoQkz+bIAd2rrOU/95wgV530x0/qqKwBLWoGkxdcnNcvVT4hl 3SOTZy1VjwkAfyayHPU8VisXqJGbB3KWevBZlb6AtrXzFu8AHuBeeAAe /fOgreCh\")\n\tkskRrDnskey, _ := NewRR(\"isc.org.                7200    IN      DNSKEY  257 3 5 BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMVFu2hWLDMvoOMRXjGr hhCeFvAZih7yJHf8ZGfW6hd38hXG/xylYCO6Krpbdojwx8YMXLA5/kA+ u50WIL8ZR1R6KTbsYVMf/Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy3 47cBB1zMnnz/4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQz Bkj0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ7+ysyL KOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA8lVUgEf/rzeC/bB yBNsO70aEFTd\")\n\tkskRrRrsig, _ := NewRR(\"isc.org.                7200    IN      RRSIG   DNSKEY 5 2 7200 20180627230244 20180528230244 12892 isc.org. ebKBlhYi1hPGTdPg6zSwvprOIkoFMs+WIhMSjoYW6/K5CS9lDDFdK4cu TgXJRT3etrltTuJiFe2HRpp+7t5cKLy+CeJZVzqrCz200MoHiFuLI9yI DJQGaS5YYCiFbw5+jUGU6aUhZ7Y5/YufeqATkRZzdrKwgK+zri8LPw9T WLoVJPAOW7GR0dgxl9WKmO7Fzi9P8BZR3NuwLV7329X94j+4zyswaw7q e5vif0ybzFveODLsEi/E0a2rTXc4QzzyM0fSVxRkVQyQ7ifIPP4ohnnT d5qpPUbE8xxBzTdWR/TaKADC5aCFkppG9lVAq5CPfClii2949X5RYzy1 rxhuSA==\")\n\tzskRrRrsig, _ := NewRR(\"isc.org.                7200    IN      RRSIG   DNSKEY 5 2 7200 20180627230244 20180528230244 19923 isc.org. RgCfzUeq4RJPGoe9RRB6cWf6d/Du+tHK5SxI5QL1waA3O5qVtQKFkY1C dq/yyVjwzfjD9F62TObujOaktv8X80ZMcNPmgHbvK1xOqelMBWv5hxj3 xRe+QQObLZ5NPfHFsphQKXvwgO5Sjk8py2B2iCr3BHCZ8S38oIfuSrQx sn8=\")\n\n\tzsk, ksk := zskRrDnskey.(*DNSKEY), kskRrDnskey.(*DNSKEY)\n\tzskSig, kskSig := zskRrRrsig.(*RRSIG), kskRrRrsig.(*RRSIG)\n\n\tif e := zskSig.Verify(zsk, []RR{zsk, ksk}); e != nil {\n\t\tt.Fatalf(\"cannot verify RRSIG with keytag [%d]. Cause [%s]\", zsk.KeyTag(), e.Error())\n\t}\n\n\tif e := kskSig.Verify(ksk, []RR{zsk, ksk}); e != nil {\n\t\tt.Fatalf(\"cannot verify RRSIG with keytag [%d]. Cause [%s]\", ksk.KeyTag(), e.Error())\n\t}\n}\n\nfunc TestParseKeyReadError(t *testing.T) {\n\tm, err := parseKey(errReader{}, \"\")\n\tif err == nil || !strings.Contains(err.Error(), errTestReadError.Error()) {\n\t\tt.Errorf(\"expected error to contain %q, but got %v\", errTestReadError, err)\n\t}\n\tif m != nil {\n\t\tt.Errorf(\"expected a nil map, but got %v\", m)\n\t}\n}\n\nfunc TestRSAMD5KeyTag(t *testing.T) {\n\trr1, _ := NewRR(\"test.  IN DNSKEY  257 3 1 AwEAAcntNdoMnY8pvyPcpDTAaiqHyAhf53XUBANq166won/fjBFvmuzhTuP5r4el/pV0tzEBL73zpoU48BqF66uiL+qRijXCySJiaBUvLNll5rpwuduAOoVpmwOmkC4fV6izHOAx/Uy8c+pYP0YR8+1P7GuTFxgnMmt9sUGtoe+la0X/ ;{id = 27461 (ksk), size = 1024b}\")\n\trr2, _ := NewRR(\"test.  IN DNSKEY  257 3 1 AwEAAf0bKO/m45ylk5BlSLmQHQRBLx1m/ZUXvyPFB387bJXxnTk6so3ub97L1RQ+8bOoiRh3Qm5EaYihjco7J8b/W5WbS3tVsE79nY584RfTKT2zcZ9AoFP2XLChXxPIf/6l0H9n6sH0aBjsG8vabEIp8e06INM3CXVPiMRPPeGNa0Ub ;{id = 27461 (ksk), size = 1024b}\")\n\n\texp := uint16(27461)\n\tif x := rr1.(*DNSKEY).KeyTag(); x != exp {\n\t\tt.Errorf(\"expected %d, got %d, as keytag for rr1\", exp, x)\n\t}\n\tif x := rr2.(*DNSKEY).KeyTag(); x != exp { // yes, same key tag\n\t\tt.Errorf(\"expected %d, got %d, as keytag for rr2\", exp, x)\n\t}\n}\n"
  },
  {
    "path": "dnsutil/util.go",
    "content": "// Package dnsutil contains higher-level methods useful with the dns\n// package.  While package dns implements the DNS protocols itself,\n// these functions are related but not directly required for protocol\n// processing.  They are often useful in preparing input/output of the\n// functions in package dns.\npackage dnsutil\n\nimport (\n\t\"strings\"\n\n\t\"github.com/miekg/dns\"\n)\n\n// AddOrigin adds origin to s if s is not already a FQDN.\n// Note that the result may not be a FQDN.  If origin does not end\n// with a \".\", the result won't either.\n// This implements the zonefile convention (specified in RFC 1035,\n// Section \"5.1. Format\") that \"@\" represents the\n// apex (bare) domain. i.e. AddOrigin(\"@\", \"foo.com.\") returns \"foo.com.\".\nfunc AddOrigin(s, origin string) string {\n\t// (\"foo.\", \"origin.\") -> \"foo.\" (already a FQDN)\n\t// (\"foo\", \"origin.\") -> \"foo.origin.\"\n\t// (\"foo\", \"origin\") -> \"foo.origin\"\n\t// (\"foo\", \".\") -> \"foo.\" (Same as dns.Fqdn())\n\t// (\"foo.\", \".\") -> \"foo.\" (Same as dns.Fqdn())\n\t// (\"@\", \"origin.\") -> \"origin.\" (@ represents the apex (bare) domain)\n\t// (\"\", \"origin.\") -> \"origin.\" (not obvious)\n\t// (\"foo\", \"\") -> \"foo\" (not obvious)\n\n\tif dns.IsFqdn(s) {\n\t\treturn s // s is already a FQDN, no need to mess with it.\n\t}\n\tif origin == \"\" {\n\t\treturn s // Nothing to append.\n\t}\n\tif s == \"@\" || s == \"\" {\n\t\treturn origin // Expand apex.\n\t}\n\tif origin == \".\" {\n\t\treturn dns.Fqdn(s)\n\t}\n\n\treturn s + \".\" + origin // The simple case.\n}\n\n// TrimDomainName trims origin from s if s is a subdomain.\n// This function will never return \"\", but returns \"@\" instead (@ represents the apex domain).\nfunc TrimDomainName(s, origin string) string {\n\t// An apex (bare) domain is always returned as \"@\".\n\t// If the return value ends in a \".\", the domain was not the suffix.\n\t// origin can end in \".\" or not. Either way the results should be the same.\n\n\tif s == \"\" {\n\t\treturn \"@\"\n\t}\n\t// Someone is using TrimDomainName(s, \".\") to remove a dot if it exists.\n\tif origin == \".\" {\n\t\treturn strings.TrimSuffix(s, origin)\n\t}\n\n\toriginal := s\n\ts = dns.Fqdn(s)\n\torigin = dns.Fqdn(origin)\n\n\tif !dns.IsSubDomain(origin, s) {\n\t\treturn original\n\t}\n\n\tslabels := dns.Split(s)\n\tolabels := dns.Split(origin)\n\tm := dns.CompareDomainName(s, origin)\n\tif len(olabels) == m {\n\t\tif len(olabels) == len(slabels) {\n\t\t\treturn \"@\" // origin == s\n\t\t}\n\t\tif (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) {\n\t\t\treturn \"@\" // TrimDomainName(\".foo.\", \"foo.\")\n\t\t}\n\t}\n\n\t// Return the first (len-m) labels:\n\treturn s[:slabels[len(slabels)-m]-1]\n}\n"
  },
  {
    "path": "dnsutil/util_test.go",
    "content": "package dnsutil\n\nimport \"testing\"\n\nfunc TestAddOrigin(t *testing.T) {\n\tvar tests = []struct{ e1, e2, expected string }{\n\t\t{\"@\", \"example.com\", \"example.com\"},\n\t\t{\"foo\", \"example.com\", \"foo.example.com\"},\n\t\t{\"foo.\", \"example.com\", \"foo.\"},\n\t\t{\"@\", \"example.com.\", \"example.com.\"},\n\t\t{\"foo\", \"example.com.\", \"foo.example.com.\"},\n\t\t{\"foo.\", \"example.com.\", \"foo.\"},\n\t\t{\"example.com\", \".\", \"example.com.\"},\n\t\t{\"example.com.\", \".\", \"example.com.\"},\n\t\t// Oddball tests:\n\t\t// In general origin should not be \"\" or \".\" but at least\n\t\t// these tests verify we don't crash and will keep results\n\t\t// from changing unexpectedly.\n\t\t{\"*.\", \"\", \"*.\"},\n\t\t{\"@\", \"\", \"@\"},\n\t\t{\"foobar\", \"\", \"foobar\"},\n\t\t{\"foobar.\", \"\", \"foobar.\"},\n\t\t{\"*.\", \".\", \"*.\"},\n\t\t{\"@\", \".\", \".\"},\n\t\t{\"foobar\", \".\", \"foobar.\"},\n\t\t{\"foobar.\", \".\", \"foobar.\"},\n\t}\n\tfor _, test := range tests {\n\t\tactual := AddOrigin(test.e1, test.e2)\n\t\tif test.expected != actual {\n\t\t\tt.Errorf(\"AddOrigin(%#v, %#v) expected %#v, got %#v\\n\", test.e1, test.e2, test.expected, actual)\n\t\t}\n\t}\n}\n\nfunc TestTrimDomainName(t *testing.T) {\n\t// Basic tests.\n\t// Try trimming \"example.com\" and \"example.com.\" from typical use cases.\n\ttestsEx := []struct{ experiment, expected string }{\n\t\t{\"foo.example.com\", \"foo\"},\n\t\t{\"foo.example.com.\", \"foo\"},\n\t\t{\".foo.example.com\", \".foo\"},\n\t\t{\".foo.example.com.\", \".foo\"},\n\t\t{\"*.example.com\", \"*\"},\n\t\t{\"example.com\", \"@\"},\n\t\t{\"example.com.\", \"@\"},\n\t\t{\"com.\", \"com.\"},\n\t\t{\"foo.\", \"foo.\"},\n\t\t{\"serverfault.com.\", \"serverfault.com.\"},\n\t\t{\"serverfault.com\", \"serverfault.com\"},\n\t\t{\".foo.ronco.com\", \".foo.ronco.com\"},\n\t\t{\".foo.ronco.com.\", \".foo.ronco.com.\"},\n\t}\n\tfor _, dom := range []string{\"example.com\", \"example.com.\"} {\n\t\tfor i, test := range testsEx {\n\t\t\tactual := TrimDomainName(test.experiment, dom)\n\t\t\tif test.expected != actual {\n\t\t\t\tt.Errorf(\"%d TrimDomainName(%#v, %#v): expected %v, got %v\\n\", i, test.experiment, dom, test.expected, actual)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Paranoid tests.\n\t// These test shouldn't be needed but I was weary of off-by-one errors.\n\t// In theory, these can't happen because there are no single-letter TLDs,\n\t// but it is good to exercise the code this way.\n\ttests := []struct{ experiment, expected string }{\n\t\t{\"\", \"@\"},\n\t\t{\".\", \".\"},\n\t\t{\"a.b.c.d.e.f.\", \"a.b.c.d.e\"},\n\t\t{\"b.c.d.e.f.\", \"b.c.d.e\"},\n\t\t{\"c.d.e.f.\", \"c.d.e\"},\n\t\t{\"d.e.f.\", \"d.e\"},\n\t\t{\"e.f.\", \"e\"},\n\t\t{\"f.\", \"@\"},\n\t\t{\".a.b.c.d.e.f.\", \".a.b.c.d.e\"},\n\t\t{\".b.c.d.e.f.\", \".b.c.d.e\"},\n\t\t{\".c.d.e.f.\", \".c.d.e\"},\n\t\t{\".d.e.f.\", \".d.e\"},\n\t\t{\".e.f.\", \".e\"},\n\t\t{\".f.\", \"@\"},\n\t\t{\"a.b.c.d.e.f\", \"a.b.c.d.e\"},\n\t\t{\"a.b.c.d.e.\", \"a.b.c.d.e.\"},\n\t\t{\"a.b.c.d.e\", \"a.b.c.d.e\"},\n\t\t{\"a.b.c.d.\", \"a.b.c.d.\"},\n\t\t{\"a.b.c.d\", \"a.b.c.d\"},\n\t\t{\"a.b.c.\", \"a.b.c.\"},\n\t\t{\"a.b.c\", \"a.b.c\"},\n\t\t{\"a.b.\", \"a.b.\"},\n\t\t{\"a.b\", \"a.b\"},\n\t\t{\"a.\", \"a.\"},\n\t\t{\"a\", \"a\"},\n\t\t{\".a.b.c.d.e.f\", \".a.b.c.d.e\"},\n\t\t{\".a.b.c.d.e.\", \".a.b.c.d.e.\"},\n\t\t{\".a.b.c.d.e\", \".a.b.c.d.e\"},\n\t\t{\".a.b.c.d.\", \".a.b.c.d.\"},\n\t\t{\".a.b.c.d\", \".a.b.c.d\"},\n\t\t{\".a.b.c.\", \".a.b.c.\"},\n\t\t{\".a.b.c\", \".a.b.c\"},\n\t\t{\".a.b.\", \".a.b.\"},\n\t\t{\".a.b\", \".a.b\"},\n\t\t{\".a.\", \".a.\"},\n\t\t{\".a\", \".a\"},\n\t}\n\tfor _, dom := range []string{\"f\", \"f.\"} {\n\t\tfor i, test := range tests {\n\t\t\tactual := TrimDomainName(test.experiment, dom)\n\t\t\tif test.expected != actual {\n\t\t\t\tt.Errorf(\"%d TrimDomainName(%#v, %#v): expected %v, got %v\\n\", i, test.experiment, dom, test.expected, actual)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Test cases for bugs found in the wild.\n\t// These test cases provide both origin, s, and the expected result.\n\t// If you find a bug in the while, this is probably the easiest place\n\t// to add it as a test case.\n\tvar testsWild = []struct{ e1, e2, expected string }{\n\t\t{\"mathoverflow.net.\", \".\", \"mathoverflow.net\"},\n\t\t{\"mathoverflow.net\", \".\", \"mathoverflow.net\"},\n\t\t{\"\", \".\", \"@\"},\n\t\t{\"@\", \".\", \"@\"},\n\t}\n\tfor i, test := range testsWild {\n\t\tactual := TrimDomainName(test.e1, test.e2)\n\t\tif test.expected != actual {\n\t\t\tt.Errorf(\"%d TrimDomainName(%#v, %#v): expected %v, got %v\\n\", i, test.e1, test.e2, test.expected, actual)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "doc.go",
    "content": "/*\nPackage dns implements a full featured interface to the Domain Name System.\nBoth server- and client-side programming is supported. The package allows\ncomplete control over what is sent out to the DNS. The API follows the\nless-is-more principle, by presenting a small, clean interface.\n\nIt supports (asynchronous) querying/replying, incoming/outgoing zone transfers,\nTSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.\n\nNote that domain names MUST be fully qualified before sending them, unqualified\nnames in a message will result in a packing failure.\n\nResource records are native types. They are not stored in wire format. Basic\nusage pattern for creating a new resource record:\n\n\tr := new(dns.MX)\n\tr.Hdr = dns.RR_Header{Name: \"miek.nl.\", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}\n\tr.Preference = 10\n\tr.Mx = \"mx.miek.nl.\"\n\nOr directly from a string:\n\n\tmx, err := dns.NewRR(\"miek.nl. 3600 IN MX 10 mx.miek.nl.\")\n\nOr when the default origin (.) and TTL (3600) and class (IN) suit you:\n\n\tmx, err := dns.NewRR(\"miek.nl MX 10 mx.miek.nl\")\n\nOr even:\n\n\tmx, err := dns.NewRR(\"$ORIGIN nl.\\nmiek 1H IN MX 10 mx.miek\")\n\nIn the DNS messages are exchanged, these messages contain resource records\n(sets). Use pattern for creating a message:\n\n\tm := new(dns.Msg)\n\tm.SetQuestion(\"miek.nl.\", dns.TypeMX)\n\nOr when not certain if the domain name is fully qualified:\n\n\tm.SetQuestion(dns.Fqdn(\"miek.nl\"), dns.TypeMX)\n\nThe message m is now a message with the question section set to ask the MX\nrecords for the miek.nl. zone.\n\nThe following is slightly more verbose, but more flexible:\n\n\tm1 := new(dns.Msg)\n\tm1.Id = dns.Id()\n\tm1.RecursionDesired = true\n\tm1.Question = make([]dns.Question, 1)\n\tm1.Question[0] = dns.Question{\"miek.nl.\", dns.TypeMX, dns.ClassINET}\n\nAfter creating a message it can be sent. Basic use pattern for synchronous\nquerying the DNS at a server configured on 127.0.0.1 and port 53:\n\n\tc := new(dns.Client)\n\tin, rtt, err := c.Exchange(m1, \"127.0.0.1:53\")\n\nSuppressing multiple outstanding queries (with the same question, type and\nclass) is as easy as setting:\n\n\tc.SingleInflight = true\n\nMore advanced options are available using a net.Dialer and the corresponding API.\nFor example it is possible to set a timeout, or to specify a source IP address\nand port to use for the connection:\n\n\tc := new(dns.Client)\n\tladdr := net.UDPAddr{\n\t\tIP: net.ParseIP(\"[::1]\"),\n\t\tPort: 12345,\n\t\tZone: \"\",\n\t}\n\tc.Dialer = &net.Dialer{\n\t\tTimeout: 200 * time.Millisecond,\n\t\tLocalAddr: &laddr,\n\t}\n\tin, rtt, err := c.Exchange(m1, \"8.8.8.8:53\")\n\nIf these \"advanced\" features are not needed, a simple UDP query can be sent,\nwith:\n\n\tin, err := dns.Exchange(m1, \"127.0.0.1:53\")\n\nWhen this functions returns you will get DNS message. A DNS message consists\nout of four sections.\nThe question section: in.Question, the answer section: in.Answer,\nthe authority section: in.Ns and the additional section: in.Extra.\n\nEach of these sections (except the Question section) contain a []RR. Basic\nuse pattern for accessing the rdata of a TXT RR as the first RR in\nthe Answer section:\n\n\tif t, ok := in.Answer[0].(*dns.TXT); ok {\n\t\t// do something with t.Txt\n\t}\n\n# Domain Name and TXT Character String Representations\n\nBoth domain names and TXT character strings are converted to presentation form\nboth when unpacked and when converted to strings.\n\nFor TXT character strings, tabs, carriage returns and line feeds will be\nconverted to \\t, \\r and \\n respectively. Back slashes and quotations marks will\nbe escaped. Bytes below 32 and above 127 will be converted to \\DDD form.\n\nFor domain names, in addition to the above rules brackets, periods, spaces,\nsemicolons and the at symbol are escaped.\n\n# DNSSEC\n\nDNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses\npublic key cryptography to sign resource records. The public keys are stored in\nDNSKEY records and the signatures in RRSIG records.\n\nRequesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK)\nbit to a request.\n\n\tm := new(dns.Msg)\n\tm.SetEdns0(4096, true)\n\nSignature generation, signature verification and key generation are all supported.\n\n# DYNAMIC UPDATES\n\nDynamic updates reuses the DNS message format, but renames three of the\nsections. Question is Zone, Answer is Prerequisite, Authority is Update, only\nthe Additional is not renamed. See RFC 2136 for the gory details.\n\nYou can set a rather complex set of rules for the existence of absence of\ncertain resource records or names in a zone to specify if resource records\nshould be added or removed. The table from RFC 2136 supplemented with the Go\nDNS function shows which functions exist to specify the prerequisites.\n\n\t3.2.4 - Table Of Metavalues Used In Prerequisite Section\n\n\t CLASS    TYPE     RDATA    Meaning                    Function\n\t --------------------------------------------------------------\n\t ANY      ANY      empty    Name is in use             dns.NameUsed\n\t ANY      rrset    empty    RRset exists (value indep) dns.RRsetUsed\n\t NONE     ANY      empty    Name is not in use         dns.NameNotUsed\n\t NONE     rrset    empty    RRset does not exist       dns.RRsetNotUsed\n\t zone     rrset    rr       RRset exists (value dep)   dns.Used\n\nThe prerequisite section can also be left empty. If you have decided on the\nprerequisites you can tell what RRs should be added or deleted. The next table\nshows the options you have and what functions to call.\n\n\t3.4.2.6 - Table Of Metavalues Used In Update Section\n\n\t CLASS    TYPE     RDATA    Meaning                     Function\n\t ---------------------------------------------------------------\n\t ANY      ANY      empty    Delete all RRsets from name dns.RemoveName\n\t ANY      rrset    empty    Delete an RRset             dns.RemoveRRset\n\t NONE     rrset    rr       Delete an RR from RRset     dns.Remove\n\t zone     rrset    rr       Add to an RRset             dns.Insert\n\n# TRANSACTION SIGNATURE\n\nAn TSIG or transaction signature adds a HMAC TSIG record to each message sent.\nThe supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512.\n\nBasic use pattern when querying with a TSIG name \"axfr.\" (note that these key names\nmust be fully qualified - as they are domain names) and the base64 secret\n\"so6ZGir4GPAqINNh9U5c3A==\":\n\nIf an incoming message contains a TSIG record it MUST be the last record in\nthe additional section (RFC2845 3.2).  This means that you should make the\ncall to SetTsig last, right before executing the query.  If you make any\nchanges to the RRset after calling SetTsig() the signature will be incorrect.\n\n\tc := new(dns.Client)\n\tc.TsigSecret = map[string]string{\"axfr.\": \"so6ZGir4GPAqINNh9U5c3A==\"}\n\tm := new(dns.Msg)\n\tm.SetQuestion(\"miek.nl.\", dns.TypeMX)\n\tm.SetTsig(\"axfr.\", dns.HmacSHA256, 300, time.Now().Unix())\n\t...\n\t// When sending the TSIG RR is calculated and filled in before sending\n\nWhen requesting an zone transfer (almost all TSIG usage is when requesting zone\ntransfers), with TSIG, this is the basic use pattern. In this example we\nrequest an AXFR for miek.nl. with TSIG key named \"axfr.\" and secret\n\"so6ZGir4GPAqINNh9U5c3A==\" and using the server 176.58.119.54:\n\n\tt := new(dns.Transfer)\n\tm := new(dns.Msg)\n\tt.TsigSecret = map[string]string{\"axfr.\": \"so6ZGir4GPAqINNh9U5c3A==\"}\n\tm.SetAxfr(\"miek.nl.\")\n\tm.SetTsig(\"axfr.\", dns.HmacSHA256, 300, time.Now().Unix())\n\tc, err := t.In(m, \"176.58.119.54:53\")\n\tfor r := range c { ... }\n\nYou can now read the records from the transfer as they come in. Each envelope\nis checked with TSIG. If something is not correct an error is returned.\n\nA custom TSIG implementation can be used. This requires additional code to\nperform any session establishment and signature generation/verification. The\nclient must be configured with an implementation of the TsigProvider interface:\n\n\ttype Provider struct{}\n\n\tfunc (*Provider) Generate(msg []byte, tsig *dns.TSIG) ([]byte, error) {\n\t\t// Use tsig.Hdr.Name and tsig.Algorithm in your code to\n\t\t// generate the MAC using msg as the payload.\n\t}\n\n\tfunc (*Provider) Verify(msg []byte, tsig *dns.TSIG) error {\n\t\t// Use tsig.Hdr.Name and tsig.Algorithm in your code to verify\n\t\t// that msg matches the value in tsig.MAC.\n\t}\n\n\tc := new(dns.Client)\n\tc.TsigProvider = new(Provider)\n\tm := new(dns.Msg)\n\tm.SetQuestion(\"miek.nl.\", dns.TypeMX)\n\tm.SetTsig(keyname, dns.HmacSHA256, 300, time.Now().Unix())\n\t...\n\t// TSIG RR is calculated by calling your Generate method\n\nBasic use pattern validating and replying to a message that has TSIG set.\n\n\tserver := &dns.Server{Addr: \":53\", Net: \"udp\"}\n\tserver.TsigSecret = map[string]string{\"axfr.\": \"so6ZGir4GPAqINNh9U5c3A==\"}\n\tgo server.ListenAndServe()\n\tdns.HandleFunc(\".\", handleRequest)\n\n\tfunc handleRequest(w dns.ResponseWriter, r *dns.Msg) {\n\t\tm := new(dns.Msg)\n\t\tm.SetReply(r)\n\t\tif r.IsTsig() != nil {\n\t\t\tif w.TsigStatus() == nil {\n\t\t\t\t// *Msg r has an TSIG record and it was validated\n\t\t\t\tm.SetTsig(\"axfr.\", dns.HmacSHA256, 300, time.Now().Unix())\n\t\t\t} else {\n\t\t\t\t// *Msg r has an TSIG records and it was not validated\n\t\t\t}\n\t\t}\n\t\tw.WriteMsg(m)\n\t}\n\n# PRIVATE RRS\n\nRFC 6895 sets aside a range of type codes for private use. This range is 65,280\n- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these\ncan be used, before requesting an official type code from IANA.\n\nSee https://miek.nl/2014/september/21/idn-and-private-rr-in-go-dns/ for more\ninformation.\n\n# EDNS0\n\nEDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by\nRFC 6891. It defines a new RR type, the OPT RR, which is then completely\nabused.\n\nBasic use pattern for creating an (empty) OPT RR:\n\n\to := new(dns.OPT)\n\to.Hdr.Name = \".\" // MUST be the root zone, per definition.\n\to.Hdr.Rrtype = dns.TypeOPT\n\nThe rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces.\nCurrently only a few have been standardized: EDNS0_NSID (RFC 5001) and\nEDNS0_SUBNET (RFC 7871). Note that these options may be combined in an OPT RR.\nBasic use pattern for a server to check if (and which) options are set:\n\n\t// o is a dns.OPT\n\tfor _, s := range o.Option {\n\t\tswitch e := s.(type) {\n\t\tcase *dns.EDNS0_NSID:\n\t\t\t// do stuff with e.Nsid\n\t\tcase *dns.EDNS0_SUBNET:\n\t\t\t// access e.Family, e.Address, etc.\n\t\t}\n\t}\n\nSIG(0)\n\nFrom RFC 2931:\n\n\tSIG(0) provides protection for DNS transactions and requests ....\n\t... protection for glue records, DNS requests, protection for message headers\n\ton requests and responses, and protection of the overall integrity of a response.\n\nIt works like TSIG, except that SIG(0) uses public key cryptography, instead of\nthe shared secret approach in TSIG. Supported algorithms: ECDSAP256SHA256,\nECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512.\n\nSigning subsequent messages in multi-message sessions is not implemented.\n*/\npackage dns\n"
  },
  {
    "path": "duplicate.go",
    "content": "package dns\n\n//go:generate go run duplicate_generate.go\n\n// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL.\n// So this means the header data is equal *and* the RDATA is the same. Returns true\n// if so, otherwise false. It's a protocol violation to have identical RRs in a message.\nfunc IsDuplicate(r1, r2 RR) bool {\n\t// Check whether the record header is identical.\n\tif !r1.Header().isDuplicate(r2.Header()) {\n\t\treturn false\n\t}\n\n\t// Check whether the RDATA is identical.\n\treturn r1.isDuplicate(r2)\n}\n\nfunc (r1 *RR_Header) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RR_Header)\n\tif !ok {\n\t\treturn false\n\t}\n\tif r1.Class != r2.Class {\n\t\treturn false\n\t}\n\tif r1.Rrtype != r2.Rrtype {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Name, r2.Name) {\n\t\treturn false\n\t}\n\t// ignore TTL\n\treturn true\n}\n\n// isDuplicateName checks if the domain names s1 and s2 are equal.\nfunc isDuplicateName(s1, s2 string) bool { return equal(s1, s2) }\n"
  },
  {
    "path": "duplicate_generate.go",
    "content": "//go:build ignore\n// +build ignore\n\n// types_generate.go is meant to run with go generate. It will use\n// go/{importer,types} to track down all the RR struct types. Then for each type\n// it will generate conversion tables (TypeToRR and TypeToString) and banal\n// methods (len, Header, copy) based on the struct tags. The generated source is\n// written to ztypes.go, and is meant to be checked into git.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nvar packageHdr = `\n// Code generated by \"go run duplicate_generate.go\"; DO NOT EDIT.\n\npackage dns\n\n`\n\nfunc getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {\n\tst, ok := t.Underlying().(*types.Struct)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tif st.NumFields() == 0 {\n\t\treturn nil, false\n\t}\n\tif st.Field(0).Type() == scope.Lookup(\"RR_Header\").Type() {\n\t\treturn st, false\n\t}\n\tif st.Field(0).Anonymous() {\n\t\tst, _ := getTypeStruct(st.Field(0).Type(), scope)\n\t\treturn st, true\n\t}\n\treturn nil, false\n}\n\n// loadModule retrieves package description for a given module.\nfunc loadModule(name string) (*types.Package, error) {\n\tconf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo}\n\tpkgs, err := packages.Load(&conf, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pkgs[0].Types, nil\n}\n\nfunc main() {\n\t// Import and type-check the package\n\tpkg, err := loadModule(\"github.com/miekg/dns\")\n\tfatalIfErr(err)\n\tscope := pkg.Scope()\n\n\t// Collect actual types (*X)\n\tvar namedTypes []string\n\tfor _, name := range scope.Names() {\n\t\to := scope.Lookup(name)\n\t\tif o == nil || !o.Exported() {\n\t\t\tcontinue\n\t\t}\n\n\t\tif st, _ := getTypeStruct(o.Type(), scope); st == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif name == \"PrivateRR\" || name == \"OPT\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tnamedTypes = append(namedTypes, o.Name())\n\t}\n\n\tb := &bytes.Buffer{}\n\tb.WriteString(packageHdr)\n\n\t// Generate the duplicate check for each type.\n\tfmt.Fprint(b, \"// isDuplicate() functions\\n\\n\")\n\tfor _, name := range namedTypes {\n\n\t\to := scope.Lookup(name)\n\t\tst, _ := getTypeStruct(o.Type(), scope)\n\t\tfmt.Fprintf(b, \"func (r1 *%s) isDuplicate(_r2 RR) bool {\\n\", name)\n\t\tfmt.Fprintf(b, \"r2, ok := _r2.(*%s)\\n\", name)\n\t\tfmt.Fprint(b, \"if !ok { return false }\\n\")\n\t\tfmt.Fprint(b, \"_ = r2\\n\")\n\t\tfor i := 1; i < st.NumFields(); i++ {\n\t\t\tfield := st.Field(i).Name()\n\t\t\to2 := func(s string) { fmt.Fprintf(b, s+\"\\n\", field, field) }\n\t\t\to3 := func(s string) { fmt.Fprintf(b, s+\"\\n\", field, field, field) }\n\n\t\t\t// For some reason, a and aaaa don't pop up as *types.Slice here (mostly like because the are\n\t\t\t// *indirectly* defined as a slice in the net package).\n\t\t\tif _, ok := st.Field(i).Type().(*types.Slice); ok {\n\t\t\t\to2(\"if len(r1.%s) != len(r2.%s) {\\nreturn false\\n}\")\n\n\t\t\t\tif st.Tag(i) == `dns:\"cdomain-name\"` || st.Tag(i) == `dns:\"domain-name\"` {\n\t\t\t\t\to3(`for i := 0; i < len(r1.%s); i++ {\n\t\t\t\t\t\tif !isDuplicateName(r1.%s[i], r2.%s[i]) {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}`)\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif st.Tag(i) == `dns:\"apl\"` {\n\t\t\t\t\to3(`for i := 0; i < len(r1.%s); i++ {\n\t\t\t\t\t\tif !r1.%s[i].equals(&r2.%s[i]) {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}`)\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif st.Tag(i) == `dns:\"pairs\"` {\n\t\t\t\t\to2(`if !areSVCBPairArraysEqual(r1.%s, r2.%s) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}`)\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\to3(`for i := 0; i < len(r1.%s); i++ {\n\t\t\t\t\tif r1.%s[i] != r2.%s[i] {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}`)\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch st.Tag(i) {\n\t\t\tcase `dns:\"-\"`:\n\t\t\t\t// ignored\n\t\t\tcase `dns:\"a\"`, `dns:\"aaaa\"`:\n\t\t\t\to2(\"if !r1.%s.Equal(r2.%s) {\\nreturn false\\n}\")\n\t\t\tcase `dns:\"cdomain-name\"`, `dns:\"domain-name\"`:\n\t\t\t\to2(\"if !isDuplicateName(r1.%s, r2.%s) {\\nreturn false\\n}\")\n\t\t\tcase `dns:\"ipsechost\"`, `dns:\"amtrelayhost\"`:\n\t\t\t\to2(`switch r1.GatewayType {\n\t\t\t\tcase IPSECGatewayIPv4, IPSECGatewayIPv6:\n\t\t\t\t\tif !r1.GatewayAddr.Equal(r2.GatewayAddr) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\tcase IPSECGatewayHost:\n\t\t\t\t\tif !isDuplicateName(r1.%s, r2.%s) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t`)\n\t\t\tdefault:\n\t\t\t\to2(\"if r1.%s != r2.%s {\\nreturn false\\n}\")\n\t\t\t}\n\t\t}\n\t\tfmt.Fprint(b, \"return true\\n}\\n\\n\")\n\t}\n\n\t// gofmt\n\tres, err := format.Source(b.Bytes())\n\tif err != nil {\n\t\tb.WriteTo(os.Stderr)\n\t\tlog.Fatal(err)\n\t}\n\n\t// write result\n\tf, err := os.Create(\"zduplicate.go\")\n\tfatalIfErr(err)\n\tdefer f.Close()\n\tf.Write(res)\n}\n\nfunc fatalIfErr(err error) {\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "duplicate_test.go",
    "content": "package dns\n\nimport \"testing\"\n\nfunc TestDuplicateA(t *testing.T) {\n\ta1, _ := NewRR(\"www.example.org. 2700 IN A 127.0.0.1\")\n\ta2, _ := NewRR(\"www.example.org. IN A 127.0.0.1\")\n\tif !IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n\n\ta2, _ = NewRR(\"www.example.org. IN A 127.0.0.2\")\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n}\n\nfunc TestDuplicateTXT(t *testing.T) {\n\ta1, _ := NewRR(\"www.example.org. IN TXT \\\"aa\\\"\")\n\ta2, _ := NewRR(\"www.example.org. IN TXT \\\"aa\\\"\")\n\n\tif !IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n\n\ta2, _ = NewRR(\"www.example.org. IN TXT \\\"aa\\\" \\\"bb\\\"\")\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n\n\ta1, _ = NewRR(\"www.example.org. IN TXT \\\"aa\\\" \\\"bc\\\"\")\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n}\n\nfunc TestDuplicateSVCB(t *testing.T) {\n\ta1, _ := NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1::3:3:3:3 key65300=\\254\\032\\030\\000\\ \\043,\\;`)\n\ta2, _ := NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1:0::3:3:3:3 key65300=\"\\254\\ \\030\\000 +\\,;\"`)\n\n\tif !IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n\n\ta2, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1::3:3:3:3 key65300=\"\\255\\ \\030\\000 +\\,;\"`)\n\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n\n\ta1, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1::3:3:3:3`)\n\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n\n\ta2, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv4hint=1.1.1.1`)\n\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n\n\ta1, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv4hint=1.1.1.1,1.0.2.1`)\n\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", a1.String(), a2.String())\n\t}\n}\n\nfunc TestDuplicateOwner(t *testing.T) {\n\ta1, _ := NewRR(\"www.example.org. IN A 127.0.0.1\")\n\ta2, _ := NewRR(\"www.example.org. IN A 127.0.0.1\")\n\tif !IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n\n\ta2, _ = NewRR(\"WWw.exaMPle.org. IN A 127.0.0.2\")\n\tif IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n}\n\nfunc TestDuplicateDomain(t *testing.T) {\n\ta1, _ := NewRR(\"www.example.org. IN CNAME example.org.\")\n\ta2, _ := NewRR(\"www.example.org. IN CNAME example.org.\")\n\tif !IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n\n\ta2, _ = NewRR(\"www.example.org. IN CNAME exAMPLe.oRG.\")\n\tif !IsDuplicate(a1, a2) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", a1.String(), a2.String())\n\t}\n}\n\nfunc TestDuplicateWrongRrtype(t *testing.T) {\n\t// Test that IsDuplicate won't panic for a record that's lying about\n\t// it's Rrtype.\n\n\tr1 := &A{Hdr: RR_Header{Rrtype: TypeA}}\n\tr2 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}}\n\tif IsDuplicate(r1, r2) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", r1.String(), r2.String())\n\t}\n\n\tr3 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}}\n\tr4 := &A{Hdr: RR_Header{Rrtype: TypeA}}\n\tif IsDuplicate(r3, r4) {\n\t\tt.Errorf(\"expected %s/%s not to be duplicates, but got true\", r3.String(), r4.String())\n\t}\n\n\tr5 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}}\n\tr6 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}}\n\tif !IsDuplicate(r5, r6) {\n\t\tt.Errorf(\"expected %s/%s to be duplicates, but got false\", r5.String(), r6.String())\n\t}\n}\n"
  },
  {
    "path": "dyn_test.go",
    "content": "package dns\n\n// Find better solution\n"
  },
  {
    "path": "edns.go",
    "content": "package dns\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n)\n\n// EDNS0 Option codes.\nconst (\n\tEDNS0LLQ          = 0x1     // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01\n\tEDNS0UL           = 0x2     // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt\n\tEDNS0NSID         = 0x3     // nsid (See RFC 5001)\n\tEDNS0ESU          = 0x4     // ENUM Source-URI draft: https://datatracker.ietf.org/doc/html/draft-kaplan-enum-source-uri-00\n\tEDNS0DAU          = 0x5     // DNSSEC Algorithm Understood\n\tEDNS0DHU          = 0x6     // DS Hash Understood\n\tEDNS0N3U          = 0x7     // NSEC3 Hash Understood\n\tEDNS0SUBNET       = 0x8     // client-subnet (See RFC 7871)\n\tEDNS0EXPIRE       = 0x9     // EDNS0 expire\n\tEDNS0COOKIE       = 0xa     // EDNS0 Cookie\n\tEDNS0TCPKEEPALIVE = 0xb     // EDNS0 tcp keep alive (See RFC 7828)\n\tEDNS0PADDING      = 0xc     // EDNS0 padding (See RFC 7830)\n\tEDNS0EDE          = 0xf     // EDNS0 extended DNS errors (See RFC 8914)\n\tEDNS0REPORTING    = 0x12    // EDNS0 reporting (See RFC 9567)\n\tEDNS0ZONEVERSION  = 0x13    // EDNS0 Zone Version (See RFC 9660)\n\tEDNS0LOCALSTART   = 0xFDE9  // Beginning of range reserved for local/experimental use (See RFC 6891)\n\tEDNS0LOCALEND     = 0xFFFE  // End of range reserved for local/experimental use (See RFC 6891)\n\t_DO               = 1 << 15 // DNSSEC OK\n\t_CO               = 1 << 14 // Compact Answers OK\n)\n\n// makeDataOpt is used to unpack the EDNS0 option(s) from a message.\nfunc makeDataOpt(code uint16) EDNS0 {\n\t// All the EDNS0.* constants above need to be in this switch.\n\tswitch code {\n\tcase EDNS0LLQ:\n\t\treturn new(EDNS0_LLQ)\n\tcase EDNS0UL:\n\t\treturn new(EDNS0_UL)\n\tcase EDNS0NSID:\n\t\treturn new(EDNS0_NSID)\n\tcase EDNS0DAU:\n\t\treturn new(EDNS0_DAU)\n\tcase EDNS0DHU:\n\t\treturn new(EDNS0_DHU)\n\tcase EDNS0N3U:\n\t\treturn new(EDNS0_N3U)\n\tcase EDNS0SUBNET:\n\t\treturn new(EDNS0_SUBNET)\n\tcase EDNS0EXPIRE:\n\t\treturn new(EDNS0_EXPIRE)\n\tcase EDNS0COOKIE:\n\t\treturn new(EDNS0_COOKIE)\n\tcase EDNS0TCPKEEPALIVE:\n\t\treturn new(EDNS0_TCP_KEEPALIVE)\n\tcase EDNS0PADDING:\n\t\treturn new(EDNS0_PADDING)\n\tcase EDNS0EDE:\n\t\treturn new(EDNS0_EDE)\n\tcase EDNS0ESU:\n\t\treturn new(EDNS0_ESU)\n\tcase EDNS0REPORTING:\n\t\treturn new(EDNS0_REPORTING)\n\tcase EDNS0ZONEVERSION:\n\t\treturn new(EDNS0_ZONEVERSION)\n\tdefault:\n\t\te := new(EDNS0_LOCAL)\n\t\te.Code = code\n\t\treturn e\n\t}\n}\n\n// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. See RFC 6891.\ntype OPT struct {\n\tHdr    RR_Header\n\tOption []EDNS0 `dns:\"opt\"`\n}\n\nfunc (rr *OPT) String() string {\n\ts := \"\\n;; OPT PSEUDOSECTION:\\n; EDNS: version \" + strconv.Itoa(int(rr.Version())) + \"; \"\n\ts += \"flags:\"\n\tif rr.Do() {\n\t\ts += \" do\"\n\t}\n\tif rr.Co() {\n\t\ts += \" co\"\n\t}\n\ts += \"; \"\n\tif z := rr.Z(); z != 0 {\n\t\ts += fmt.Sprintf(\"MBZ: 0x%04x, \", z)\n\t}\n\ts += \"udp: \" + strconv.Itoa(int(rr.UDPSize()))\n\n\tfor _, o := range rr.Option {\n\t\tswitch o.(type) {\n\t\tcase *EDNS0_NSID:\n\t\t\ts += \"\\n; NSID: \" + o.String()\n\t\t\th, e := o.pack()\n\t\t\tvar r string\n\t\t\tif e == nil {\n\t\t\t\tfor _, c := range h {\n\t\t\t\t\tr += \"(\" + string(c) + \")\"\n\t\t\t\t}\n\t\t\t\ts += \"  \" + r\n\t\t\t}\n\t\tcase *EDNS0_SUBNET:\n\t\t\ts += \"\\n; SUBNET: \" + o.String()\n\t\tcase *EDNS0_COOKIE:\n\t\t\ts += \"\\n; COOKIE: \" + o.String()\n\t\tcase *EDNS0_EXPIRE:\n\t\t\ts += \"\\n; EXPIRE: \" + o.String()\n\t\tcase *EDNS0_TCP_KEEPALIVE:\n\t\t\ts += \"\\n; KEEPALIVE: \" + o.String()\n\t\tcase *EDNS0_UL:\n\t\t\ts += \"\\n; UPDATE LEASE: \" + o.String()\n\t\tcase *EDNS0_LLQ:\n\t\t\ts += \"\\n; LONG LIVED QUERIES: \" + o.String()\n\t\tcase *EDNS0_DAU:\n\t\t\ts += \"\\n; DNSSEC ALGORITHM UNDERSTOOD: \" + o.String()\n\t\tcase *EDNS0_DHU:\n\t\t\ts += \"\\n; DS HASH UNDERSTOOD: \" + o.String()\n\t\tcase *EDNS0_N3U:\n\t\t\ts += \"\\n; NSEC3 HASH UNDERSTOOD: \" + o.String()\n\t\tcase *EDNS0_LOCAL:\n\t\t\ts += \"\\n; LOCAL OPT: \" + o.String()\n\t\tcase *EDNS0_PADDING:\n\t\t\ts += \"\\n; PADDING: \" + o.String()\n\t\tcase *EDNS0_EDE:\n\t\t\ts += \"\\n; EDE: \" + o.String()\n\t\tcase *EDNS0_ESU:\n\t\t\ts += \"\\n; ESU: \" + o.String()\n\t\tcase *EDNS0_REPORTING:\n\t\t\ts += \"\\n; REPORT-CHANNEL: \" + o.String()\n\t\tcase *EDNS0_ZONEVERSION:\n\t\t\ts += \"\\n; ZONEVERSION: \" + o.String()\n\t\t}\n\t}\n\treturn s\n}\n\nfunc (rr *OPT) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, o := range rr.Option {\n\t\tl += 4 // Account for 2-byte option code and 2-byte option length.\n\t\tlo, _ := o.pack()\n\t\tl += len(lo)\n\t}\n\treturn l\n}\n\nfunc (*OPT) parse(c *zlexer, origin string) *ParseError {\n\treturn &ParseError{err: \"OPT records do not have a presentation format\"}\n}\n\nfunc (rr *OPT) isDuplicate(r2 RR) bool { return false }\n\n// Version returns the EDNS version used. Only zero is defined.\nfunc (rr *OPT) Version() uint8 {\n\treturn uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16)\n}\n\n// SetVersion sets the version of EDNS. This is usually zero.\nfunc (rr *OPT) SetVersion(v uint8) {\n\trr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)<<16\n}\n\n// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).\nfunc (rr *OPT) ExtendedRcode() int {\n\treturn int(rr.Hdr.Ttl&0xFF000000>>24) << 4\n}\n\n// SetExtendedRcode sets the EDNS extended RCODE field.\n//\n// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0.\nfunc (rr *OPT) SetExtendedRcode(v uint16) {\n\trr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24\n}\n\n// UDPSize returns the UDP buffer size.\nfunc (rr *OPT) UDPSize() uint16 {\n\treturn rr.Hdr.Class\n}\n\n// SetUDPSize sets the UDP buffer size.\nfunc (rr *OPT) SetUDPSize(size uint16) {\n\trr.Hdr.Class = size\n}\n\n// Do returns the value of the DO (DNSSEC OK) bit.\nfunc (rr *OPT) Do() bool {\n\treturn rr.Hdr.Ttl&_DO == _DO\n}\n\n// SetDo sets the DO (DNSSEC OK) bit.\n// If we pass an argument, set the DO bit to that value.\n// It is possible to pass 2 or more arguments, but they will be ignored.\nfunc (rr *OPT) SetDo(do ...bool) {\n\tif len(do) == 1 {\n\t\tif do[0] {\n\t\t\trr.Hdr.Ttl |= _DO\n\t\t} else {\n\t\t\trr.Hdr.Ttl &^= _DO\n\t\t}\n\t} else {\n\t\trr.Hdr.Ttl |= _DO\n\t}\n}\n\n// Co returns the value of the CO (Compact Answers OK) bit.\nfunc (rr *OPT) Co() bool {\n\treturn rr.Hdr.Ttl&_CO == _CO\n}\n\n// SetCo sets the CO (Compact Answers OK) bit.\n// If we pass an argument, set the CO bit to that value.\n// It is possible to pass 2 or more arguments, but they will be ignored.\nfunc (rr *OPT) SetCo(co ...bool) {\n\tif len(co) == 1 {\n\t\tif co[0] {\n\t\t\trr.Hdr.Ttl |= _CO\n\t\t} else {\n\t\t\trr.Hdr.Ttl &^= _CO\n\t\t}\n\t} else {\n\t\trr.Hdr.Ttl |= _CO\n\t}\n}\n\n// Z returns the Z part of the OPT RR as a uint16 with only the 14 least significant bits used.\nfunc (rr *OPT) Z() uint16 {\n\treturn uint16(rr.Hdr.Ttl & 0x3FFF)\n}\n\n// SetZ sets the Z part of the OPT RR, note only the 14 least significant bits of z are used.\nfunc (rr *OPT) SetZ(z uint16) {\n\trr.Hdr.Ttl = rr.Hdr.Ttl&^0x3FFF | uint32(z&0x3FFF)\n}\n\n// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.\ntype EDNS0 interface {\n\t// Option returns the option code for the option.\n\tOption() uint16\n\t// pack returns the bytes of the option data.\n\tpack() ([]byte, error)\n\t// unpack sets the data as found in the buffer. Is also sets\n\t// the length of the slice as the length of the option data.\n\tunpack([]byte) error\n\t// String returns the string representation of the option.\n\tString() string\n\t// copy returns a deep-copy of the option.\n\tcopy() EDNS0\n}\n\n// EDNS0_NSID option is used to retrieve a nameserver\n// identifier. When sending a request Nsid must be set to the empty string\n// The identifier is an opaque string encoded as hex.\n// Basic use pattern for creating an nsid option:\n//\n//\to := new(dns.OPT)\n//\to.Hdr.Name = \".\"\n//\to.Hdr.Rrtype = dns.TypeOPT\n//\te := new(dns.EDNS0_NSID)\n//\te.Code = dns.EDNS0NSID\n//\te.Nsid = \"AA\"\n//\to.Option = append(o.Option, e)\ntype EDNS0_NSID struct {\n\tCode uint16 // always EDNS0NSID\n\tNsid string // string needs to be hex encoded\n}\n\nfunc (e *EDNS0_NSID) pack() ([]byte, error) {\n\th, err := hex.DecodeString(e.Nsid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn h, nil\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_NSID) Option() uint16        { return EDNS0NSID } // Option returns the option code.\nfunc (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }\nfunc (e *EDNS0_NSID) String() string        { return e.Nsid }\nfunc (e *EDNS0_NSID) copy() EDNS0           { return &EDNS0_NSID{e.Code, e.Nsid} }\n\n// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver\n// an idea of where the client lives. See RFC 7871. It can then give back a different\n// answer depending on the location or network topology.\n// Basic use pattern for creating an subnet option:\n//\n//\to := new(dns.OPT)\n//\to.Hdr.Name = \".\"\n//\to.Hdr.Rrtype = dns.TypeOPT\n//\te := new(dns.EDNS0_SUBNET)\n//\te.Code = dns.EDNS0SUBNET // by default this is filled in through unpacking OPT packets (unpackDataOpt)\n//\te.Family = 1\t// 1 for IPv4 source address, 2 for IPv6\n//\te.SourceNetmask = 32\t// 32 for IPV4, 128 for IPv6\n//\te.SourceScope = 0\n//\te.Address = net.ParseIP(\"127.0.0.1\").To4()\t// for IPv4\n//\t// e.Address = net.ParseIP(\"2001:7b8:32a::2\")\t// for IPV6\n//\to.Option = append(o.Option, e)\n//\n// This code will parse all the available bits when unpacking (up to optlen).\n// When packing it will apply SourceNetmask. If you need more advanced logic,\n// patches welcome and good luck.\ntype EDNS0_SUBNET struct {\n\tCode          uint16 // always EDNS0SUBNET\n\tFamily        uint16 // 1 for IP, 2 for IP6\n\tSourceNetmask uint8\n\tSourceScope   uint8\n\tAddress       net.IP\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }\n\nfunc (e *EDNS0_SUBNET) pack() ([]byte, error) {\n\tswitch e.Family {\n\tcase 0:\n\t\t// \"dig\" sets AddressFamily to 0 if SourceNetmask is also 0\n\t\t// We might don't need to complain either\n\t\tif e.SourceNetmask != 0 {\n\t\t\treturn nil, errors.New(\"bad address family\")\n\t\t}\n\t\tb := make([]byte, 4)\n\t\tb[3] = e.SourceScope\n\t\treturn b, nil\n\tcase 1:\n\t\tif e.SourceNetmask > net.IPv4len*8 {\n\t\t\treturn nil, errors.New(\"bad netmask\")\n\t\t}\n\t\tip4 := e.Address.To4()\n\t\tif len(ip4) != net.IPv4len {\n\t\t\treturn nil, errors.New(\"bad address\")\n\t\t}\n\t\tneedLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up\n\t\tb := make([]byte, 4+needLength)\n\t\tbinary.BigEndian.PutUint16(b[0:], e.Family)\n\t\tb[2] = e.SourceNetmask\n\t\tb[3] = e.SourceScope\n\t\tif needLength > 0 {\n\t\t\tip := ip4.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))\n\t\t\tcopy(b[4:], ip[:needLength])\n\t\t}\n\t\treturn b, nil\n\tcase 2:\n\t\tif e.SourceNetmask > net.IPv6len*8 {\n\t\t\treturn nil, errors.New(\"bad netmask\")\n\t\t}\n\t\tif len(e.Address) != net.IPv6len {\n\t\t\treturn nil, errors.New(\"bad address\")\n\t\t}\n\t\tneedLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up\n\t\tb := make([]byte, 4+needLength)\n\t\tbinary.BigEndian.PutUint16(b[0:], e.Family)\n\t\tb[2] = e.SourceNetmask\n\t\tb[3] = e.SourceScope\n\t\tif needLength > 0 {\n\t\t\tip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))\n\t\t\tcopy(b[4:], ip[:needLength])\n\t\t}\n\t\treturn b, nil\n\tdefault:\n\t\treturn nil, errors.New(\"bad address family\")\n\t}\n}\n\nfunc (e *EDNS0_SUBNET) unpack(b []byte) error {\n\tif len(b) < 4 {\n\t\treturn ErrBuf\n\t}\n\te.Family = binary.BigEndian.Uint16(b)\n\te.SourceNetmask = b[2]\n\te.SourceScope = b[3]\n\tswitch e.Family {\n\tcase 0:\n\t\t// \"dig\" sets AddressFamily to 0 if SourceNetmask is also 0\n\t\t// It's okay to accept such a packet\n\t\tif e.SourceNetmask != 0 {\n\t\t\treturn errors.New(\"bad address family\")\n\t\t}\n\t\te.Address = net.IPv4(0, 0, 0, 0)\n\tcase 1:\n\t\tif e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {\n\t\t\treturn errors.New(\"bad netmask\")\n\t\t}\n\t\taddr := make(net.IP, net.IPv4len)\n\t\tcopy(addr, b[4:])\n\t\te.Address = addr.To16()\n\tcase 2:\n\t\tif e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {\n\t\t\treturn errors.New(\"bad netmask\")\n\t\t}\n\t\taddr := make(net.IP, net.IPv6len)\n\t\tcopy(addr, b[4:])\n\t\te.Address = addr\n\tdefault:\n\t\treturn errors.New(\"bad address family\")\n\t}\n\treturn nil\n}\n\nfunc (e *EDNS0_SUBNET) String() (s string) {\n\tif e.Address == nil {\n\t\ts = \"<nil>\"\n\t} else if e.Address.To4() != nil {\n\t\ts = e.Address.String()\n\t} else {\n\t\ts = \"[\" + e.Address.String() + \"]\"\n\t}\n\ts += \"/\" + strconv.Itoa(int(e.SourceNetmask)) + \"/\" + strconv.Itoa(int(e.SourceScope))\n\treturn\n}\n\nfunc (e *EDNS0_SUBNET) copy() EDNS0 {\n\treturn &EDNS0_SUBNET{\n\t\te.Code,\n\t\te.Family,\n\t\te.SourceNetmask,\n\t\te.SourceScope,\n\t\te.Address,\n\t}\n}\n\n// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.\n//\n//\to := new(dns.OPT)\n//\to.Hdr.Name = \".\"\n//\to.Hdr.Rrtype = dns.TypeOPT\n//\te := new(dns.EDNS0_COOKIE)\n//\te.Code = dns.EDNS0COOKIE\n//\te.Cookie = \"24a5ac..\"\n//\to.Option = append(o.Option, e)\n//\n// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is\n// always 8 bytes. It may then optionally be followed by the server cookie. The server\n// cookie is of variable length, 8 to a maximum of 32 bytes. In other words:\n//\n//\tcCookie := o.Cookie[:16]\n//\tsCookie := o.Cookie[16:]\n//\n// There is no guarantee that the Cookie string has a specific length.\ntype EDNS0_COOKIE struct {\n\tCode   uint16 // always EDNS0COOKIE\n\tCookie string // hex encoded cookie data\n}\n\nfunc (e *EDNS0_COOKIE) pack() ([]byte, error) {\n\th, err := hex.DecodeString(e.Cookie)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn h, nil\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_COOKIE) Option() uint16        { return EDNS0COOKIE }\nfunc (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }\nfunc (e *EDNS0_COOKIE) String() string        { return e.Cookie }\nfunc (e *EDNS0_COOKIE) copy() EDNS0           { return &EDNS0_COOKIE{e.Code, e.Cookie} }\n\n// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set\n// an expiration on an update RR. This is helpful for clients that cannot clean\n// up after themselves. This is a draft RFC and more information can be found at\n// https://tools.ietf.org/html/draft-sekar-dns-ul-02\n//\n//\to := new(dns.OPT)\n//\to.Hdr.Name = \".\"\n//\to.Hdr.Rrtype = dns.TypeOPT\n//\te := new(dns.EDNS0_UL)\n//\te.Code = dns.EDNS0UL\n//\te.Lease = 120 // in seconds\n//\to.Option = append(o.Option, e)\ntype EDNS0_UL struct {\n\tCode     uint16 // always EDNS0UL\n\tLease    uint32\n\tKeyLease uint32\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_UL) Option() uint16 { return EDNS0UL }\nfunc (e *EDNS0_UL) String() string { return fmt.Sprintf(\"%d %d\", e.Lease, e.KeyLease) }\nfunc (e *EDNS0_UL) copy() EDNS0    { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} }\n\n// Copied: http://golang.org/src/pkg/net/dnsmsg.go\nfunc (e *EDNS0_UL) pack() ([]byte, error) {\n\tvar b []byte\n\tif e.KeyLease == 0 {\n\t\tb = make([]byte, 4)\n\t} else {\n\t\tb = make([]byte, 8)\n\t\tbinary.BigEndian.PutUint32(b[4:], e.KeyLease)\n\t}\n\tbinary.BigEndian.PutUint32(b, e.Lease)\n\treturn b, nil\n}\n\nfunc (e *EDNS0_UL) unpack(b []byte) error {\n\tswitch len(b) {\n\tcase 4:\n\t\te.KeyLease = 0\n\tcase 8:\n\t\te.KeyLease = binary.BigEndian.Uint32(b[4:])\n\tdefault:\n\t\treturn ErrBuf\n\t}\n\te.Lease = binary.BigEndian.Uint32(b)\n\treturn nil\n}\n\n// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01\n// Implemented for completeness, as the EDNS0 type code is assigned.\ntype EDNS0_LLQ struct {\n\tCode      uint16 // always EDNS0LLQ\n\tVersion   uint16\n\tOpcode    uint16\n\tError     uint16\n\tId        uint64\n\tLeaseLife uint32\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }\n\nfunc (e *EDNS0_LLQ) pack() ([]byte, error) {\n\tb := make([]byte, 18)\n\tbinary.BigEndian.PutUint16(b[0:], e.Version)\n\tbinary.BigEndian.PutUint16(b[2:], e.Opcode)\n\tbinary.BigEndian.PutUint16(b[4:], e.Error)\n\tbinary.BigEndian.PutUint64(b[6:], e.Id)\n\tbinary.BigEndian.PutUint32(b[14:], e.LeaseLife)\n\treturn b, nil\n}\n\nfunc (e *EDNS0_LLQ) unpack(b []byte) error {\n\tif len(b) < 18 {\n\t\treturn ErrBuf\n\t}\n\te.Version = binary.BigEndian.Uint16(b[0:])\n\te.Opcode = binary.BigEndian.Uint16(b[2:])\n\te.Error = binary.BigEndian.Uint16(b[4:])\n\te.Id = binary.BigEndian.Uint64(b[6:])\n\te.LeaseLife = binary.BigEndian.Uint32(b[14:])\n\treturn nil\n}\n\nfunc (e *EDNS0_LLQ) String() string {\n\ts := strconv.FormatUint(uint64(e.Version), 10) + \" \" + strconv.FormatUint(uint64(e.Opcode), 10) +\n\t\t\" \" + strconv.FormatUint(uint64(e.Error), 10) + \" \" + strconv.FormatUint(e.Id, 10) +\n\t\t\" \" + strconv.FormatUint(uint64(e.LeaseLife), 10)\n\treturn s\n}\n\nfunc (e *EDNS0_LLQ) copy() EDNS0 {\n\treturn &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}\n}\n\n// EDNS0_DAU implements the EDNS0 \"DNSSEC Algorithm Understood\" option. See RFC 6975.\ntype EDNS0_DAU struct {\n\tCode    uint16 // always EDNS0DAU\n\tAlgCode []uint8\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_DAU) Option() uint16        { return EDNS0DAU }\nfunc (e *EDNS0_DAU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil }\nfunc (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil }\n\nfunc (e *EDNS0_DAU) String() string {\n\ts := \"\"\n\tfor _, alg := range e.AlgCode {\n\t\tif a, ok := AlgorithmToString[alg]; ok {\n\t\t\ts += \" \" + a\n\t\t} else {\n\t\t\ts += \" \" + strconv.Itoa(int(alg))\n\t\t}\n\t}\n\treturn s\n}\nfunc (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} }\n\n// EDNS0_DHU implements the EDNS0 \"DS Hash Understood\" option. See RFC 6975.\ntype EDNS0_DHU struct {\n\tCode    uint16 // always EDNS0DHU\n\tAlgCode []uint8\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_DHU) Option() uint16        { return EDNS0DHU }\nfunc (e *EDNS0_DHU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil }\nfunc (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil }\n\nfunc (e *EDNS0_DHU) String() string {\n\ts := \"\"\n\tfor _, alg := range e.AlgCode {\n\t\tif a, ok := HashToString[alg]; ok {\n\t\t\ts += \" \" + a\n\t\t} else {\n\t\t\ts += \" \" + strconv.Itoa(int(alg))\n\t\t}\n\t}\n\treturn s\n}\nfunc (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} }\n\n// EDNS0_N3U implements the EDNS0 \"NSEC3 Hash Understood\" option. See RFC 6975.\ntype EDNS0_N3U struct {\n\tCode    uint16 // always EDNS0N3U\n\tAlgCode []uint8\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_N3U) Option() uint16        { return EDNS0N3U }\nfunc (e *EDNS0_N3U) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil }\nfunc (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil }\n\nfunc (e *EDNS0_N3U) String() string {\n\t// Re-use the hash map\n\ts := \"\"\n\tfor _, alg := range e.AlgCode {\n\t\tif a, ok := HashToString[alg]; ok {\n\t\t\ts += \" \" + a\n\t\t} else {\n\t\t\ts += \" \" + strconv.Itoa(int(alg))\n\t\t}\n\t}\n\treturn s\n}\nfunc (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }\n\n// EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314.\ntype EDNS0_EXPIRE struct {\n\tCode   uint16 // always EDNS0EXPIRE\n\tExpire uint32\n\tEmpty  bool // Empty is used to signal an empty Expire option in a backwards compatible way, it's not used on the wire.\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }\nfunc (e *EDNS0_EXPIRE) copy() EDNS0    { return &EDNS0_EXPIRE{e.Code, e.Expire, e.Empty} }\n\nfunc (e *EDNS0_EXPIRE) pack() ([]byte, error) {\n\tif e.Empty {\n\t\treturn []byte{}, nil\n\t}\n\tb := make([]byte, 4)\n\tbinary.BigEndian.PutUint32(b, e.Expire)\n\treturn b, nil\n}\n\nfunc (e *EDNS0_EXPIRE) unpack(b []byte) error {\n\tif len(b) == 0 {\n\t\t// zero-length EXPIRE query, see RFC 7314 Section 2\n\t\te.Empty = true\n\t\treturn nil\n\t}\n\tif len(b) < 4 {\n\t\treturn ErrBuf\n\t}\n\te.Expire = binary.BigEndian.Uint32(b)\n\te.Empty = false\n\treturn nil\n}\n\nfunc (e *EDNS0_EXPIRE) String() (s string) {\n\tif e.Empty {\n\t\treturn \"\"\n\t}\n\treturn strconv.FormatUint(uint64(e.Expire), 10)\n}\n\n// The EDNS0_LOCAL option is used for local/experimental purposes. The option\n// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]\n// (RFC6891), although any unassigned code can actually be used.  The content of\n// the option is made available in Data, unaltered.\n// Basic use pattern for creating a local option:\n//\n//\to := new(dns.OPT)\n//\to.Hdr.Name = \".\"\n//\to.Hdr.Rrtype = dns.TypeOPT\n//\te := new(dns.EDNS0_LOCAL)\n//\te.Code = dns.EDNS0LOCALSTART\n//\te.Data = []byte{72, 82, 74}\n//\to.Option = append(o.Option, e)\ntype EDNS0_LOCAL struct {\n\tCode uint16\n\tData []byte\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_LOCAL) Option() uint16 { return e.Code }\n\nfunc (e *EDNS0_LOCAL) String() string {\n\treturn strconv.FormatInt(int64(e.Code), 10) + \":0x\" + hex.EncodeToString(e.Data)\n}\n\nfunc (e *EDNS0_LOCAL) copy() EDNS0 {\n\treturn &EDNS0_LOCAL{e.Code, cloneSlice(e.Data)}\n}\n\nfunc (e *EDNS0_LOCAL) pack() ([]byte, error) {\n\treturn cloneSlice(e.Data), nil\n}\n\nfunc (e *EDNS0_LOCAL) unpack(b []byte) error {\n\te.Data = cloneSlice(b)\n\treturn nil\n}\n\n// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep\n// the TCP connection alive. See RFC 7828.\ntype EDNS0_TCP_KEEPALIVE struct {\n\tCode uint16 // always EDNSTCPKEEPALIVE\n\n\t// Timeout is an idle timeout value for the TCP connection, specified in\n\t// units of 100 milliseconds, encoded in network byte order. If set to 0,\n\t// pack will return a nil slice.\n\tTimeout uint16\n\n\t// Length is the option's length.\n\t// Deprecated: this field is deprecated and is always equal to 0.\n\tLength uint16\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }\n\nfunc (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {\n\tif e.Timeout > 0 {\n\t\tb := make([]byte, 2)\n\t\tbinary.BigEndian.PutUint16(b, e.Timeout)\n\t\treturn b, nil\n\t}\n\treturn nil, nil\n}\n\nfunc (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {\n\tswitch len(b) {\n\tcase 0:\n\tcase 2:\n\t\te.Timeout = binary.BigEndian.Uint16(b)\n\tdefault:\n\t\treturn fmt.Errorf(\"length mismatch, want 0/2 but got %d\", len(b))\n\t}\n\treturn nil\n}\n\nfunc (e *EDNS0_TCP_KEEPALIVE) String() string {\n\ts := \"use tcp keep-alive\"\n\tif e.Timeout == 0 {\n\t\ts += \", timeout omitted\"\n\t} else {\n\t\ts += fmt.Sprintf(\", timeout %dms\", e.Timeout*100)\n\t}\n\treturn s\n}\n\nfunc (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Timeout, e.Length} }\n\n// EDNS0_PADDING option is used to add padding to a request/response. The default\n// value of padding SHOULD be 0x0 but other values MAY be used, for instance if\n// compression is applied before encryption which may break signatures.\ntype EDNS0_PADDING struct {\n\tPadding []byte\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_PADDING) Option() uint16        { return EDNS0PADDING }\nfunc (e *EDNS0_PADDING) pack() ([]byte, error) { return cloneSlice(e.Padding), nil }\nfunc (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = cloneSlice(b); return nil }\nfunc (e *EDNS0_PADDING) String() string        { return fmt.Sprintf(\"%0X\", e.Padding) }\nfunc (e *EDNS0_PADDING) copy() EDNS0           { return &EDNS0_PADDING{cloneSlice(e.Padding)} }\n\n// Extended DNS Error Codes (RFC 8914).\nconst (\n\tExtendedErrorCodeOther uint16 = iota\n\tExtendedErrorCodeUnsupportedDNSKEYAlgorithm\n\tExtendedErrorCodeUnsupportedDSDigestType\n\tExtendedErrorCodeStaleAnswer\n\tExtendedErrorCodeForgedAnswer\n\tExtendedErrorCodeDNSSECIndeterminate\n\tExtendedErrorCodeDNSBogus\n\tExtendedErrorCodeSignatureExpired\n\tExtendedErrorCodeSignatureNotYetValid\n\tExtendedErrorCodeDNSKEYMissing\n\tExtendedErrorCodeRRSIGsMissing\n\tExtendedErrorCodeNoZoneKeyBitSet\n\tExtendedErrorCodeNSECMissing\n\tExtendedErrorCodeCachedError\n\tExtendedErrorCodeNotReady\n\tExtendedErrorCodeBlocked\n\tExtendedErrorCodeCensored\n\tExtendedErrorCodeFiltered\n\tExtendedErrorCodeProhibited\n\tExtendedErrorCodeStaleNXDOMAINAnswer\n\tExtendedErrorCodeNotAuthoritative\n\tExtendedErrorCodeNotSupported\n\tExtendedErrorCodeNoReachableAuthority\n\tExtendedErrorCodeNetworkError\n\tExtendedErrorCodeInvalidData\n\tExtendedErrorCodeSignatureExpiredBeforeValid\n\tExtendedErrorCodeTooEarly\n\tExtendedErrorCodeUnsupportedNSEC3IterValue\n\tExtendedErrorCodeUnableToConformToPolicy\n\tExtendedErrorCodeSynthesized\n\tExtendedErrorCodeInvalidQueryType\n)\n\n// ExtendedErrorCodeToString maps extended error info codes to a human readable\n// description.\nvar ExtendedErrorCodeToString = map[uint16]string{\n\tExtendedErrorCodeOther:                       \"Other\",\n\tExtendedErrorCodeUnsupportedDNSKEYAlgorithm:  \"Unsupported DNSKEY Algorithm\",\n\tExtendedErrorCodeUnsupportedDSDigestType:     \"Unsupported DS Digest Type\",\n\tExtendedErrorCodeStaleAnswer:                 \"Stale Answer\",\n\tExtendedErrorCodeForgedAnswer:                \"Forged Answer\",\n\tExtendedErrorCodeDNSSECIndeterminate:         \"DNSSEC Indeterminate\",\n\tExtendedErrorCodeDNSBogus:                    \"DNSSEC Bogus\",\n\tExtendedErrorCodeSignatureExpired:            \"Signature Expired\",\n\tExtendedErrorCodeSignatureNotYetValid:        \"Signature Not Yet Valid\",\n\tExtendedErrorCodeDNSKEYMissing:               \"DNSKEY Missing\",\n\tExtendedErrorCodeRRSIGsMissing:               \"RRSIGs Missing\",\n\tExtendedErrorCodeNoZoneKeyBitSet:             \"No Zone Key Bit Set\",\n\tExtendedErrorCodeNSECMissing:                 \"NSEC Missing\",\n\tExtendedErrorCodeCachedError:                 \"Cached Error\",\n\tExtendedErrorCodeNotReady:                    \"Not Ready\",\n\tExtendedErrorCodeBlocked:                     \"Blocked\",\n\tExtendedErrorCodeCensored:                    \"Censored\",\n\tExtendedErrorCodeFiltered:                    \"Filtered\",\n\tExtendedErrorCodeProhibited:                  \"Prohibited\",\n\tExtendedErrorCodeStaleNXDOMAINAnswer:         \"Stale NXDOMAIN Answer\",\n\tExtendedErrorCodeNotAuthoritative:            \"Not Authoritative\",\n\tExtendedErrorCodeNotSupported:                \"Not Supported\",\n\tExtendedErrorCodeNoReachableAuthority:        \"No Reachable Authority\",\n\tExtendedErrorCodeNetworkError:                \"Network Error\",\n\tExtendedErrorCodeInvalidData:                 \"Invalid Data\",\n\tExtendedErrorCodeSignatureExpiredBeforeValid: \"Signature Expired Before Valid\",\n\tExtendedErrorCodeTooEarly:                    \"Too Early\",\n\tExtendedErrorCodeUnsupportedNSEC3IterValue:   \"Unsupported NSEC3 Iterations Value\",\n\tExtendedErrorCodeUnableToConformToPolicy:     \"Unable To Conform To Policy\",\n\tExtendedErrorCodeSynthesized:                 \"Synthesized\",\n\tExtendedErrorCodeInvalidQueryType:            \"Invalid Query Type\",\n}\n\n// StringToExtendedErrorCode is a map from human readable descriptions to\n// extended error info codes.\nvar StringToExtendedErrorCode = reverseInt16(ExtendedErrorCodeToString)\n\n// EDNS0_EDE option is used to return additional information about the cause of\n// DNS errors.\ntype EDNS0_EDE struct {\n\tInfoCode  uint16\n\tExtraText string\n}\n\n// Option implements the EDNS0 interface.\nfunc (e *EDNS0_EDE) Option() uint16 { return EDNS0EDE }\nfunc (e *EDNS0_EDE) copy() EDNS0    { return &EDNS0_EDE{e.InfoCode, e.ExtraText} }\n\nfunc (e *EDNS0_EDE) String() string {\n\tinfo := strconv.FormatUint(uint64(e.InfoCode), 10)\n\tif s, ok := ExtendedErrorCodeToString[e.InfoCode]; ok {\n\t\tinfo += fmt.Sprintf(\" (%s)\", s)\n\t}\n\treturn fmt.Sprintf(\"%s: (%s)\", info, e.ExtraText)\n}\n\nfunc (e *EDNS0_EDE) pack() ([]byte, error) {\n\tb := make([]byte, 2+len(e.ExtraText))\n\tbinary.BigEndian.PutUint16(b[0:], e.InfoCode)\n\tcopy(b[2:], e.ExtraText)\n\treturn b, nil\n}\n\nfunc (e *EDNS0_EDE) unpack(b []byte) error {\n\tif len(b) < 2 {\n\t\treturn ErrBuf\n\t}\n\te.InfoCode = binary.BigEndian.Uint16(b[0:])\n\te.ExtraText = string(b[2:])\n\treturn nil\n}\n\n// The EDNS0_ESU option for ENUM Source-URI Extension.\ntype EDNS0_ESU struct {\n\tCode uint16 // always EDNS0ESU\n\tUri  string\n}\n\nfunc (e *EDNS0_ESU) Option() uint16        { return EDNS0ESU }\nfunc (e *EDNS0_ESU) String() string        { return e.Uri }\nfunc (e *EDNS0_ESU) copy() EDNS0           { return &EDNS0_ESU{e.Code, e.Uri} }\nfunc (e *EDNS0_ESU) pack() ([]byte, error) { return []byte(e.Uri), nil }\nfunc (e *EDNS0_ESU) unpack(b []byte) error {\n\te.Uri = string(b)\n\treturn nil\n}\n\n// EDNS0_REPORTING implements the EDNS0 Reporting Channel option (RFC 9567).\ntype EDNS0_REPORTING struct {\n\tCode        uint16 // always EDNS0REPORTING\n\tAgentDomain string\n}\n\nfunc (e *EDNS0_REPORTING) Option() uint16 { return EDNS0REPORTING }\nfunc (e *EDNS0_REPORTING) String() string { return e.AgentDomain }\nfunc (e *EDNS0_REPORTING) copy() EDNS0    { return &EDNS0_REPORTING{e.Code, e.AgentDomain} }\nfunc (e *EDNS0_REPORTING) pack() ([]byte, error) {\n\tb := make([]byte, 255)\n\toff1, err := PackDomainName(Fqdn(e.AgentDomain), b, 0, nil, false)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"bad agent domain: %w\", err)\n\t}\n\treturn b[:off1], nil\n}\nfunc (e *EDNS0_REPORTING) unpack(b []byte) error {\n\tdomain, _, err := UnpackDomainName(b, 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"bad agent domain: %w\", err)\n\t}\n\te.AgentDomain = domain\n\treturn nil\n}\n\n// EDNS0_ZONEVERSION implements the EDNS0 Zone Version option (RFC 9660).\ntype EDNS0_ZONEVERSION struct {\n\t// always EDNS0ZONEVERSION (19)\n\tCode uint16\n\t// An unsigned 1-octet Label Count indicating\n\t// the number of labels for the name of the zone that VERSION value refers to.\n\tLabelCount uint8\n\t// An unsigned 1-octet type number distinguishing the format and meaning of version.\n\t// 0 SOA-SERIAL, 1-245 Unassigned, 246-255 Reserved for private use, see RFC 9660.\n\tType uint8\n\t// An opaque octet string conveying the zone version data (VERSION).\n\tVersion string\n}\n\nfunc (e *EDNS0_ZONEVERSION) Option() uint16 { return EDNS0ZONEVERSION }\nfunc (e *EDNS0_ZONEVERSION) String() string { return e.Version }\nfunc (e *EDNS0_ZONEVERSION) copy() EDNS0 {\n\treturn &EDNS0_ZONEVERSION{e.Code, e.LabelCount, e.Type, e.Version}\n}\nfunc (e *EDNS0_ZONEVERSION) pack() ([]byte, error) {\n\tb := []byte{\n\t\t// first octet label count\n\t\te.LabelCount,\n\t\t// second octet is type\n\t\te.Type,\n\t}\n\tif len(e.Version) > 0 {\n\t\tb = append(b, []byte(e.Version)...)\n\t}\n\treturn b, nil\n}\nfunc (e *EDNS0_ZONEVERSION) unpack(b []byte) error {\n\tif len(b) < 2 {\n\t\treturn ErrBuf\n\t}\n\te.LabelCount = b[0]\n\te.Type = b[1]\n\tif len(b) > 2 {\n\t\te.Version = string(b[2:])\n\t} else {\n\t\te.Version = \"\"\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "edns_test.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"testing\"\n)\n\nfunc TestOPTTtl(t *testing.T) {\n\te := &OPT{}\n\te.Hdr.Name = \".\"\n\te.Hdr.Rrtype = TypeOPT\n\n\t// verify the default setting of DO=0\n\tif e.Do() {\n\t\tt.Errorf(\"DO bit should be zero\")\n\t}\n\n\t// There are 6 possible invocations of SetDo():\n\t//\n\t// 1. Starting with DO=0, using SetDo()\n\t// 2. Starting with DO=0, using SetDo(true)\n\t// 3. Starting with DO=0, using SetDo(false)\n\t// 4. Starting with DO=1, using SetDo()\n\t// 5. Starting with DO=1, using SetDo(true)\n\t// 6. Starting with DO=1, using SetDo(false)\n\n\t// verify that invoking SetDo() sets DO=1 (TEST #1)\n\te.SetDo()\n\tif !e.Do() {\n\t\tt.Errorf(\"DO bit should be non-zero\")\n\t}\n\t// verify that using SetDo(true) works when DO=1 (TEST #5)\n\te.SetDo(true)\n\tif !e.Do() {\n\t\tt.Errorf(\"DO bit should still be non-zero\")\n\t}\n\t// verify that we can use SetDo(false) to set DO=0 (TEST #6)\n\te.SetDo(false)\n\tif e.Do() {\n\t\tt.Errorf(\"DO bit should be zero\")\n\t}\n\t// verify that if we call SetDo(false) when DO=0 that it is unchanged (TEST #3)\n\te.SetDo(false)\n\tif e.Do() {\n\t\tt.Errorf(\"DO bit should still be zero\")\n\t}\n\t// verify that using SetDo(true) works for DO=0 (TEST #2)\n\te.SetDo(true)\n\tif !e.Do() {\n\t\tt.Errorf(\"DO bit should be non-zero\")\n\t}\n\t// verify that using SetDo() works for DO=1 (TEST #4)\n\te.SetDo()\n\tif !e.Do() {\n\t\tt.Errorf(\"DO bit should be non-zero\")\n\t}\n\n\t// CO (Compact ANswers OK) flag tests follow the same pattern as DO tests\n\t// verify that invoking SetCo() sets CO=1\n\te.SetCo()\n\tif !e.Co() {\n\t\tt.Errorf(\"CO bit should be non-zero\")\n\t}\n\n\t// verify that using SetCo(true) works when CO=1\n\te.SetCo(true)\n\tif !e.Co() {\n\t\tt.Errorf(\"CO bit should still be non-zero\")\n\t}\n\t// verify that we can use SetCo(false) to set CO=0\n\te.SetCo(false)\n\tif e.Co() {\n\t\tt.Errorf(\"CO bit should be zero\")\n\t}\n\t// verify that if we call SetCo(false) when CO=0 that it is unchanged\n\te.SetCo(false)\n\tif e.Co() {\n\t\tt.Errorf(\"CO bit should still be zero\")\n\t}\n\t// verify that using SetCo(true) works for CO=0\n\te.SetCo(true)\n\tif !e.Co() {\n\t\tt.Errorf(\"CO bit should be non-zero\")\n\t}\n\t// verify that using SetCo() works for CO=1\n\te.SetCo()\n\tif !e.Co() {\n\t\tt.Errorf(\"CO bit should be non-zero\")\n\t}\n\n\tif e.Version() != 0 {\n\t\tt.Errorf(\"version should be non-zero\")\n\t}\n\n\te.SetVersion(42)\n\tif e.Version() != 42 {\n\t\tt.Errorf(\"set 42, expected %d, got %d\", 42, e.Version())\n\t}\n\n\te.SetExtendedRcode(42)\n\t// ExtendedRcode has the last 4 bits set to 0.\n\tif e.ExtendedRcode() != 42&0xFFFFFFF0 {\n\t\tt.Errorf(\"set 42, expected %d, got %d\", 42&0xFFFFFFF0, e.ExtendedRcode())\n\t}\n\n\t// This will reset the 8 upper bits of the extended rcode\n\te.SetExtendedRcode(RcodeNotAuth)\n\tif e.ExtendedRcode() != 0 {\n\t\tt.Errorf(\"Setting a non-extended rcode is expected to set extended rcode to 0, got: %d\", e.ExtendedRcode())\n\t}\n}\n\nfunc TestEDNS0_SUBNETUnpack(t *testing.T) {\n\tfor _, ip := range []net.IP{\n\t\tnet.IPv4(0xde, 0xad, 0xbe, 0xef),\n\t\tnet.ParseIP(\"192.0.2.1\"),\n\t\tnet.ParseIP(\"2001:db8::68\"),\n\t} {\n\t\tvar s1 EDNS0_SUBNET\n\t\ts1.Address = ip\n\n\t\tif ip.To4() == nil {\n\t\t\ts1.Family = 2\n\t\t\ts1.SourceNetmask = net.IPv6len * 8\n\t\t} else {\n\t\t\ts1.Family = 1\n\t\t\ts1.SourceNetmask = net.IPv4len * 8\n\t\t}\n\n\t\tb, err := s1.pack()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to pack: %v\", err)\n\t\t}\n\n\t\tvar s2 EDNS0_SUBNET\n\t\tif err := s2.unpack(b); err != nil {\n\t\t\tt.Fatalf(\"failed to unpack: %v\", err)\n\t\t}\n\n\t\tif !ip.Equal(s2.Address) {\n\t\t\tt.Errorf(\"address different after unpacking; expected %s, got %s\", ip, s2.Address)\n\t\t}\n\t}\n}\n\nfunc TestEDNS0_UL(t *testing.T) {\n\tcases := []struct {\n\t\tl  uint32\n\t\tkl uint32\n\t}{\n\t\t{0x01234567, 0},\n\t\t{0x76543210, 0xFEDCBA98},\n\t}\n\tfor _, c := range cases {\n\t\texpect := EDNS0_UL{EDNS0UL, c.l, c.kl}\n\t\tb, err := expect.pack()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to pack: %v\", err)\n\t\t}\n\t\tactual := EDNS0_UL{EDNS0UL, ^uint32(0), ^uint32(0)}\n\t\tif err := actual.unpack(b); err != nil {\n\t\t\tt.Fatalf(\"failed to unpack: %v\", err)\n\t\t}\n\t\tif expect != actual {\n\t\t\tt.Errorf(\"unpacked option is different; expected %v, got %v\", expect, actual)\n\t\t}\n\t}\n}\n\nfunc TestZ(t *testing.T) {\n\te := &OPT{}\n\te.Hdr.Name = \".\"\n\te.Hdr.Rrtype = TypeOPT\n\te.SetVersion(8)\n\te.SetDo()\n\te.SetCo()\n\tif e.Z() != 0 {\n\t\tt.Errorf(\"expected Z of 0, got %d\", e.Z())\n\t}\n\te.SetZ(5)\n\tif e.Z() != 5 {\n\t\tt.Errorf(\"expected Z of 5, got %d\", e.Z())\n\t}\n\te.SetZ(0xFFFF)\n\tif e.Z() != 0x3FFF {\n\t\tt.Errorf(\"expected Z of 0x3FFFF, got %d\", e.Z())\n\t}\n\tif e.Version() != 8 {\n\t\tt.Errorf(\"expected version to still be 8, got %d\", e.Version())\n\t}\n\tif !e.Do() {\n\t\tt.Error(\"expected DO to be set\")\n\t}\n}\n\nfunc TestEDNS0_ESU(t *testing.T) {\n\tp := []byte{\n\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x29, 0x04,\n\t\t0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00,\n\t\t0x04, 0x00, 0x24, 0x73, 0x69, 0x70, 0x3A, 0x2B,\n\t\t0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,\n\t\t0x39, 0x40, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x63,\n\t\t0x6F, 0x6D, 0x3B, 0x75, 0x73, 0x65, 0x72, 0x3D,\n\t\t0x63, 0x67, 0x72, 0x61, 0x74, 0x65, 0x73,\n\t}\n\n\tm := new(Msg)\n\tif err := m.Unpack(p); err != nil {\n\t\tt.Fatalf(\"failed to unpack: %v\", err)\n\t}\n\topt := m.IsEdns0()\n\tif opt == nil {\n\t\tt.Fatalf(\"expected edns0 option\")\n\t}\n\tif len(opt.Option) != 1 {\n\t\tt.Fatalf(\"expected only one option: %v\", opt.Option)\n\t}\n\tedns0 := opt.Option[0]\n\tesu, ok := edns0.(*EDNS0_ESU)\n\tif !ok {\n\t\tt.Fatalf(\"expected option of type EDNS0_ESU, got %t\", edns0)\n\t}\n\texpect := \"sip:+123456789@test.com;user=cgrates\"\n\tif esu.Uri != expect {\n\t\tt.Errorf(\"unpacked option is different; expected %v, got %v\", expect, esu.Uri)\n\t}\n}\n\nfunc TestEDNS0_TCP_KEEPALIVE_unpack(t *testing.T) {\n\tcases := []struct {\n\t\tname        string\n\t\tb           []byte\n\t\texpected    uint16\n\t\texpectedErr bool\n\t}{\n\t\t{\n\t\t\tname:     \"empty\",\n\t\t\tb:        []byte{},\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tname:     \"timeout 1\",\n\t\t\tb:        []byte{0, 1},\n\t\t\texpected: 1,\n\t\t},\n\t\t{\n\t\t\tname:        \"invalid\",\n\t\t\tb:           []byte{0, 1, 3},\n\t\t\texpectedErr: true,\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\te := &EDNS0_TCP_KEEPALIVE{}\n\t\t\terr := e.unpack(tc.b)\n\t\t\tif err != nil && !tc.expectedErr {\n\t\t\t\tt.Error(\"failed to unpack, expected no error\")\n\t\t\t}\n\t\t\tif err == nil && tc.expectedErr {\n\t\t\t\tt.Error(\"unpacked, but expected an error\")\n\t\t\t}\n\t\t\tif e.Timeout != tc.expected {\n\t\t\t\tt.Errorf(\"invalid timeout, actual: %d, expected: %d\", e.Timeout, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEDNS0_TCP_KEEPALIVE_pack(t *testing.T) {\n\tcases := []struct {\n\t\tname     string\n\t\tedns     *EDNS0_TCP_KEEPALIVE\n\t\texpected []byte\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tedns: &EDNS0_TCP_KEEPALIVE{\n\t\t\t\tCode:    EDNS0TCPKEEPALIVE,\n\t\t\t\tTimeout: 0,\n\t\t\t},\n\t\t\texpected: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"timeout 1\",\n\t\t\tedns: &EDNS0_TCP_KEEPALIVE{\n\t\t\t\tCode:    EDNS0TCPKEEPALIVE,\n\t\t\t\tTimeout: 1,\n\t\t\t},\n\t\t\texpected: []byte{0, 1},\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tb, err := tc.edns.pack()\n\t\t\tif err != nil {\n\t\t\t\tt.Error(\"expected no error\")\n\t\t\t}\n\n\t\t\tif tc.expected == nil && b != nil {\n\t\t\t\tt.Errorf(\"invalid result, expected nil\")\n\t\t\t}\n\n\t\t\tres := bytes.Compare(b, tc.expected)\n\t\t\tif res != 0 {\n\t\t\t\tt.Errorf(\"invalid result, expected: %v, actual: %v\", tc.expected, b)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "example_test.go",
    "content": "package dns_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\n\t\"github.com/miekg/dns\"\n)\n\n// Retrieve the MX records for miek.nl.\nfunc ExampleMX() {\n\tconfig, _ := dns.ClientConfigFromFile(\"/etc/resolv.conf\")\n\tc := new(dns.Client)\n\tm := new(dns.Msg)\n\tm.SetQuestion(\"miek.nl.\", dns.TypeMX)\n\tm.RecursionDesired = true\n\tr, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port))\n\tif err != nil {\n\t\treturn\n\t}\n\tif r.Rcode != dns.RcodeSuccess {\n\t\treturn\n\t}\n\tfor _, a := range r.Answer {\n\t\tif mx, ok := a.(*dns.MX); ok {\n\t\t\tfmt.Printf(\"%s\\n\", mx.String())\n\t\t}\n\t}\n}\n\n// Retrieve the DNSKEY records of a zone and convert them\n// to DS records for SHA1, SHA256 and SHA384.\nfunc ExampleDS() {\n\tconfig, _ := dns.ClientConfigFromFile(\"/etc/resolv.conf\")\n\tc := new(dns.Client)\n\tm := new(dns.Msg)\n\tzone := \"miek.nl\"\n\tm.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)\n\tm.SetEdns0(4096, true)\n\tr, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port))\n\tif err != nil {\n\t\treturn\n\t}\n\tif r.Rcode != dns.RcodeSuccess {\n\t\treturn\n\t}\n\tfor _, k := range r.Answer {\n\t\tif key, ok := k.(*dns.DNSKEY); ok {\n\t\t\tfor _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} {\n\t\t\t\tfmt.Printf(\"%s; %d\\n\", key.ToDS(alg).String(), key.Flags)\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst TypeAPAIR = 0x0F99\n\ntype APAIR struct {\n\taddr [2]net.IP\n}\n\nfunc NewAPAIR() dns.PrivateRdata { return new(APAIR) }\n\nfunc (rd *APAIR) String() string { return rd.addr[0].String() + \" \" + rd.addr[1].String() }\n\nfunc (rd *APAIR) Parse(txt []string) error {\n\tif len(txt) != 2 {\n\t\treturn errors.New(\"two addresses required for APAIR\")\n\t}\n\tfor i, s := range txt {\n\t\tip := net.ParseIP(s)\n\t\tif ip == nil {\n\t\t\treturn errors.New(\"invalid IP in APAIR text representation\")\n\t\t}\n\t\trd.addr[i] = ip\n\t}\n\treturn nil\n}\n\nfunc (rd *APAIR) Pack(buf []byte) (int, error) {\n\tb := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)\n\tn := copy(buf, b)\n\tif n != len(b) {\n\t\treturn n, dns.ErrBuf\n\t}\n\treturn n, nil\n}\n\nfunc (rd *APAIR) Unpack(buf []byte) (int, error) {\n\tln := net.IPv4len * 2\n\tif len(buf) != ln {\n\t\treturn 0, errors.New(\"invalid length of APAIR rdata\")\n\t}\n\tcp := make([]byte, ln)\n\tcopy(cp, buf) // clone bytes to use them in IPs\n\n\trd.addr[0] = net.IP(cp[:3])\n\trd.addr[1] = net.IP(cp[4:])\n\n\treturn len(buf), nil\n}\n\nfunc (rd *APAIR) Copy(dest dns.PrivateRdata) error {\n\tcp := make([]byte, rd.Len())\n\t_, err := rd.Pack(cp)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\td := dest.(*APAIR)\n\td.addr[0] = net.IP(cp[:3])\n\td.addr[1] = net.IP(cp[4:])\n\treturn nil\n}\n\nfunc (rd *APAIR) Len() int {\n\treturn net.IPv4len * 2\n}\n\nfunc ExamplePrivateHandle() {\n\tdns.PrivateHandle(\"APAIR\", TypeAPAIR, NewAPAIR)\n\tdefer dns.PrivateHandleRemove(TypeAPAIR)\n\tvar oldId = dns.Id\n\tdns.Id = func() uint16 { return 3 }\n\tdefer func() { dns.Id = oldId }()\n\n\trr, err := dns.NewRR(\"miek.nl. APAIR (1.2.3.4    1.2.3.5)\")\n\tif err != nil {\n\t\tlog.Fatal(\"could not parse APAIR record: \", err)\n\t}\n\tfmt.Println(rr) // see first line of Output below\n\n\tm := new(dns.Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeAPAIR)\n\tm.Answer = append(m.Answer, rr)\n\n\tfmt.Println(m)\n\t// Output: miek.nl.\t3600\tIN\tAPAIR\t1.2.3.4 1.2.3.5\n\t// ;; opcode: QUERY, status: NOERROR, id: 3\n\t// ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0\n\t//\n\t// ;; QUESTION SECTION:\n\t// ;miek.nl.\tIN\t APAIR\n\t//\n\t// ;; ANSWER SECTION:\n\t// miek.nl.\t3600\tIN\tAPAIR\t1.2.3.4 1.2.3.5\n}\n"
  },
  {
    "path": "format.go",
    "content": "package dns\n\nimport (\n\t\"net\"\n\t\"reflect\"\n\t\"strconv\"\n)\n\n// NumField returns the number of rdata fields r has.\nfunc NumField(r RR) int {\n\treturn reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header\n}\n\n// Field returns the rdata field i as a string. Fields are indexed starting from 1.\n// RR types that holds slice data, for instance the NSEC type bitmap will return a single\n// string where the types are concatenated using a space.\n// Accessing non existing fields will cause a panic.\nfunc Field(r RR, i int) string {\n\tif i == 0 {\n\t\treturn \"\"\n\t}\n\td := reflect.ValueOf(r).Elem().Field(i)\n\tswitch d.Kind() {\n\tcase reflect.String:\n\t\treturn d.String()\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\treturn strconv.FormatInt(d.Int(), 10)\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\treturn strconv.FormatUint(d.Uint(), 10)\n\tcase reflect.Slice:\n\t\tswitch reflect.ValueOf(r).Elem().Type().Field(i).Tag {\n\t\tcase `dns:\"a\"`:\n\t\t\t// TODO(miek): Hmm store this as 16 bytes\n\t\t\tif d.Len() < net.IPv4len {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\tif d.Len() < net.IPv6len {\n\t\t\t\treturn net.IPv4(byte(d.Index(0).Uint()),\n\t\t\t\t\tbyte(d.Index(1).Uint()),\n\t\t\t\t\tbyte(d.Index(2).Uint()),\n\t\t\t\t\tbyte(d.Index(3).Uint())).String()\n\t\t\t}\n\t\t\treturn net.IPv4(byte(d.Index(12).Uint()),\n\t\t\t\tbyte(d.Index(13).Uint()),\n\t\t\t\tbyte(d.Index(14).Uint()),\n\t\t\t\tbyte(d.Index(15).Uint())).String()\n\t\tcase `dns:\"aaaa\"`:\n\t\t\tif d.Len() < net.IPv6len {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn net.IP{\n\t\t\t\tbyte(d.Index(0).Uint()),\n\t\t\t\tbyte(d.Index(1).Uint()),\n\t\t\t\tbyte(d.Index(2).Uint()),\n\t\t\t\tbyte(d.Index(3).Uint()),\n\t\t\t\tbyte(d.Index(4).Uint()),\n\t\t\t\tbyte(d.Index(5).Uint()),\n\t\t\t\tbyte(d.Index(6).Uint()),\n\t\t\t\tbyte(d.Index(7).Uint()),\n\t\t\t\tbyte(d.Index(8).Uint()),\n\t\t\t\tbyte(d.Index(9).Uint()),\n\t\t\t\tbyte(d.Index(10).Uint()),\n\t\t\t\tbyte(d.Index(11).Uint()),\n\t\t\t\tbyte(d.Index(12).Uint()),\n\t\t\t\tbyte(d.Index(13).Uint()),\n\t\t\t\tbyte(d.Index(14).Uint()),\n\t\t\t\tbyte(d.Index(15).Uint()),\n\t\t\t}.String()\n\t\tcase `dns:\"nsec\"`:\n\t\t\tif d.Len() == 0 {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\ts := Type(d.Index(0).Uint()).String()\n\t\t\tfor i := 1; i < d.Len(); i++ {\n\t\t\t\ts += \" \" + Type(d.Index(i).Uint()).String()\n\t\t\t}\n\t\t\treturn s\n\t\tdefault:\n\t\t\t// if it does not have a tag its a string slice\n\t\t\tfallthrough\n\t\tcase `dns:\"txt\"`:\n\t\t\tif d.Len() == 0 {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\ts := d.Index(0).String()\n\t\t\tfor i := 1; i < d.Len(); i++ {\n\t\t\t\ts += \" \" + d.Index(i).String()\n\t\t\t}\n\t\t\treturn s\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "format_test.go",
    "content": "package dns\n\nimport (\n\t\"testing\"\n)\n\nfunc TestFieldEmptyAOrAAAAData(t *testing.T) {\n\tres := Field(new(A), 1)\n\tif res != \"\" {\n\t\tt.Errorf(\"expected empty string but got %v\", res)\n\t}\n\tres = Field(new(AAAA), 1)\n\tif res != \"\" {\n\t\tt.Errorf(\"expected empty string but got %v\", res)\n\t}\n}\n"
  },
  {
    "path": "fuzz.go",
    "content": "//go:build fuzz\n// +build fuzz\n\npackage dns\n\nimport \"strings\"\n\nfunc Fuzz(data []byte) int {\n\tmsg := new(Msg)\n\n\tif err := msg.Unpack(data); err != nil {\n\t\treturn 0\n\t}\n\tif _, err := msg.Pack(); err != nil {\n\t\treturn 0\n\t}\n\n\treturn 1\n}\n\nfunc FuzzNewRR(data []byte) int {\n\tstr := string(data)\n\t// Do not fuzz lines that include the $INCLUDE keyword and hint the fuzzer\n\t// at avoiding them.\n\t// See GH#1025 for context.\n\tif strings.Contains(strings.ToUpper(str), \"$INCLUDE\") {\n\t\treturn -1\n\t}\n\tif _, err := NewRR(str); err != nil {\n\t\treturn 0\n\t}\n\treturn 1\n}\n"
  },
  {
    "path": "fuzz_test.go",
    "content": "package dns\n\nimport (\n\t\"net\"\n\t\"testing\"\n)\n\n// TestPackDataOpt tests generated using fuzz.go and with a message pack\n// containing the following bytes:\n// \"0000\\x00\\x00000000\\x00\\x00/00000\" +\n// \"0\\x00\\v\\x00#\\b00000000\\x00\\x00)000\" +\n// \"000\\x00\\x1c00\\x00\\x0000\\x00\\x01000\\x00\\x00\\x00\\b\" +\n// \"\\x00\\v\\x00\\x02\\x0000000000\"\n// That bytes sequence created the overflow error.\nfunc TestPackDataOpt(t *testing.T) {\n\ttype args struct {\n\t\toption []EDNS0\n\t\tmsg    []byte\n\t\toff    int\n\t}\n\ttests := []struct {\n\t\tname       string\n\t\targs       args\n\t\twant       int\n\t\twantErr    bool\n\t\twantErrMsg string\n\t}{\n\t\t{\n\t\t\tname: \"overflow\",\n\t\t\targs: args{\n\t\t\t\toption: []EDNS0{\n\t\t\t\t\t&EDNS0_LOCAL{Code: 0x3030, Data: []uint8{}},\n\t\t\t\t\t&EDNS0_LOCAL{Code: 0x3030, Data: []uint8{0x30}},\n\t\t\t\t\t&EDNS0_LOCAL{Code: 0x3030, Data: []uint8{}},\n\t\t\t\t\t&EDNS0_SUBNET{\n\t\t\t\t\t\tCode: 0x0, Family: 0x2,\n\t\t\t\t\t\tSourceNetmask: 0x0, SourceScope: 0x30,\n\t\t\t\t\t\tAddress: net.IP{0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},\n\t\t\t\t},\n\t\t\t\tmsg: []byte{\n\t\t\t\t\t0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x2,\n\t\t\t\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x30,\n\t\t\t\t\t0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x0b, 0x00,\n\t\t\t\t\t0x23, 0x08, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\n\t\t\t\t\t0x30, 0x30, 0x00, 0x00, 0x29, 0x30, 0x30, 0x30,\n\t\t\t\t\t0x30, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,\n\t\t\t\t\t0x00, 0x30, 0x30, 0x00, 0x01, 0x30, 0x00, 0x00,\n\t\t\t\t\t0x00,\n\t\t\t\t},\n\t\t\t\toff: 54,\n\t\t\t},\n\t\t\twantErr:    true,\n\t\t\twantErrMsg: \"dns: overflow packing opt\",\n\t\t\twant:       57,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := packDataOpt(tt.args.option, tt.args.msg, tt.args.off)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"packDataOpt() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil && tt.wantErrMsg != err.Error() {\n\t\t\t\tt.Errorf(\"packDataOpt() error msg = %v, wantErrMsg %v\", err.Error(), tt.wantErrMsg)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"packDataOpt() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestCrashNSEC tests generated using fuzz.go and with a message pack\n// containing the following bytes:\n// \"0000\\x00\\x00000000\\x00\\x00/00000\" +\n// \"0\\x00\\v\\x00#\\b00000\\x00\\x00\\x00\\x00\\x00\\x1a000\" +\n// \"000\\x00\\x00\\x00\\x00\\x1a000000\\x00\\x00\\x00\\x00\\x1a0\" +\n// \"00000\\x00\\v00\\a0000000\\x00\"\n// That byte sequence, when Unpack() and subsequent Pack() created a\n// panic: runtime error: slice bounds out of range\n// which was attributed to the fact that NSEC RR length computation was different (and smaller)\n// then when within packDataNsec.\nfunc TestCrashNSEC(t *testing.T) {\n\tcompression := make(map[string]struct{})\n\tnsec := &NSEC{\n\t\tHdr: RR_Header{\n\t\t\tName:     \".\",\n\t\t\tRrtype:   0x2f,\n\t\t\tClass:    0x3030,\n\t\t\tTtl:      0x30303030,\n\t\t\tRdlength: 0xb,\n\t\t},\n\t\tNextDomain: \".\",\n\t\tTypeBitMap: []uint16{\n\t\t\t0x2302, 0x2303, 0x230a, 0x230b,\n\t\t\t0x2312, 0x2313, 0x231a, 0x231b,\n\t\t\t0x2322, 0x2323,\n\t\t},\n\t}\n\texpectedLength := 19\n\tl := nsec.len(0, compression)\n\tif l != expectedLength {\n\t\tt.Fatalf(\"expected length of %d, got %d\", expectedLength, l)\n\t}\n}\n\n// TestCrashNSEC3 tests generated using fuzz.go and with a message pack\n// containing the following bytes:\n// \"0000\\x00\\x00000000\\x00\\x00200000\" +\n// \"0\\x00\\v0000\\x00\\x00#\\x0300\\x00\\x00\\x00\\x1a000\" +\n// \"000\\x00\\v00\\x0200\\x00\\x03000\\x00\"\n// That byte sequence, when Unpack() and subsequent Pack() created a\n// panic: runtime error: slice bounds out of range\n// which was attributed to the fact that NSEC3 RR length computation was\n// different (and smaller) then within NSEC3.pack (which relies on\n// packDataNsec).\nfunc TestCrashNSEC3(t *testing.T) {\n\tcompression := make(map[string]struct{})\n\tnsec3 := &NSEC3{\n\t\tHdr: RR_Header{\n\t\t\tName:     \".\",\n\t\t\tRrtype:   0x32,\n\t\t\tClass:    0x3030,\n\t\t\tTtl:      0x30303030,\n\t\t\tRdlength: 0xb,\n\t\t},\n\t\tHash:       0x30,\n\t\tFlags:      0x30,\n\t\tIterations: 0x3030,\n\t\tSaltLength: 0x0,\n\t\tSalt:       \"\",\n\t\tHashLength: 0x0,\n\t\tNextDomain: \".\",\n\t\tTypeBitMap: []uint16{\n\t\t\t0x2302, 0x2303, 0x230a, 0x230b,\n\t\t},\n\t}\n\texpectedLength := 24\n\tl := nsec3.len(0, compression)\n\tif l != expectedLength {\n\t\tt.Fatalf(\"expected length of %d, got %d\", expectedLength, l)\n\t}\n}\n\n// TestNewRRCommentLengthCrasherString test inputs to NewRR that generated crashes.\nfunc TestNewRRCommentLengthCrasherString(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   string\n\t\terr  string\n\t}{\n\n\t\t{\n\t\t\t\"HINFO1\", \" HINFO ;;;;;;;;;;;;;\" +\n\t\t\t\t\";;;;;;;;\\x00\\x19;;;;;;;;;;\" +\n\t\t\t\t\";\\u007f;;;;;;;;;;;;;;;;;;\" +\n\t\t\t\t\";;}mP_Qq_3sJ_1_84X_5\" +\n\t\t\t\t\"45iW_3K4p8J8_v9_LT3_\" +\n\t\t\t\t\"6_0l_3D4VT3xq6N_3K__\" +\n\t\t\t\t\"_U_xX2m;;;;;;(;;;;;;\" +\n\t\t\t\t\";;;;;;;;;;;;;;;\\x1d;;;;\" +\n\t\t\t\t\";;;;;;-0x804dBDe8ba \" +\n\t\t\t\t\"\\t \\t\\tr  HINFO \\\" \\t\\t\\tve\" +\n\t\t\t\t\"k1xH11e__P6_dk1_51bo\" +\n\t\t\t\t\"g8gJK1V_O_v84_Bw4_1_\" +\n\t\t\t\t\"72jQ3_0J3V_S5iYn4h5X\" +\n\t\t\t\t\"R_2n___51J nN_  \\t\\tm \" +\n\t\t\t\t\"aa_XO4_5\\t   \\t\\t \\t\\tg6b\" +\n\t\t\t\t\"p_KI_1_YWc_K8c2b___A\" +\n\t\t\t\t\"e_Y1m__4Y_R_avy6t08x\" +\n\t\t\t\t\"b5Cp9_7uS_yLa\\t\\t\\t  d \" +\n\t\t\t\t\"EKe1Q83vS___ a  \\t\\t  \" +\n\t\t\t\t\"\\tmP_Qq_3sJ_1_84X_545\" +\n\t\t\t\t\"iW_3K4p8J8_v9_LT3_6_\" +\n\t\t\t\t\"0l_3D4VT3xq6N_3K___U\" +\n\t\t\t\t\"_xX2\\\"\\\"   \\t \\t_fL Ogl5\" +\n\t\t\t\t\"_09i_9__3O7C__QMAG2U\" +\n\t\t\t\t\"35IO8RRU6aJ9_6_57_6_\" +\n\t\t\t\t\"b05BMoX5I__4833_____\" +\n\t\t\t\t\"yfD_2_OPs__sqzM_pqQi\" +\n\t\t\t\t\"_\\t\\t \\tN__GuY4_Trath_0\" +\n\t\t\t\t\"yy___cAK_a__0J0q5 L_\" +\n\t\t\t\t\"p63Fzdva_Lb_29V7_R__\" +\n\t\t\t\t\"Go_H2_8m_4__FJM5B_Y5\" +\n\t\t\t\t\"Slw_ghp_55l_X2_Pnt6Y\" +\n\t\t\t\t\"_Wd_hM7jRZ_\\t\\t   \\tm \\t\" +\n\t\t\t\t\"  \\t\\ta md rK \\x00 7_\\\"sr \" +\n\t\t\t\t\"- sg o  -0x804dBDe8b\" +\n\t\t\t\t\"a \\t \\t\\tN_W6J3PBS_W__C\" +\n\t\t\t\t\"yJu__k6F_jY0INI_LC27\" +\n\t\t\t\t\"7x14b_1b___Y8f_K_3y_\" +\n\t\t\t\t\"0055yaP_LKu_72g_T_32\" +\n\t\t\t\t\"iBk1Zm_o  9i1P44_S0_\" +\n\t\t\t\t\"_4AXUpo2__H55tL_g78_\" +\n\t\t\t\t\"8V_8l0yg6bp_KI_1_YWc\" +\n\t\t\t\t\"_K8c2b  \\t \\tmaa_XO4_5\" +\n\t\t\t\t\"rg6bp_KI_1_YWc_K8c2b\" +\n\t\t\t\t\" _C20w i_4 \\t\\t  u_k d\" +\n\t\t\t\t\" rKsg09099 \\\"\\\"2335779\" +\n\t\t\t\t\"05047986112651e025 \\t\" +\n\t\t\t\t\" \\t\\tN_W6J3PBS_W__CyJu\" +\n\t\t\t\t\"__k6F_jY0INI_LC277x1\" +\n\t\t\t\t\"4b_1b___Y8f_K_3y_005\" +\n\t\t\t\t\"5yaP_LKu_72g_T_32iBk\" +\n\t\t\t\t\"1Zm_o  9i1P44_S0__4A\" +\n\t\t\t\t\"XUpo2__H55tL_g78_8V_\" +\n\t\t\t\t\"8l0y_9K9_C__6af__wj_\" +\n\t\t\t\t\"UbSYy_ge29S_s_Qe259q\" +\n\t\t\t\t\"_kGod \\t\\t\\t\\t :0xb1AF1F\" +\n\t\t\t\t\"b71D2ACeaB3FEce2ssg \" +\n\t\t\t\t\"o dr-0x804dBDe8ba \\t \" +\n\t\t\t\t\"\\t\\t$  Y5 _BzOc6S_Lk0K\" +\n\t\t\t\t\"y43j1TzV__9367tbX56_\" +\n\t\t\t\t\"6B3__q6_v8_4_0_t_2q_\" +\n\t\t\t\t\"nJ2gV3j9_tkOrx_H__a}\" +\n\t\t\t\t\"mT 0g6bp_KI_1_YWc_K8\" +\n\t\t\t\t\"c2b\\t_ a\\t \\t54KM8f9_63\" +\n\t\t\t\t\"zJ2Q_c1_C_Zf4ICF4m0q\" +\n\t\t\t\t\"_RVm_3Zh4vr7yI_H2  a\" +\n\t\t\t\t\" m 0yq__TiqA_FQBv_SS\" +\n\t\t\t\t\"_Hm_8T8__M8F2_53TTo_\" +\n\t\t\t\t\"k_o2__u_W6Vr__524q9l\" +\n\t\t\t\t\"9CQsC_kOU___g_94   \\\"\" +\n\t\t\t\t\" ~a_j_16_6iUSu_96V1W\" +\n\t\t\t\t\"5r01j____gn157__8_LO\" +\n\t\t\t\t\"0y_08Jr6OR__WF8__JK_\" +\n\t\t\t\t\"N_wx_k_CGB_SjJ9R74i_\" +\n\t\t\t\t\"7_1t_6 m NULLNULLNUL\" +\n\t\t\t\t\"L \\t \\t\\t\\t drK\\t\\x00 7_\\\"\\\" 5\" +\n\t\t\t\t\"_5_y732S43__D_8U9FX2\" +\n\t\t\t\t\"27_k\\t\\tg6bp_KI_1_YWc_\" +\n\t\t\t\t\"K8c2b_J_wx8yw1CMw27j\" +\n\t\t\t\t\"___f_a8uw_ Er9gB_L2 \" +\n\t\t\t\t\"\\t\\t  \\t\\t\\tm aa_XO4_5 Y_\" +\n\t\t\t\t\" I_T7762_zlMi_n8_FjH\" +\n\t\t\t\t\"vy62p__M4S_8__r092af\" +\n\t\t\t\t\"P_T_vhp6__SA_jVF13c5\" +\n\t\t\t\t\"2__8J48K__S4YcjoY91X\" +\n\t\t\t\t\"_iNf06  am aa_XO4_5\\t\" +\n\t\t\t\t\" d _ am_SYY4G__2h4QL\" +\n\t\t\t\t\"iUIDd \\t\\t  \\tXXp__KFjR\" +\n\t\t\t\t\"V__JU3o\\\"\\\" d  \\t_Iks_ \" +\n\t\t\t\t\"aa_XO4_5<g6bp_KI_1_Y\" +\n\t\t\t\t\"Wc_K8c2b _BzOc6S_Lk0\" +\n\t\t\t\t\"Ky43j1TzV__9367tbX56\" +\n\t\t\t\t\"_6B3__q6_v8_4_0_t_2q\" +\n\t\t\t\t\"_nJ2gV3j9_tkOrx_H__ \" +\n\t\t\t\t\"a\\t_Iks_ \\\\ ma 0_58_r1\" +\n\t\t\t\t\"y8jib_FaV_C_e \\t \\td\\\"\\\"\" +\n\t\t\t\t\" ^Dy_0  \\t\\t \\t ;;;;;;;\" +\n\t\t\t\t\";;;;;;;;;;;\",\n\t\t\t`dns: bad HINFO Fields: \"comment length insufficient for parsing\" at line: 1:1951`,\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t_, err := NewRR(tc.in)\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"Expecting error for crasher line %s\", tc.in)\n\t\t\t}\n\t\t\tif tc.err != err.Error() {\n\t\t\t\tt.Errorf(\"Expecting error %s, got %s\", tc.err, err.Error())\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "generate.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Parse the $GENERATE statement as used in BIND9 zones.\n// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.\n// We are called after '$GENERATE '. After which we expect:\n// * the range (12-24/2)\n// * lhs (ownername)\n// * [[ttl][class]]\n// * type\n// * rhs (rdata)\n// But we are lazy here, only the range is parsed *all* occurrences\n// of $ after that are interpreted.\nfunc (zp *ZoneParser) generate(l lex) (RR, bool) {\n\ttoken := l.token\n\tstep := int64(1)\n\tif i := strings.IndexByte(token, '/'); i >= 0 {\n\t\tif i+1 == len(token) {\n\t\t\treturn zp.setParseError(\"bad step in $GENERATE range\", l)\n\t\t}\n\n\t\ts, err := strconv.ParseInt(token[i+1:], 10, 64)\n\t\tif err != nil || s <= 0 {\n\t\t\treturn zp.setParseError(\"bad step in $GENERATE range\", l)\n\t\t}\n\n\t\tstep = s\n\t\ttoken = token[:i]\n\t}\n\n\tstartStr, endStr, ok := strings.Cut(token, \"-\")\n\tif !ok {\n\t\treturn zp.setParseError(\"bad start-stop in $GENERATE range\", l)\n\t}\n\n\tstart, err := strconv.ParseInt(startStr, 10, 64)\n\tif err != nil {\n\t\treturn zp.setParseError(\"bad start in $GENERATE range\", l)\n\t}\n\n\tend, err := strconv.ParseInt(endStr, 10, 64)\n\tif err != nil {\n\t\treturn zp.setParseError(\"bad stop in $GENERATE range\", l)\n\t}\n\tif end < 0 || start < 0 || end < start || (end-start)/step > 65535 {\n\t\treturn zp.setParseError(\"bad range in $GENERATE range\", l)\n\t}\n\n\t// _BLANK\n\tl, ok = zp.c.Next()\n\tif !ok || l.value != zBlank {\n\t\treturn zp.setParseError(\"garbage after $GENERATE range\", l)\n\t}\n\n\t// Create a complete new string, which we then parse again.\n\tvar s string\n\tfor l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {\n\t\tif l.err {\n\t\t\treturn zp.setParseError(\"bad data in $GENERATE directive\", l)\n\t\t}\n\t\tif l.value == zNewline {\n\t\t\tbreak\n\t\t}\n\n\t\ts += l.token\n\t}\n\n\tr := &generateReader{\n\t\ts: s,\n\n\t\tcur:   start,\n\t\tstart: start,\n\t\tend:   end,\n\t\tstep:  step,\n\n\t\tfile: zp.file,\n\t\tlex:  &l,\n\t}\n\tzp.sub = NewZoneParser(r, zp.origin, zp.file)\n\tzp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed\n\tzp.sub.generateDisallowed = true\n\tzp.sub.SetDefaultTTL(defaultTtl)\n\treturn zp.subNext()\n}\n\ntype generateReader struct {\n\ts  string\n\tsi int\n\n\tcur   int64\n\tstart int64\n\tend   int64\n\tstep  int64\n\n\tmod bytes.Buffer\n\n\tescape bool\n\n\teof bool\n\n\tfile string\n\tlex  *lex\n}\n\nfunc (r *generateReader) parseError(msg string, end int) *ParseError {\n\tr.eof = true // Make errors sticky.\n\n\tl := *r.lex\n\tl.token = r.s[r.si-1 : end]\n\tl.column += r.si // l.column starts one zBLANK before r.s\n\n\treturn &ParseError{file: r.file, err: msg, lex: l}\n}\n\nfunc (r *generateReader) Read(p []byte) (int, error) {\n\t// NewZLexer, through NewZoneParser, should use ReadByte and\n\t// not end up here.\n\n\tpanic(\"not implemented\")\n}\n\nfunc (r *generateReader) ReadByte() (byte, error) {\n\tif r.eof {\n\t\treturn 0, io.EOF\n\t}\n\tif r.mod.Len() > 0 {\n\t\treturn r.mod.ReadByte()\n\t}\n\n\tif r.si >= len(r.s) {\n\t\tr.si = 0\n\t\tr.cur += r.step\n\n\t\tr.eof = r.cur > r.end || r.cur < 0\n\t\treturn '\\n', nil\n\t}\n\n\tsi := r.si\n\tr.si++\n\n\tswitch r.s[si] {\n\tcase '\\\\':\n\t\tif r.escape {\n\t\t\tr.escape = false\n\t\t\treturn '\\\\', nil\n\t\t}\n\n\t\tr.escape = true\n\t\treturn r.ReadByte()\n\tcase '$':\n\t\tif r.escape {\n\t\t\tr.escape = false\n\t\t\treturn '$', nil\n\t\t}\n\n\t\tmod := \"%d\"\n\n\t\tif si >= len(r.s)-1 {\n\t\t\t// End of the string\n\t\t\tfmt.Fprintf(&r.mod, mod, r.cur)\n\t\t\treturn r.mod.ReadByte()\n\t\t}\n\n\t\tif r.s[si+1] == '$' {\n\t\t\tr.si++\n\t\t\treturn '$', nil\n\t\t}\n\n\t\tvar offset int64\n\n\t\t// Search for { and }\n\t\tif r.s[si+1] == '{' {\n\t\t\t// Modifier block\n\t\t\tsep := strings.Index(r.s[si+2:], \"}\")\n\t\t\tif sep < 0 {\n\t\t\t\treturn 0, r.parseError(\"bad modifier in $GENERATE\", len(r.s))\n\t\t\t}\n\n\t\t\tvar errMsg string\n\t\t\tmod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep])\n\t\t\tif errMsg != \"\" {\n\t\t\t\treturn 0, r.parseError(errMsg, si+3+sep)\n\t\t\t}\n\t\t\tif r.start+offset < 0 || r.end+offset > 1<<31-1 {\n\t\t\t\treturn 0, r.parseError(\"bad offset in $GENERATE\", si+3+sep)\n\t\t\t}\n\n\t\t\tr.si += 2 + sep // Jump to it\n\t\t}\n\n\t\tfmt.Fprintf(&r.mod, mod, r.cur+offset)\n\t\treturn r.mod.ReadByte()\n\tdefault:\n\t\tif r.escape { // Pretty useless here\n\t\t\tr.escape = false\n\t\t\treturn r.ReadByte()\n\t\t}\n\n\t\treturn r.s[si], nil\n\t}\n}\n\n// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.\nfunc modToPrintf(s string) (string, int64, string) {\n\t// Modifier is { offset [ ,width [ ,base ] ] } - provide default\n\t// values for optional width and type, if necessary.\n\toffStr, s, ok0 := strings.Cut(s, \",\")\n\twidthStr, s, ok1 := strings.Cut(s, \",\")\n\tbase, _, ok2 := strings.Cut(s, \",\")\n\tif !ok0 {\n\t\twidthStr = \"0\"\n\t}\n\tif !ok1 {\n\t\tbase = \"d\"\n\t}\n\tif ok2 {\n\t\treturn \"\", 0, \"bad modifier in $GENERATE\"\n\t}\n\n\tswitch base {\n\tcase \"o\", \"d\", \"x\", \"X\":\n\tdefault:\n\t\treturn \"\", 0, \"bad base in $GENERATE\"\n\t}\n\n\toffset, err := strconv.ParseInt(offStr, 10, 64)\n\tif err != nil {\n\t\treturn \"\", 0, \"bad offset in $GENERATE\"\n\t}\n\n\twidth, err := strconv.ParseUint(widthStr, 10, 8)\n\tif err != nil {\n\t\treturn \"\", 0, \"bad width in $GENERATE\"\n\t}\n\n\tif width == 0 {\n\t\treturn \"%\" + base, offset, \"\"\n\t}\n\n\treturn \"%0\" + widthStr + base, offset, \"\"\n}\n"
  },
  {
    "path": "generate_test.go",
    "content": "package dns\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGenerateRangeGuard(t *testing.T) {\n\ttmpdir := t.TempDir()\n\n\tfor i := 0; i <= 1; i++ {\n\t\tpath := filepath.Join(tmpdir, fmt.Sprintf(\"%04d.conf\", i))\n\t\tdata := []byte(fmt.Sprintf(\"dhcp-%04d A 10.0.0.%d\", i, i))\n\n\t\tif err := os.WriteFile(path, data, 0o644); err != nil {\n\t\t\tt.Fatalf(\"could not create tmpfile for test: %v\", err)\n\t\t}\n\t}\n\n\ttests := [...]struct {\n\t\tzone string\n\t\tfail bool\n\t}{\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 dhcp-${0,4,d} A 10.0.0.$\n`, false},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 dhcp-${0,0,x} A 10.0.0.$\n`, false},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 128-129 dhcp-${-128,4,d} A 10.0.0.$\n`, false},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 128-129 dhcp-${-129,4,d} A 10.0.0.$\n`, true},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-2 dhcp-${2147483647,4,d} A 10.0.0.$\n`, true},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 dhcp-${2147483646,4,d} A 10.0.0.$\n`, false},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1/step dhcp-${0,4,d} A 10.0.0.$\n`, true},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1/ dhcp-${0,4,d} A 10.0.0.$\n`, true},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-10/2 dhcp-${0,4,d} A 10.0.0.$\n`, false},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1/0 dhcp-${0,4,d} A 10.0.0.$\n`, true},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 $$INCLUDE ` + tmpdir + string(filepath.Separator) + `${0,4,d}.conf\n`, false},\n\t\t{`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 dhcp-${0,4,d} A 10.0.0.$\n$GENERATE 0-2 dhcp-${0,4,d} A 10.1.0.$\n`, false},\n\t}\n\n\tfor i := range tests {\n\t\tz := NewZoneParser(strings.NewReader(tests[i].zone), \"test.\", \"test\")\n\t\tz.SetIncludeAllowed(true)\n\n\t\tfor _, ok := z.Next(); ok; _, ok = z.Next() {\n\t\t}\n\n\t\terr := z.Err()\n\t\tif err != nil && !tests[i].fail {\n\t\t\tt.Errorf(\"expected \\n\\n%s\\nto be parsed, but got %v\", tests[i].zone, err)\n\t\t} else if err == nil && tests[i].fail {\n\t\t\tt.Errorf(\"expected \\n\\n%s\\nto fail, but got no error\", tests[i].zone)\n\t\t}\n\t}\n}\n\nfunc TestGenerateIncludeDepth(t *testing.T) {\n\ttmpfile, err := os.CreateTemp(\"\", \"dns\")\n\tif err != nil {\n\t\tt.Fatalf(\"could not create tmpfile for test: %v\", err)\n\t}\n\tdefer os.Remove(tmpfile.Name())\n\n\tzone := `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 $$INCLUDE ` + tmpfile.Name() + `\n`\n\tif _, err := tmpfile.WriteString(zone); err != nil {\n\t\tt.Fatalf(\"could not write to tmpfile for test: %v\", err)\n\t}\n\tif err := tmpfile.Close(); err != nil {\n\t\tt.Fatalf(\"could not close tmpfile for test: %v\", err)\n\t}\n\n\tzp := NewZoneParser(strings.NewReader(zone), \".\", tmpfile.Name())\n\tzp.SetIncludeAllowed(true)\n\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t}\n\n\tconst expected = \"too deeply nested $INCLUDE\"\n\tif err := zp.Err(); err == nil || !strings.Contains(err.Error(), expected) {\n\t\tt.Errorf(\"expected error to include %q, got %v\", expected, err)\n\t}\n}\n\nfunc TestGenerateIncludeDisallowed(t *testing.T) {\n\tconst zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 $$INCLUDE test.conf\n`\n\tzp := NewZoneParser(strings.NewReader(zone), \".\", \"\")\n\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t}\n\n\tconst expected = \"$INCLUDE directive not allowed\"\n\tif err := zp.Err(); err == nil || !strings.Contains(err.Error(), expected) {\n\t\tt.Errorf(\"expected error to include %q, got %v\", expected, err)\n\t}\n}\n\nfunc TestGenerateSurfacesErrors(t *testing.T) {\n\tconst zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 dhcp-${0,4,dd} A 10.0.0.$\n`\n\tzp := NewZoneParser(strings.NewReader(zone), \".\", \"test\")\n\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t}\n\n\tconst expected = `test: dns: bad base in $GENERATE: \"${0,4,dd}\" at line: 2:20`\n\tif err := zp.Err(); err == nil || err.Error() != expected {\n\t\tt.Errorf(\"expected specific error, wanted %q, got %v\", expected, err)\n\t}\n}\n\nfunc TestGenerateSurfacesLexerErrors(t *testing.T) {\n\tconst zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 0-1 dhcp-${0,4,d} A 10.0.0.$ )\n`\n\tzp := NewZoneParser(strings.NewReader(zone), \".\", \"test\")\n\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t}\n\n\tconst expected = `test: dns: bad data in $GENERATE directive: \"extra closing brace\" at line: 2:40`\n\tif err := zp.Err(); err == nil || err.Error() != expected {\n\t\tt.Errorf(\"expected specific error, wanted %q, got %v\", expected, err)\n\t}\n}\n\nfunc TestGenerateModToPrintf(t *testing.T) {\n\ttests := []struct {\n\t\tmod        string\n\t\twantFmt    string\n\t\twantOffset int64\n\t\twantErr    bool\n\t}{\n\t\t{\"0,0,d\", \"%d\", 0, false},\n\t\t{\"0,0\", \"%d\", 0, false},\n\t\t{\"0\", \"%d\", 0, false},\n\t\t{\"3,2,d\", \"%02d\", 3, false},\n\t\t{\"3,2\", \"%02d\", 3, false},\n\t\t{\"3\", \"%d\", 3, false},\n\t\t{\"0,0,o\", \"%o\", 0, false},\n\t\t{\"0,0,x\", \"%x\", 0, false},\n\t\t{\"0,0,X\", \"%X\", 0, false},\n\t\t{\"0,0,z\", \"\", 0, true},\n\t\t{\"0,0,0,d\", \"\", 0, true},\n\t\t{\"-100,0,d\", \"%d\", -100, false},\n\t}\n\tfor _, test := range tests {\n\t\tgotFmt, gotOffset, errMsg := modToPrintf(test.mod)\n\t\tswitch {\n\t\tcase errMsg != \"\" && !test.wantErr:\n\t\t\tt.Errorf(\"modToPrintf(%q) - expected empty-error, but got %v\", test.mod, errMsg)\n\t\tcase errMsg == \"\" && test.wantErr:\n\t\t\tt.Errorf(\"modToPrintf(%q) - expected error, but got empty-error\", test.mod)\n\t\tcase gotFmt != test.wantFmt:\n\t\t\tt.Errorf(\"modToPrintf(%q) - expected format %q, but got %q\", test.mod, test.wantFmt, gotFmt)\n\t\tcase gotOffset != test.wantOffset:\n\t\t\tt.Errorf(\"modToPrintf(%q) - expected offset %d, but got %d\", test.mod, test.wantOffset, gotOffset)\n\t\t}\n\t}\n}\n\nfunc BenchmarkGenerate(b *testing.B) {\n\tconst zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d )\n$GENERATE 32-158 dhcp-${-32,4,d} A 10.0.0.$\n`\n\n\tfor n := 0; n < b.N; n++ {\n\t\tzp := NewZoneParser(strings.NewReader(zone), \".\", \"\")\n\n\t\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t\t}\n\n\t\tif err := zp.Err(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestCrasherString(t *testing.T) {\n\ttests := []struct {\n\t\tin  string\n\t\terr string\n\t}{\n\t\t{\"$GENERATE 0-300103\\\"$$GENERATE 2-2\", \"bad range in $GENERATE\"},\n\t\t{\"$GENERATE 0-5414137360\", \"bad range in $GENERATE\"},\n\t\t{\"$GENERATE       11522-3668518066406258\", \"bad range in $GENERATE\"},\n\t\t{\"$GENERATE 0-200\\\"(;00000000000000\\n$$GENERATE 0-0\", \"dns: garbage after $GENERATE range: \\\"\\\\\\\"\\\" at line: 1:16\"},\n\t\t{\"$GENERATE 6-2048 $$GENERATE 6-036160 $$$$ORIGIN \\\\$\", `dns: nested $GENERATE directive not allowed: \"6-036160\" at line: 1:19`},\n\t}\n\tfor _, tc := range tests {\n\t\tt.Run(tc.in, func(t *testing.T) {\n\t\t\t_, err := NewRR(tc.in)\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"Expecting error for crasher line %s\", tc.in)\n\t\t\t}\n\t\t\tif !strings.Contains(err.Error(), tc.err) {\n\t\t\t\tt.Errorf(\"Expecting error %s, got %s\", tc.err, err.Error())\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/miekg/dns\n\ngo 1.24.0\n\ntoolchain go1.24.2\n\nrequire (\n\tgolang.org/x/net v0.49.0\n\tgolang.org/x/sync v0.19.0\n\tgolang.org/x/sys v0.40.0\n\tgolang.org/x/tools v0.41.0\n)\n\nrequire golang.org/x/mod v0.32.0 // indirect\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngolang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=\ngolang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=\ngolang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=\n"
  },
  {
    "path": "hash.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"crypto\"\n\t\"hash\"\n)\n\n// identityHash will not hash, it only buffers the data written into it and returns it as-is.\ntype identityHash struct {\n\tb *bytes.Buffer\n}\n\n// Implement the hash.Hash interface.\n\nfunc (i identityHash) Write(b []byte) (int, error) { return i.b.Write(b) }\nfunc (i identityHash) Size() int                   { return i.b.Len() }\nfunc (i identityHash) BlockSize() int              { return 1024 }\nfunc (i identityHash) Reset()                      { i.b.Reset() }\nfunc (i identityHash) Sum(b []byte) []byte         { return append(b, i.b.Bytes()...) }\n\nfunc hashFromAlgorithm(alg uint8) (hash.Hash, crypto.Hash, error) {\n\thashnumber, ok := AlgorithmToHash[alg]\n\tif !ok {\n\t\treturn nil, 0, ErrAlg\n\t}\n\tif hashnumber == 0 {\n\t\treturn identityHash{b: &bytes.Buffer{}}, hashnumber, nil\n\t}\n\treturn hashnumber.New(), hashnumber, nil\n}\n"
  },
  {
    "path": "issue_test.go",
    "content": "package dns\n\n// Tests that solve that an specific issue.\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestNSEC3MissingSalt(t *testing.T) {\n\trr := testRR(\"ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 aabbccdd K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H\")\n\tm := new(Msg)\n\tm.Answer = []RR{rr}\n\tmb, err := m.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"expected to pack message. err: %s\", err)\n\t}\n\tif err := m.Unpack(mb); err != nil {\n\t\tt.Fatalf(\"expected to unpack message. missing salt? err: %s\", err)\n\t}\n\tin := rr.(*NSEC3).Salt\n\tout := m.Answer[0].(*NSEC3).Salt\n\tif in != out {\n\t\tt.Fatalf(\"expected salts to match. packed: `%s`. returned: `%s`\", in, out)\n\t}\n}\n\nfunc TestNSEC3MixedNextDomain(t *testing.T) {\n\trr := testRR(\"ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 - k8udemvp1j2f7eg6jebps17vp3n8i58h\")\n\tm := new(Msg)\n\tm.Answer = []RR{rr}\n\tmb, err := m.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"expected to pack message. err: %s\", err)\n\t}\n\tif err := m.Unpack(mb); err != nil {\n\t\tt.Fatalf(\"expected to unpack message. err: %s\", err)\n\t}\n\tin := strings.ToUpper(rr.(*NSEC3).NextDomain)\n\tout := m.Answer[0].(*NSEC3).NextDomain\n\tif in != out {\n\t\tt.Fatalf(\"expected round trip to produce NextDomain `%s`, instead `%s`\", in, out)\n\t}\n}\n"
  },
  {
    "path": "labels.go",
    "content": "package dns\n\n// Holds a bunch of helper functions for dealing with labels.\n\n// SplitDomainName splits a name string into it's labels.\n// www.miek.nl. returns []string{\"www\", \"miek\", \"nl\"}\n// .www.miek.nl. returns []string{\"\", \"www\", \"miek\", \"nl\"},\n// The root label (.) returns nil. Note that using\n// strings.Split(s) will work in most cases, but does not handle\n// escaped dots (\\.) for instance.\n// s must be a syntactically valid domain name, see IsDomainName.\nfunc SplitDomainName(s string) (labels []string) {\n\tif s == \"\" {\n\t\treturn nil\n\t}\n\tfqdnEnd := 0 // offset of the final '.' or the length of the name\n\tidx := Split(s)\n\tbegin := 0\n\tif IsFqdn(s) {\n\t\tfqdnEnd = len(s) - 1\n\t} else {\n\t\tfqdnEnd = len(s)\n\t}\n\n\tswitch len(idx) {\n\tcase 0:\n\t\treturn nil\n\tcase 1:\n\t\t// no-op\n\tdefault:\n\t\tfor _, end := range idx[1:] {\n\t\t\tlabels = append(labels, s[begin:end-1])\n\t\t\tbegin = end\n\t\t}\n\t}\n\n\treturn append(labels, s[begin:fqdnEnd])\n}\n\n// CompareDomainName compares the names s1 and s2 and\n// returns how many labels they have in common starting from the *right*.\n// The comparison stops at the first inequality. The names are downcased\n// before the comparison.\n//\n// www.miek.nl. and miek.nl. have two labels in common: miek and nl\n// www.miek.nl. and www.bla.nl. have one label in common: nl\n//\n// s1 and s2 must be syntactically valid domain names.\nfunc CompareDomainName(s1, s2 string) (n int) {\n\t// the first check: root label\n\tif s1 == \".\" || s2 == \".\" {\n\t\treturn 0\n\t}\n\n\tl1 := Split(s1)\n\tl2 := Split(s2)\n\n\tj1 := len(l1) - 1 // end\n\ti1 := len(l1) - 2 // start\n\tj2 := len(l2) - 1\n\ti2 := len(l2) - 2\n\t// the second check can be done here: last/only label\n\t// before we fall through into the for-loop below\n\tif equal(s1[l1[j1]:], s2[l2[j2]:]) {\n\t\tn++\n\t} else {\n\t\treturn\n\t}\n\tfor {\n\t\tif i1 < 0 || i2 < 0 {\n\t\t\tbreak\n\t\t}\n\t\tif equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) {\n\t\t\tn++\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t\tj1--\n\t\ti1--\n\t\tj2--\n\t\ti2--\n\t}\n\treturn\n}\n\n// CountLabel counts the number of labels in the string s.\n// s must be a syntactically valid domain name.\nfunc CountLabel(s string) (labels int) {\n\tif s == \".\" {\n\t\treturn\n\t}\n\toff := 0\n\tend := false\n\tfor {\n\t\toff, end = NextLabel(s, off)\n\t\tlabels++\n\t\tif end {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Split splits a name s into its label indexes.\n// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.\n// The root name (.) returns nil. Also see SplitDomainName.\n// s must be a syntactically valid domain name.\nfunc Split(s string) []int {\n\tif s == \".\" {\n\t\treturn nil\n\t}\n\tidx := make([]int, 1, 3)\n\toff := 0\n\tend := false\n\n\tfor {\n\t\toff, end = NextLabel(s, off)\n\t\tif end {\n\t\t\treturn idx\n\t\t}\n\t\tidx = append(idx, off)\n\t}\n}\n\n// NextLabel returns the index of the start of the next label in the\n// string s starting at offset. A negative offset will cause a panic.\n// The bool end is true when the end of the string has been reached.\n// Also see PrevLabel.\nfunc NextLabel(s string, offset int) (i int, end bool) {\n\tif s == \"\" {\n\t\treturn 0, true\n\t}\n\tfor i = offset; i < len(s)-1; i++ {\n\t\tif s[i] != '.' {\n\t\t\tcontinue\n\t\t}\n\t\tj := i - 1\n\t\tfor j >= 0 && s[j] == '\\\\' {\n\t\t\tj--\n\t\t}\n\n\t\tif (j-i)%2 == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn i + 1, false\n\t}\n\treturn i + 1, true\n}\n\n// PrevLabel returns the index of the label when starting from the right and\n// jumping n labels to the left.\n// The bool start is true when the start of the string has been overshot.\n// Also see NextLabel.\nfunc PrevLabel(s string, n int) (i int, start bool) {\n\tif s == \"\" {\n\t\treturn 0, true\n\t}\n\tif n == 0 {\n\t\treturn len(s), false\n\t}\n\n\tl := len(s) - 1\n\tif s[l] == '.' {\n\t\tl--\n\t}\n\n\tfor ; l >= 0 && n > 0; l-- {\n\t\tif s[l] != '.' {\n\t\t\tcontinue\n\t\t}\n\t\tj := l - 1\n\t\tfor j >= 0 && s[j] == '\\\\' {\n\t\t\tj--\n\t\t}\n\n\t\tif (j-l)%2 == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tn--\n\t\tif n == 0 {\n\t\t\treturn l + 1, false\n\t\t}\n\t}\n\n\treturn 0, n > 1\n}\n\n// equal compares a and b while ignoring case. It returns true when equal otherwise false.\nfunc equal(a, b string) bool {\n\t// might be lifted into API function.\n\tla := len(a)\n\tlb := len(b)\n\tif la != lb {\n\t\treturn false\n\t}\n\n\tfor i := la - 1; i >= 0; i-- {\n\t\tai := a[i]\n\t\tbi := b[i]\n\t\tif ai >= 'A' && ai <= 'Z' {\n\t\t\tai |= 'a' - 'A'\n\t\t}\n\t\tif bi >= 'A' && bi <= 'Z' {\n\t\t\tbi |= 'a' - 'A'\n\t\t}\n\t\tif ai != bi {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "labels_test.go",
    "content": "package dns\n\nimport \"testing\"\n\nfunc TestCompareDomainName(t *testing.T) {\n\ts1 := \"www.miek.nl.\"\n\ts2 := \"miek.nl.\"\n\ts3 := \"www.bla.nl.\"\n\ts4 := \"nl.www.bla.\"\n\ts5 := \"nl.\"\n\ts6 := \"miek.nl.\"\n\n\tif CompareDomainName(s1, s2) != 2 {\n\t\tt.Errorf(\"%s with %s should be %d\", s1, s2, 2)\n\t}\n\tif CompareDomainName(s1, s3) != 1 {\n\t\tt.Errorf(\"%s with %s should be %d\", s1, s3, 1)\n\t}\n\tif CompareDomainName(s3, s4) != 0 {\n\t\tt.Errorf(\"%s with %s should be %d\", s3, s4, 0)\n\t}\n\t// Non qualified tests\n\tif CompareDomainName(s1, s5) != 1 {\n\t\tt.Errorf(\"%s with %s should be %d\", s1, s5, 1)\n\t}\n\tif CompareDomainName(s1, s6) != 2 {\n\t\tt.Errorf(\"%s with %s should be %d\", s1, s5, 2)\n\t}\n\n\tif CompareDomainName(s1, \".\") != 0 {\n\t\tt.Errorf(\"%s with %s should be %d\", s1, s5, 0)\n\t}\n\tif CompareDomainName(\".\", \".\") != 0 {\n\t\tt.Errorf(\"%s with %s should be %d\", \".\", \".\", 0)\n\t}\n\tif CompareDomainName(\"test.com.\", \"TEST.COM.\") != 2 {\n\t\tt.Errorf(\"test.com. and TEST.COM. should be an exact match\")\n\t}\n}\n\nfunc TestSplit(t *testing.T) {\n\tsplitter := map[string]int{\n\t\t\"www.miek.nl.\":    3,\n\t\t\"www.miek.nl\":     3,\n\t\t\"www..miek.nl\":    4,\n\t\t`www\\.miek.nl.`:   2,\n\t\t`www\\\\.miek.nl.`:  3,\n\t\t`www\\\\\\.miek.nl.`: 2,\n\t\t\".\":               0,\n\t\t\"nl.\":             1,\n\t\t\"nl\":              1,\n\t\t\"com.\":            1,\n\t\t\".com.\":           2,\n\t}\n\tfor s, i := range splitter {\n\t\tif x := len(Split(s)); x != i {\n\t\t\tt.Errorf(\"labels should be %d, got %d: %s %v\", i, x, s, Split(s))\n\t\t}\n\t}\n}\n\nfunc TestSplit2(t *testing.T) {\n\tsplitter := map[string][]int{\n\t\t\"www.miek.nl.\": {0, 4, 9},\n\t\t\"www.miek.nl\":  {0, 4, 9},\n\t\t\"nl\":           {0},\n\t}\n\tfor s, i := range splitter {\n\t\tx := Split(s)\n\t\tswitch len(i) {\n\t\tcase 1:\n\t\t\tif x[0] != i[0] {\n\t\t\t\tt.Errorf(\"labels should be %v, got %v: %s\", i, x, s)\n\t\t\t}\n\t\tdefault:\n\t\t\tif x[0] != i[0] || x[1] != i[1] || x[2] != i[2] {\n\t\t\t\tt.Errorf(\"labels should be %v, got %v: %s\", i, x, s)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestNextLabel(t *testing.T) {\n\ttype next struct {\n\t\tstring\n\t\tint\n\t}\n\tnexts := map[next]int{\n\t\t{\"\", 1}:             0,\n\t\t{\"www.miek.nl.\", 0}: 4,\n\t\t{\"www.miek.nl.\", 4}: 9,\n\t\t{\"www.miek.nl.\", 9}: 12,\n\t}\n\tfor s, i := range nexts {\n\t\tx, ok := NextLabel(s.string, s.int)\n\t\tif i != x {\n\t\t\tt.Errorf(\"label should be %d, got %d, %t: next %d, %s\", i, x, ok, s.int, s.string)\n\t\t}\n\t}\n}\n\nfunc TestPrevLabel(t *testing.T) {\n\ttype prev struct {\n\t\tstring\n\t\tint\n\t}\n\tprever := map[prev]int{\n\t\t{\"\", 1}:             0,\n\t\t{\"www.miek.nl.\", 0}: 12,\n\t\t{\"www.miek.nl.\", 1}: 9,\n\t\t{\"www.miek.nl.\", 2}: 4,\n\n\t\t{\"www.miek.nl\", 0}: 11,\n\t\t{\"www.miek.nl\", 1}: 9,\n\t\t{\"www.miek.nl\", 2}: 4,\n\n\t\t{\"www.miek.nl.\", 5}: 0,\n\t\t{\"www.miek.nl\", 5}:  0,\n\n\t\t{\"www.miek.nl.\", 3}: 0,\n\t\t{\"www.miek.nl\", 3}:  0,\n\t}\n\tfor s, i := range prever {\n\t\tx, ok := PrevLabel(s.string, s.int)\n\t\tif i != x {\n\t\t\tt.Errorf(\"label should be %d, got %d, %t: previous %d, %s\", i, x, ok, s.int, s.string)\n\t\t}\n\t}\n}\n\nfunc TestCountLabel(t *testing.T) {\n\tsplitter := map[string]int{\n\t\t\"www.miek.nl.\": 3,\n\t\t\"www.miek.nl\":  3,\n\t\t\"nl\":           1,\n\t\t\".\":            0,\n\t}\n\tfor s, i := range splitter {\n\t\tx := CountLabel(s)\n\t\tif x != i {\n\t\t\tt.Errorf(\"CountLabel should have %d, got %d\", i, x)\n\t\t}\n\t}\n}\n\nfunc TestSplitDomainName(t *testing.T) {\n\tlabels := map[string][]string{\n\t\t\"miek.nl\":       {\"miek\", \"nl\"},\n\t\t\".\":             nil,\n\t\t\"www.miek.nl.\":  {\"www\", \"miek\", \"nl\"},\n\t\t\"www.miek.nl\":   {\"www\", \"miek\", \"nl\"},\n\t\t\"www..miek.nl\":  {\"www\", \"\", \"miek\", \"nl\"},\n\t\t`www\\.miek.nl`:  {`www\\.miek`, \"nl\"},\n\t\t`www\\\\.miek.nl`: {`www\\\\`, \"miek\", \"nl\"},\n\t\t\".www.miek.nl.\": {\"\", \"www\", \"miek\", \"nl\"},\n\t}\ndomainLoop:\n\tfor domain, splits := range labels {\n\t\tparts := SplitDomainName(domain)\n\t\tif len(parts) != len(splits) {\n\t\t\tt.Errorf(\"SplitDomainName returned %v for %s, expected %v\", parts, domain, splits)\n\t\t\tcontinue domainLoop\n\t\t}\n\t\tfor i := range parts {\n\t\t\tif parts[i] != splits[i] {\n\t\t\t\tt.Errorf(\"SplitDomainName returned %v for %s, expected %v\", parts, domain, splits)\n\t\t\t\tcontinue domainLoop\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestIsDomainName(t *testing.T) {\n\ttype ret struct {\n\t\tok  bool\n\t\tlab int\n\t}\n\tnames := map[string]*ret{\n\t\t\".\":                      {true, 1},\n\t\t\"..\":                     {false, 0},\n\t\t\"double-dot..test\":       {false, 1},\n\t\t\".leading-dot.test\":      {false, 0},\n\t\t\"@.\":                     {true, 1},\n\t\t\"www.example.com\":        {true, 3},\n\t\t\"www.e%ample.com\":        {true, 3},\n\t\t\"www.example.com.\":       {true, 3},\n\t\t\"mi\\\\k.nl.\":              {true, 2},\n\t\t\"mi\\\\k.nl\":               {true, 2},\n\t\tlongestDomain:            {true, 4},\n\t\tlongestUnprintableDomain: {true, 4},\n\t}\n\tfor d, ok := range names {\n\t\tl, k := IsDomainName(d)\n\t\tif ok.ok != k || ok.lab != l {\n\t\t\tt.Errorf(\" got %v %d for %s \", k, l, d)\n\t\t\tt.Errorf(\"have %v %d for %s \", ok.ok, ok.lab, d)\n\t\t}\n\t}\n}\n\nfunc TestIsFqdnEscaped(t *testing.T) {\n\tfor s, expect := range map[string]bool{\n\t\t\".\":                  true,\n\t\t\"\\\\.\":                false,\n\t\t\"\\\\\\\\.\":              true,\n\t\t\"\\\\\\\\\\\\.\":            false,\n\t\t\"\\\\\\\\\\\\\\\\.\":          true,\n\t\t\"a.\":                 true,\n\t\t\"a\\\\.\":               false,\n\t\t\"a\\\\\\\\.\":             true,\n\t\t\"a\\\\\\\\\\\\.\":           false,\n\t\t\"ab.\":                true,\n\t\t\"ab\\\\.\":              false,\n\t\t\"ab\\\\\\\\.\":            true,\n\t\t\"ab\\\\\\\\\\\\.\":          false,\n\t\t\"..\":                 true,\n\t\t\".\\\\.\":               false,\n\t\t\".\\\\\\\\.\":             true,\n\t\t\".\\\\\\\\\\\\.\":           false,\n\t\t\"example.org.\":       true,\n\t\t\"example.org\\\\.\":     false,\n\t\t\"example.org\\\\\\\\.\":   true,\n\t\t\"example.org\\\\\\\\\\\\.\": false,\n\t\t\"example\\\\.org.\":     true,\n\t\t\"example\\\\\\\\.org.\":   true,\n\t\t\"example\\\\\\\\\\\\.org.\": true,\n\t\t\"\\\\example.org.\":     true,\n\t\t\"\\\\\\\\example.org.\":   true,\n\t\t\"\\\\\\\\\\\\example.org.\": true,\n\t} {\n\t\tif got := IsFqdn(s); got != expect {\n\t\t\tt.Errorf(\"IsFqdn(%q) = %t, expected %t\", s, got, expect)\n\t\t}\n\t}\n}\n\nfunc TestCanonicalName(t *testing.T) {\n\tfor s, expect := range map[string]string{\n\t\t\"\":                 \".\",\n\t\t\".\":                \".\",\n\t\t\"tld\":              \"tld.\",\n\t\t\"tld.\":             \"tld.\",\n\t\t\"example.test\":     \"example.test.\",\n\t\t\"Lower.CASE.test.\": \"lower.case.test.\",\n\t\t\"*.Test\":           \"*.test.\",\n\t\t\"ÉxamplE.com\":      \"Éxample.com.\",\n\t\t\"É.com\":            \"É.com.\",\n\t} {\n\t\tif got := CanonicalName(s); got != expect {\n\t\t\tt.Errorf(\"CanonicalName(%q) = %q, expected %q\", s, got, expect)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSplitLabels(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tSplit(\"www.example.com.\")\n\t}\n}\n\nfunc BenchmarkLenLabels(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tCountLabel(\"www.example.com.\")\n\t}\n}\n\nfunc BenchmarkCompareDomainName(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tCompareDomainName(\"www.example.com.\", \"aa.example.com.\")\n\t}\n}\n\nfunc BenchmarkIsSubDomain(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tIsSubDomain(\"www.example.com.\", \"aa.example.com.\")\n\t\tIsSubDomain(\"example.com.\", \"aa.example.com.\")\n\t\tIsSubDomain(\"miek.nl.\", \"aa.example.com.\")\n\t}\n}\n\nfunc BenchmarkNextLabelSimple(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tNextLabel(\"www.example.com\", 0)\n\t\tNextLabel(\"www.example.com\", 5)\n\t\tNextLabel(\"www.example.com\", 12)\n\t}\n}\n\nfunc BenchmarkPrevLabelSimple(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tPrevLabel(\"www.example.com\", 0)\n\t\tPrevLabel(\"www.example.com\", 5)\n\t\tPrevLabel(\"www.example.com\", 12)\n\t}\n}\n\nfunc BenchmarkNextLabelComplex(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tNextLabel(`www\\.example.com`, 0)\n\t\tNextLabel(`www\\\\.example.com`, 0)\n\t\tNextLabel(`www\\\\\\.example.com`, 0)\n\t}\n}\n\nfunc BenchmarkPrevLabelComplex(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tPrevLabel(`www\\.example.com`, 10)\n\t\tPrevLabel(`www\\\\.example.com`, 10)\n\t\tPrevLabel(`www\\\\\\.example.com`, 10)\n\t}\n}\n\nfunc BenchmarkNextLabelMixed(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tNextLabel(\"www.example.com\", 0)\n\t\tNextLabel(`www\\.example.com`, 0)\n\t\tNextLabel(\"www.example.com\", 5)\n\t\tNextLabel(`www\\\\.example.com`, 0)\n\t\tNextLabel(\"www.example.com\", 12)\n\t\tNextLabel(`www\\\\\\.example.com`, 0)\n\t}\n}\n\nfunc BenchmarkPrevLabelMixed(b *testing.B) {\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tPrevLabel(\"www.example.com\", 0)\n\t\tPrevLabel(`www\\.example.com`, 10)\n\t\tPrevLabel(\"www.example.com\", 5)\n\t\tPrevLabel(`www\\\\.example.com`, 10)\n\t\tPrevLabel(\"www.example.com\", 12)\n\t\tPrevLabel(`www\\\\\\.example.com`, 10)\n\t}\n}\n"
  },
  {
    "path": "leak_test.go",
    "content": "package dns\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\n// copied from net/http/main_test.go\n\nfunc interestingGoroutines() (gs []string) {\n\tbuf := make([]byte, 2<<20)\n\tbuf = buf[:runtime.Stack(buf, true)]\n\tfor _, g := range strings.Split(string(buf), \"\\n\\n\") {\n\t\tsl := strings.SplitN(g, \"\\n\", 2)\n\t\tif len(sl) != 2 {\n\t\t\tcontinue\n\t\t}\n\t\tstack := strings.TrimSpace(sl[1])\n\t\tif stack == \"\" ||\n\t\t\tstrings.Contains(stack, \"testing.(*M).before.func1\") ||\n\t\t\tstrings.Contains(stack, \"os/signal.signal_recv\") ||\n\t\t\tstrings.Contains(stack, \"created by net.startServer\") ||\n\t\t\tstrings.Contains(stack, \"created by testing.RunTests\") ||\n\t\t\tstrings.Contains(stack, \"closeWriteAndWait\") ||\n\t\t\tstrings.Contains(stack, \"testing.Main(\") ||\n\t\t\tstrings.Contains(stack, \"testing.(*T).Run(\") ||\n\t\t\t// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)\n\t\t\tstrings.Contains(stack, \"runtime.goexit\") ||\n\t\t\tstrings.Contains(stack, \"created by runtime.gc\") ||\n\t\t\tstrings.Contains(stack, \"dns.interestingGoroutines\") ||\n\t\t\tstrings.Contains(stack, \"runtime.MHeap_Scavenger\") {\n\t\t\tcontinue\n\t\t}\n\t\tgs = append(gs, stack)\n\t}\n\tsort.Strings(gs)\n\treturn\n}\n\nfunc goroutineLeaked() error {\n\tif testing.Short() {\n\t\t// Don't worry about goroutine leaks in -short mode or in\n\t\t// benchmark mode. Too distracting when there are false positives.\n\t\treturn nil\n\t}\n\n\tvar stackCount map[string]int\n\tfor i := 0; i < 5; i++ {\n\t\tn := 0\n\t\tstackCount = make(map[string]int)\n\t\tgs := interestingGoroutines()\n\t\tfor _, g := range gs {\n\t\t\tstackCount[g]++\n\t\t\tn++\n\t\t}\n\t\tif n == 0 {\n\t\t\treturn nil\n\t\t}\n\t\t// Wait for goroutines to schedule and die off:\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n\tfor stack, count := range stackCount {\n\t\tfmt.Fprintf(os.Stderr, \"%d instances of:\\n%s\\n\", count, stack)\n\t}\n\treturn fmt.Errorf(\"too many goroutines running after dns test(s)\")\n}\n"
  },
  {
    "path": "length_test.go",
    "content": "package dns\n\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCompressLength(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeMX)\n\tul := m.Len()\n\tm.Compress = true\n\tif ul != m.Len() {\n\t\tt.Fatalf(\"should be equal\")\n\t}\n}\n\n// Does the predicted length match final packed length?\nfunc TestMsgCompressLength(t *testing.T) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\tmsg.Compress = true\n\t\treturn msg\n\t}\n\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrA := testRR(name1 + \" 3600 IN A 192.0.2.1\")\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\ttests := []*Msg{\n\t\tmakeMsg(name1, []RR{rrA}, nil, nil),\n\t\tmakeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}\n\n\tfor _, msg := range tests {\n\t\tpredicted := msg.Len()\n\t\tbuf, err := msg.Pack()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif predicted < len(buf) {\n\t\t\tt.Errorf(\"predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d\",\n\t\t\t\tmsg.Question[0].Name, len(msg.Answer), predicted, len(buf))\n\t\t}\n\t}\n}\n\nfunc TestMsgLength(t *testing.T) {\n\tmakeMsg := func(question string, ans, ns, e []RR) *Msg {\n\t\tmsg := new(Msg)\n\t\tmsg.Compress = true\n\t\tmsg.SetQuestion(Fqdn(question), TypeANY)\n\t\tmsg.Answer = append(msg.Answer, ans...)\n\t\tmsg.Ns = append(msg.Ns, ns...)\n\t\tmsg.Extra = append(msg.Extra, e...)\n\t\treturn msg\n\t}\n\n\tname1 := \"12345678901234567890123456789012345.12345678.123.\"\n\trrA := testRR(name1 + \" 3600 IN A 192.0.2.1\")\n\trrMx := testRR(name1 + \" 3600 IN MX 10 \" + name1)\n\ttests := []*Msg{\n\t\tmakeMsg(name1, []RR{rrA}, nil, nil),\n\t\tmakeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}\n\n\tfor _, msg := range tests {\n\t\tpredicted := msg.Len()\n\t\tbuf, err := msg.Pack()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif predicted < len(buf) {\n\t\t\tt.Errorf(\"predicted length is wrong: predicted %s (len=%d), actual %d\",\n\t\t\t\tmsg.Question[0].Name, predicted, len(buf))\n\t\t}\n\t}\n}\n\nfunc TestCompressionLenSearchInsert(t *testing.T) {\n\tc := make(map[string]struct{})\n\tcompressionLenSearch(c, \"example.com\", 12)\n\tif _, ok := c[\"example.com\"]; !ok {\n\t\tt.Errorf(\"bad example.com\")\n\t}\n\tif _, ok := c[\"com\"]; !ok {\n\t\tt.Errorf(\"bad com\")\n\t}\n\n\t// Test boundaries\n\tc = make(map[string]struct{})\n\t// foo label starts at 16379\n\t// com label starts at 16384\n\tcompressionLenSearch(c, \"foo.com\", 16379)\n\tif _, ok := c[\"foo.com\"]; !ok {\n\t\tt.Errorf(\"bad foo.com\")\n\t}\n\t// com label is accessible\n\tif _, ok := c[\"com\"]; !ok {\n\t\tt.Errorf(\"bad com\")\n\t}\n\n\tc = make(map[string]struct{})\n\t// foo label starts at 16379\n\t// com label starts at 16385 => outside range\n\tcompressionLenSearch(c, \"foo.com\", 16380)\n\tif _, ok := c[\"foo.com\"]; !ok {\n\t\tt.Errorf(\"bad foo.com\")\n\t}\n\t// com label is NOT accessible\n\tif _, ok := c[\"com\"]; ok {\n\t\tt.Errorf(\"bad com\")\n\t}\n\n\tc = make(map[string]struct{})\n\tcompressionLenSearch(c, \"example.com\", 16375)\n\tif _, ok := c[\"example.com\"]; !ok {\n\t\tt.Errorf(\"bad example.com\")\n\t}\n\t// com starts AFTER 16384\n\tif _, ok := c[\"com\"]; !ok {\n\t\tt.Errorf(\"bad com\")\n\t}\n\n\tc = make(map[string]struct{})\n\tcompressionLenSearch(c, \"example.com\", 16376)\n\tif _, ok := c[\"example.com\"]; !ok {\n\t\tt.Errorf(\"bad example.com\")\n\t}\n\t// com starts AFTER 16384\n\tif _, ok := c[\"com\"]; ok {\n\t\tt.Errorf(\"bad com\")\n\t}\n}\n\nfunc TestCompressionLenSearch(t *testing.T) {\n\tc := make(map[string]struct{})\n\tcompressed, ok := compressionLenSearch(c, \"a.b.org.\", maxCompressionOffset)\n\tif compressed != 0 || ok {\n\t\tt.Errorf(\"Failed: compressed:=%d, ok:=%v\", compressed, ok)\n\t}\n\tc[\"org.\"] = struct{}{}\n\tcompressed, ok = compressionLenSearch(c, \"a.b.org.\", maxCompressionOffset)\n\tif compressed != 4 || !ok {\n\t\tt.Errorf(\"Failed: compressed:=%d, ok:=%v\", compressed, ok)\n\t}\n\tc[\"b.org.\"] = struct{}{}\n\tcompressed, ok = compressionLenSearch(c, \"a.b.org.\", maxCompressionOffset)\n\tif compressed != 2 || !ok {\n\t\tt.Errorf(\"Failed: compressed:=%d, ok:=%v\", compressed, ok)\n\t}\n\t// Not found long compression\n\tc[\"x.b.org.\"] = struct{}{}\n\tcompressed, ok = compressionLenSearch(c, \"a.b.org.\", maxCompressionOffset)\n\tif compressed != 2 || !ok {\n\t\tt.Errorf(\"Failed: compressed:=%d, ok:=%v\", compressed, ok)\n\t}\n\t// Found long compression\n\tc[\"a.b.org.\"] = struct{}{}\n\tcompressed, ok = compressionLenSearch(c, \"a.b.org.\", maxCompressionOffset)\n\tif compressed != 0 || !ok {\n\t\tt.Errorf(\"Failed: compressed:=%d, ok:=%v\", compressed, ok)\n\t}\n}\n\nfunc TestMsgLength2(t *testing.T) {\n\t// Serialized replies\n\tvar testMessages = []string{\n\t\t// google.com. IN A?\n\t\t\"064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000\",\n\t\t// amazon.com. IN A? (reply has no EDNS0 record)\n\t\t\"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001\",\n\t\t// yahoo.com. IN A?\n\t\t\"fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000\",\n\t\t// microsoft.com. IN A?\n\t\t\"f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000\",\n\t\t// google.com. IN MX?\n\t\t\"724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000\",\n\t\t// reddit.com. IN A?\n\t\t\"12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000\",\n\t}\n\n\tfor i, hexData := range testMessages {\n\t\t// we won't fail the decoding of the hex\n\t\tinput, _ := hex.DecodeString(hexData)\n\n\t\tm := new(Msg)\n\t\tm.Unpack(input)\n\t\tm.Compress = true\n\t\tlenComp := m.Len()\n\t\tb, _ := m.Pack()\n\t\tpacComp := len(b)\n\t\tm.Compress = false\n\t\tlenUnComp := m.Len()\n\t\tb, _ = m.Pack()\n\t\tpacUnComp := len(b)\n\t\tif pacComp != lenComp {\n\t\t\tt.Errorf(\"msg.Len(compressed)=%d actual=%d for test %d\", lenComp, pacComp, i)\n\t\t}\n\t\tif pacUnComp != lenUnComp {\n\t\t\tt.Errorf(\"msg.Len(uncompressed)=%d actual=%d for test %d\", lenUnComp, pacUnComp, i)\n\t\t}\n\t}\n}\n\nfunc TestMsgLengthCompressionMalformed(t *testing.T) {\n\t// SOA with empty hostmaster, which is illegal\n\tsoa := &SOA{Hdr: RR_Header{Name: \".\", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},\n\t\tNs:      \".\",\n\t\tMbox:    \"\",\n\t\tSerial:  0,\n\t\tRefresh: 28800,\n\t\tRetry:   7200,\n\t\tExpire:  604800,\n\t\tMinttl:  60}\n\tm := new(Msg)\n\tm.Compress = true\n\tm.Ns = []RR{soa}\n\tm.Len() // Should not crash.\n}\n\nfunc TestMsgCompressLength2(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(Fqdn(\"bliep.\"), TypeANY)\n\tmsg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: \"blaat.\", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: \"foo.bar.\"})\n\tmsg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: \"foo.bar.\", Rrtype: 0x1, Class: 0x1, Ttl: 0x3c}, A: net.IP{0xac, 0x11, 0x0, 0x3}})\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Errorf(\"predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d\",\n\t\t\tmsg.Question[0].Name, len(msg.Answer), predicted, len(buf))\n\t}\n}\n\nfunc TestMsgCompressLengthLargeRecords(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"my.service.acme.\", TypeSRV)\n\tj := 1\n\tfor i := 0; i < 250; i++ {\n\t\ttarget := fmt.Sprintf(\"host-redis-1-%d.test.acme.com.node.dc1.consul.\", i)\n\t\tmsg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: \"redis.service.consul.\", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})\n\t\tmsg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf(\"fx.168.%d.%d.\", j, i)})\n\t}\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Fatalf(\"predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d\", msg.Question[0].Name, len(msg.Answer), predicted, len(buf))\n\t}\n}\n\nfunc compressionMapsEqual(a map[string]struct{}, b map[string]int) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\n\tfor k := range a {\n\t\tif _, ok := b[k]; !ok {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc compressionMapsDifference(a map[string]struct{}, b map[string]int) string {\n\tvar s strings.Builder\n\n\tvar c int\n\tfmt.Fprintf(&s, \"length compression map (%d):\", len(a))\n\tfor k := range b {\n\t\tif _, ok := a[k]; !ok {\n\t\t\tif c > 0 {\n\t\t\t\ts.WriteString(\",\")\n\t\t\t}\n\n\t\t\tfmt.Fprintf(&s, \" missing %q\", k)\n\t\t\tc++\n\t\t}\n\t}\n\n\tc = 0\n\tfmt.Fprintf(&s, \"\\npack compression map (%d):\", len(b))\n\tfor k := range a {\n\t\tif _, ok := b[k]; !ok {\n\t\t\tif c > 0 {\n\t\t\t\ts.WriteString(\",\")\n\t\t\t}\n\n\t\t\tfmt.Fprintf(&s, \" missing %q\", k)\n\t\t\tc++\n\t\t}\n\t}\n\n\treturn s.String()\n}\n\nfunc TestCompareCompressionMapsForANY(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"a.service.acme.\", TypeANY)\n\t// Be sure to have more than 14bits\n\tfor i := 0; i < 2000; i++ {\n\t\ttarget := fmt.Sprintf(\"host.app-%d.x%d.test.acme.\", i%250, i)\n\t\tmsg.Answer = append(msg.Answer, &AAAA{Hdr: RR_Header{Name: target, Rrtype: TypeAAAA, Class: ClassINET, Ttl: 0x3c}, AAAA: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i / 255), byte(i % 255)}})\n\t\tmsg.Answer = append(msg.Answer, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})\n\t\tif msg.Len() > 16384 {\n\t\t\tbreak\n\t\t}\n\t}\n\tfor labelSize := 0; labelSize < 63; labelSize++ {\n\t\tmsg.SetQuestion(fmt.Sprintf(\"a%s.service.acme.\", strings.Repeat(\"x\", labelSize)), TypeANY)\n\n\t\tcompressionFake := make(map[string]struct{})\n\t\tlenFake := msgLenWithCompressionMap(msg, compressionFake)\n\n\t\tcompressionReal := make(map[string]int)\n\t\tbuf, err := msg.packBufferWithCompressionMap(nil, compressionMap{ext: compressionReal}, true)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif lenFake != len(buf) {\n\t\t\tt.Fatalf(\"padding= %d ; Predicted len := %d != real:= %d\", labelSize, lenFake, len(buf))\n\t\t}\n\t\tif !compressionMapsEqual(compressionFake, compressionReal) {\n\t\t\tt.Fatalf(\"padding= %d ; Fake Compression Map != Real Compression Map\\n%s\", labelSize, compressionMapsDifference(compressionFake, compressionReal))\n\t\t}\n\t}\n}\n\nfunc TestCompareCompressionMapsForSRV(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"a.service.acme.\", TypeSRV)\n\t// Be sure to have more than 14bits\n\tfor i := 0; i < 2000; i++ {\n\t\ttarget := fmt.Sprintf(\"host.app-%d.x%d.test.acme.\", i%250, i)\n\t\tmsg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: \"redis.service.consul.\", Class: ClassINET, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})\n\t\tmsg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})\n\t\tif msg.Len() > 16384 {\n\t\t\tbreak\n\t\t}\n\t}\n\tfor labelSize := 0; labelSize < 63; labelSize++ {\n\t\tmsg.SetQuestion(fmt.Sprintf(\"a%s.service.acme.\", strings.Repeat(\"x\", labelSize)), TypeAAAA)\n\n\t\tcompressionFake := make(map[string]struct{})\n\t\tlenFake := msgLenWithCompressionMap(msg, compressionFake)\n\n\t\tcompressionReal := make(map[string]int)\n\t\tbuf, err := msg.packBufferWithCompressionMap(nil, compressionMap{ext: compressionReal}, true)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif lenFake != len(buf) {\n\t\t\tt.Fatalf(\"padding= %d ; Predicted len := %d != real:= %d\", labelSize, lenFake, len(buf))\n\t\t}\n\t\tif !compressionMapsEqual(compressionFake, compressionReal) {\n\t\t\tt.Fatalf(\"padding= %d ; Fake Compression Map != Real Compression Map\\n%s\", labelSize, compressionMapsDifference(compressionFake, compressionReal))\n\t\t}\n\t}\n}\n\nfunc TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"my.service.acme.\", TypeSRV)\n\n\tfor i := 0; i < 250; i++ {\n\t\ttarget := fmt.Sprintf(\"host-redis-x-%d.test.acme.com.node.dc1.consul.\", i)\n\t\tmsg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: \"redis.service.consul.\", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})\n\t\tmsg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf(\"fx.168.x.%d.\", i)})\n\t}\n\tfor labelSize := 1; labelSize < 63; labelSize++ {\n\t\tmsg.SetQuestion(fmt.Sprintf(\"my.%s.service.acme.\", strings.Repeat(\"x\", labelSize)), TypeSRV)\n\t\tpredicted := msg.Len()\n\t\tbuf, err := msg.Pack()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif predicted != len(buf) {\n\t\t\tt.Fatalf(\"padding= %d ; predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d\", labelSize, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))\n\t\t}\n\t}\n}\n\nfunc TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) {\n\t// we want to cross the 14 (16384) bit boundary here, so we build it up to just below and go slightly over.\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"redis.service.consul.\", TypeSRV)\n\tfor i := 0; i < 170; i++ {\n\t\ttarget := fmt.Sprintf(\"host-redis-%d-%d.test.acme.com.node.dc1.consul.\", i/256, i%256)\n\t\tmsg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: \"redis.service.consul.\", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})\n\t\tmsg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf(\"fx.168.%d.%d.\", i/256, i%256)})\n\t}\n\t// msg.Len() == 15458\n\t// msg.Len() == 16470 at 180\n\n\tfor i := 170; i < 181; i++ {\n\t\ttarget := fmt.Sprintf(\"host-redis-%d-%d.test.acme.com.node.dc1.consul.\", i/256, i%256)\n\t\tmsg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: \"redis.service.consul.\", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})\n\t\tmsg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf(\"fx.168.%d.%d.\", i/256, i%256)})\n\t\tpredicted := msg.Len()\n\t\tbuf, err := msg.Pack()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif predicted != len(buf) {\n\t\t\tt.Fatalf(\"predicted compressed length is wrong for %d records: predicted %s (len=%d) %d, actual %d\", i, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))\n\t\t}\n\t}\n}\n\nfunc TestMsgCompressionMultipleQuestions(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"www.example.org.\", TypeA)\n\tmsg.Question = append(msg.Question, Question{\"other.example.org.\", TypeA, ClassINET})\n\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Fatalf(\"predicted compressed length is wrong: predicted %d, actual %d\", predicted, len(buf))\n\t}\n}\n\nfunc TestMsgCompressMultipleCompressedNames(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"www.example.com.\", TypeSRV)\n\tmsg.Answer = append(msg.Answer, &MINFO{\n\t\tHdr:   RR_Header{Name: \"www.example.com.\", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c},\n\t\tRmail: \"mail.example.org.\",\n\t\tEmail: \"mail.example.org.\",\n\t})\n\tmsg.Answer = append(msg.Answer, &SOA{\n\t\tHdr:  RR_Header{Name: \"www.example.com.\", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c},\n\t\tNs:   \"ns.example.net.\",\n\t\tMbox: \"mail.example.net.\",\n\t})\n\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Fatalf(\"predicted compressed length is wrong: predicted %d, actual %d\", predicted, len(buf))\n\t}\n}\n\nfunc TestMsgCompressLengthEscapingMatch(t *testing.T) {\n\t// Although slightly non-optimal, \"example.org.\" and \"ex\\\\097mple.org.\"\n\t// are not considered equal in the compression map, even though \\097 is\n\t// a valid escaping of a. This test ensures that the Len code and the\n\t// Pack code don't disagree on this.\n\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"www.example.org.\", TypeA)\n\tmsg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: \"ex\\\\097mple.org.\", Rrtype: TypeNS, Class: ClassINET}, Ns: \"ns.example.org.\"})\n\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Fatalf(\"predicted compressed length is wrong: predicted %d, actual %d\", predicted, len(buf))\n\t}\n}\n\nfunc TestMsgLengthEscaped(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.SetQuestion(`\\000\\001\\002.\\003\\004\\005\\006\\007\\008\\009.\\a\\b\\c.`, TypeA)\n\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Fatalf(\"predicted compressed length is wrong: predicted %d, actual %d\", predicted, len(buf))\n\t}\n}\n\nfunc TestMsgCompressLengthEscaped(t *testing.T) {\n\tmsg := new(Msg)\n\tmsg.Compress = true\n\tmsg.SetQuestion(\"www.example.org.\", TypeA)\n\tmsg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: `\\000\\001\\002.example.org.`, Rrtype: TypeNS, Class: ClassINET}, Ns: `ns.\\e\\x\\a\\m\\p\\l\\e.org.`})\n\tmsg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: `www.\\e\\x\\a\\m\\p\\l\\e.org.`, Rrtype: TypeNS, Class: ClassINET}, Ns: \"ns.example.org.\"})\n\n\tpredicted := msg.Len()\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif predicted != len(buf) {\n\t\tt.Fatalf(\"predicted compressed length is wrong: predicted %d, actual %d\", predicted, len(buf))\n\t}\n}\n"
  },
  {
    "path": "listen_no_socket_options.go",
    "content": "//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd\n// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd\n\npackage dns\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\nconst (\n\tsupportsReusePort = false\n\tsupportsReuseAddr = false\n)\n\nfunc listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) {\n\tif reuseport || reuseaddr {\n\t\t// TODO(tmthrgd): return an error?\n\t}\n\n\treturn net.Listen(network, addr)\n}\n\nfunc listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) {\n\tif reuseport || reuseaddr {\n\t\t// TODO(tmthrgd): return an error?\n\t}\n\n\treturn net.ListenPacket(network, addr)\n}\n\n// this is just for test compatibility\nfunc checkReuseport(fd uintptr) (bool, error) {\n\treturn false, fmt.Errorf(\"not supported\")\n}\n\n// this is just for test compatibility\nfunc checkReuseaddr(fd uintptr) (bool, error) {\n\treturn false, fmt.Errorf(\"not supported\")\n}\n"
  },
  {
    "path": "listen_socket_options.go",
    "content": "//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd\n// +build aix darwin dragonfly freebsd linux netbsd openbsd\n\npackage dns\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst supportsReusePort = true\n\nfunc reuseportControl(network, address string, c syscall.RawConn) error {\n\tvar opErr error\n\terr := c.Control(func(fd uintptr) {\n\t\topErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn opErr\n}\n\nconst supportsReuseAddr = true\n\nfunc reuseaddrControl(network, address string, c syscall.RawConn) error {\n\tvar opErr error\n\terr := c.Control(func(fd uintptr) {\n\t\topErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn opErr\n}\n\nfunc reuseaddrandportControl(network, address string, c syscall.RawConn) error {\n\terr := reuseaddrControl(network, address, c)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn reuseportControl(network, address, c)\n}\n\n// this is just for test compatibility\nfunc checkReuseport(fd uintptr) (bool, error) {\n\tv, err := unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn v == 1, nil\n}\n\n// this is just for test compatibility\nfunc checkReuseaddr(fd uintptr) (bool, error) {\n\tv, err := unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn v == 1, nil\n}\n\nfunc listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) {\n\tvar lc net.ListenConfig\n\tswitch {\n\tcase reuseaddr && reuseport:\n\t\tlc.Control = reuseaddrandportControl\n\tcase reuseport:\n\t\tlc.Control = reuseportControl\n\tcase reuseaddr:\n\t\tlc.Control = reuseaddrControl\n\t}\n\n\treturn lc.Listen(context.Background(), network, addr)\n}\n\nfunc listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) {\n\tvar lc net.ListenConfig\n\tswitch {\n\tcase reuseaddr && reuseport:\n\t\tlc.Control = reuseaddrandportControl\n\tcase reuseport:\n\t\tlc.Control = reuseportControl\n\tcase reuseaddr:\n\t\tlc.Control = reuseaddrControl\n\t}\n\n\treturn lc.ListenPacket(context.Background(), network, addr)\n}\n"
  },
  {
    "path": "msg.go",
    "content": "// DNS packet assembly, see RFC 1035. Converting from - Unpack() -\n// and to - Pack() - wire format.\n// All the packers and unpackers take a (msg []byte, off int)\n// and return (off1 int, ok bool).  If they return ok==false, they\n// also return off1==len(msg), so that the next unpacker will\n// also fail.  This lets us avoid checks of ok until the end of a\n// packing sequence.\n\npackage dns\n\n//go:generate go run msg_generate.go\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nconst (\n\tmaxCompressionOffset    = 2 << 13 // We have 14 bits for the compression pointer\n\tmaxDomainNameWireOctets = 255     // See RFC 1035 section 2.3.4\n\n\t// This is the maximum number of compression pointers that should occur in a\n\t// semantically valid message. Each label in a domain name must be at least one\n\t// octet and is separated by a period. The root label won't be represented by a\n\t// compression pointer to a compression pointer, hence the -2 to exclude the\n\t// smallest valid root label.\n\t//\n\t// It is possible to construct a valid message that has more compression pointers\n\t// than this, and still doesn't loop, by pointing to a previous pointer. This is\n\t// not something a well written implementation should ever do, so we leave them\n\t// to trip the maximum compression pointer check.\n\tmaxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2\n\n\t// This is the maximum length of a domain name in presentation format. The\n\t// maximum wire length of a domain name is 255 octets (see above), with the\n\t// maximum label length being 63. The wire format requires one extra byte over\n\t// the presentation format, reducing the number of octets by 1. Each label in\n\t// the name will be separated by a single period, with each octet in the label\n\t// expanding to at most 4 bytes (\\DDD). If all other labels are of the maximum\n\t// length, then the final label can only be 61 octets long to not exceed the\n\t// maximum allowed wire length.\n\tmaxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1\n)\n\n// Errors defined in this package.\nvar (\n\tErrAlg           error = &Error{err: \"bad algorithm\"}                  // ErrAlg indicates an error with the (DNSSEC) algorithm.\n\tErrAuth          error = &Error{err: \"bad authentication\"}             // ErrAuth indicates an error in the TSIG authentication.\n\tErrBuf           error = &Error{err: \"buffer size too small\"}          // ErrBuf indicates that the buffer used is too small for the message.\n\tErrConnEmpty     error = &Error{err: \"conn has no connection\"}         // ErrConnEmpty indicates a connection is being used before it is initialized.\n\tErrExtendedRcode error = &Error{err: \"bad extended rcode\"}             // ErrExtendedRcode ...\n\tErrFqdn          error = &Error{err: \"domain must be fully qualified\"} // ErrFqdn indicates that a domain name does not have a closing dot.\n\tErrId            error = &Error{err: \"id mismatch\"}                    // ErrId indicates there is a mismatch with the message's ID.\n\tErrKeyAlg        error = &Error{err: \"bad key algorithm\"}              // ErrKeyAlg indicates that the algorithm in the key is not valid.\n\tErrKey           error = &Error{err: \"bad key\"}\n\tErrKeySize       error = &Error{err: \"bad key size\"}\n\tErrLongDomain    error = &Error{err: fmt.Sprintf(\"domain name exceeded %d wire-format octets\", maxDomainNameWireOctets)}\n\tErrNoSig         error = &Error{err: \"no signature found\"}\n\tErrPrivKey       error = &Error{err: \"bad private key\"}\n\tErrRcode         error = &Error{err: \"bad rcode\"}\n\tErrRdata         error = &Error{err: \"bad rdata\"}\n\tErrRRset         error = &Error{err: \"bad rrset\"}\n\tErrSecret        error = &Error{err: \"no secrets defined\"}\n\tErrShortRead     error = &Error{err: \"short read\"}\n\tErrSig           error = &Error{err: \"bad signature\"} // ErrSig indicates that a signature can not be cryptographically validated.\n\tErrSoa           error = &Error{err: \"no SOA\"}        // ErrSOA indicates that no SOA RR was seen when doing zone transfers.\n\tErrTime          error = &Error{err: \"bad time\"}      // ErrTime indicates a timing error in TSIG authentication.\n)\n\n// Id by default returns a 16-bit random number to be used as a message id. The\n// number is drawn from a cryptographically secure random number generator.\n// This being a variable the function can be reassigned to a custom function.\n// For instance, to make it return a static value for testing:\n//\n//\tdns.Id = func() uint16 { return 3 }\nvar Id = id\n\n// id returns a 16 bits random number to be used as a\n// message id. The random provided should be good enough.\nfunc id() uint16 {\n\tvar output uint16\n\terr := binary.Read(rand.Reader, binary.BigEndian, &output)\n\tif err != nil {\n\t\tpanic(\"dns: reading random id failed: \" + err.Error())\n\t}\n\treturn output\n}\n\n// MsgHdr is a a manually-unpacked version of (id, bits).\ntype MsgHdr struct {\n\tId                 uint16\n\tResponse           bool\n\tOpcode             int\n\tAuthoritative      bool\n\tTruncated          bool\n\tRecursionDesired   bool\n\tRecursionAvailable bool\n\tZero               bool\n\tAuthenticatedData  bool\n\tCheckingDisabled   bool\n\tRcode              int\n}\n\n// Msg contains the layout of a DNS message.\ntype Msg struct {\n\tMsgHdr\n\tCompress bool       `json:\"-\"` // If true, the message will be compressed when converted to wire format.\n\tQuestion []Question // Holds the RR(s) of the question section.\n\tAnswer   []RR       // Holds the RR(s) of the answer section.\n\tNs       []RR       // Holds the RR(s) of the authority section.\n\tExtra    []RR       // Holds the RR(s) of the additional section.\n}\n\n// ClassToString is a maps Classes to strings for each CLASS wire type.\nvar ClassToString = map[uint16]string{\n\tClassINET:   \"IN\",\n\tClassCSNET:  \"CS\",\n\tClassCHAOS:  \"CH\",\n\tClassHESIOD: \"HS\",\n\tClassNONE:   \"NONE\",\n\tClassANY:    \"ANY\",\n}\n\n// OpcodeToString maps Opcodes to strings.\nvar OpcodeToString = map[int]string{\n\tOpcodeQuery:  \"QUERY\",\n\tOpcodeIQuery: \"IQUERY\",\n\tOpcodeStatus: \"STATUS\",\n\tOpcodeNotify: \"NOTIFY\",\n\tOpcodeUpdate: \"UPDATE\",\n}\n\n// RcodeToString maps Rcodes to strings.\nvar RcodeToString = map[int]string{\n\tRcodeSuccess:                    \"NOERROR\",\n\tRcodeFormatError:                \"FORMERR\",\n\tRcodeServerFailure:              \"SERVFAIL\",\n\tRcodeNameError:                  \"NXDOMAIN\",\n\tRcodeNotImplemented:             \"NOTIMP\",\n\tRcodeRefused:                    \"REFUSED\",\n\tRcodeYXDomain:                   \"YXDOMAIN\", // See RFC 2136\n\tRcodeYXRrset:                    \"YXRRSET\",\n\tRcodeNXRrset:                    \"NXRRSET\",\n\tRcodeNotAuth:                    \"NOTAUTH\",\n\tRcodeNotZone:                    \"NOTZONE\",\n\tRcodeStatefulTypeNotImplemented: \"DSOTYPENI\",\n\tRcodeBadSig:                     \"BADSIG\", // Also known as RcodeBadVers, see RFC 6891\n\t//\tRcodeBadVers:        \"BADVERS\",\n\tRcodeBadKey:    \"BADKEY\",\n\tRcodeBadTime:   \"BADTIME\",\n\tRcodeBadMode:   \"BADMODE\",\n\tRcodeBadName:   \"BADNAME\",\n\tRcodeBadAlg:    \"BADALG\",\n\tRcodeBadTrunc:  \"BADTRUNC\",\n\tRcodeBadCookie: \"BADCOOKIE\",\n}\n\n// compressionMap is used to allow a more efficient compression map\n// to be used for internal packDomainName calls without changing the\n// signature or functionality of public API.\n//\n// In particular, map[string]uint16 uses 25% less per-entry memory\n// than does map[string]int.\ntype compressionMap struct {\n\text map[string]int    // external callers\n\tint map[string]uint16 // internal callers\n}\n\nfunc (m compressionMap) valid() bool {\n\treturn m.int != nil || m.ext != nil\n}\n\nfunc (m compressionMap) insert(s string, pos int) {\n\tif m.ext != nil {\n\t\tm.ext[s] = pos\n\t} else {\n\t\tm.int[s] = uint16(pos)\n\t}\n}\n\nfunc (m compressionMap) find(s string) (int, bool) {\n\tif m.ext != nil {\n\t\tpos, ok := m.ext[s]\n\t\treturn pos, ok\n\t}\n\n\tpos, ok := m.int[s]\n\treturn int(pos), ok\n}\n\n// Domain names are a sequence of counted strings\n// split at the dots. They end with a zero-length string.\n\n// PackDomainName packs a domain name s into msg[off:].\n// If compression is wanted compress must be true and the compression\n// map needs to hold a mapping between domain names and offsets\n// pointing into msg.\nfunc PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {\n\treturn packDomainName(s, msg, off, compressionMap{ext: compression}, compress)\n}\n\nfunc packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\t// XXX: A logical copy of this function exists in IsDomainName and\n\t// should be kept in sync with this function.\n\n\tls := len(s)\n\tif ls == 0 { // Ok, for instance when dealing with update RR without any rdata.\n\t\treturn off, nil\n\t}\n\n\t// If not fully qualified, error out.\n\tif !IsFqdn(s) {\n\t\treturn len(msg), ErrFqdn\n\t}\n\n\t// Each dot ends a segment of the name.\n\t// We trade each dot byte for a length byte.\n\t// Except for escaped dots (\\.), which are normal dots.\n\t// There is also a trailing zero.\n\n\t// Compression\n\tpointer := -1\n\n\t// Emit sequence of counted strings, chopping at dots.\n\tvar (\n\t\tbegin     int\n\t\tcompBegin int\n\t\tcompOff   int\n\t\tbs        []byte\n\t\twasDot    bool\n\t)\nloop:\n\tfor i := 0; i < ls; i++ {\n\t\tvar c byte\n\t\tif bs == nil {\n\t\t\tc = s[i]\n\t\t} else {\n\t\t\tc = bs[i]\n\t\t}\n\n\t\tswitch c {\n\t\tcase '\\\\':\n\t\t\tif off+1 > len(msg) {\n\t\t\t\treturn len(msg), ErrBuf\n\t\t\t}\n\n\t\t\tif bs == nil {\n\t\t\t\tbs = []byte(s)\n\t\t\t}\n\n\t\t\t// check for \\DDD\n\t\t\tif isDDD(bs[i+1:]) {\n\t\t\t\tbs[i] = dddToByte(bs[i+1:])\n\t\t\t\tcopy(bs[i+1:ls-3], bs[i+4:])\n\t\t\t\tls -= 3\n\t\t\t\tcompOff += 3\n\t\t\t} else {\n\t\t\t\tcopy(bs[i:ls-1], bs[i+1:])\n\t\t\t\tls--\n\t\t\t\tcompOff++\n\t\t\t}\n\n\t\t\twasDot = false\n\t\tcase '.':\n\t\t\tif i == 0 && len(s) > 1 {\n\t\t\t\t// leading dots are not legal except for the root zone\n\t\t\t\treturn len(msg), ErrRdata\n\t\t\t}\n\n\t\t\tif wasDot {\n\t\t\t\t// two dots back to back is not legal\n\t\t\t\treturn len(msg), ErrRdata\n\t\t\t}\n\t\t\twasDot = true\n\n\t\t\tlabelLen := i - begin\n\t\t\tif labelLen >= 1<<6 { // top two bits of length must be clear\n\t\t\t\treturn len(msg), ErrRdata\n\t\t\t}\n\n\t\t\t// off can already (we're in a loop) be bigger than len(msg)\n\t\t\t// this happens when a name isn't fully qualified\n\t\t\tif off+1+labelLen > len(msg) {\n\t\t\t\treturn len(msg), ErrBuf\n\t\t\t}\n\n\t\t\t// Don't try to compress '.'\n\t\t\t// We should only compress when compress is true, but we should also still pick\n\t\t\t// up names that can be used for *future* compression(s).\n\t\t\tif compression.valid() && !isRootLabel(s, bs, begin, ls) {\n\t\t\t\tif p, ok := compression.find(s[compBegin:]); ok {\n\t\t\t\t\t// The first hit is the longest matching dname\n\t\t\t\t\t// keep the pointer offset we get back and store\n\t\t\t\t\t// the offset of the current name, because that's\n\t\t\t\t\t// where we need to insert the pointer later\n\n\t\t\t\t\t// If compress is true, we're allowed to compress this dname\n\t\t\t\t\tif compress {\n\t\t\t\t\t\tpointer = p // Where to point to\n\t\t\t\t\t\tbreak loop\n\t\t\t\t\t}\n\t\t\t\t} else if off < maxCompressionOffset {\n\t\t\t\t\t// Only offsets smaller than maxCompressionOffset can be used.\n\t\t\t\t\tcompression.insert(s[compBegin:], off)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// The following is covered by the length check above.\n\t\t\tmsg[off] = byte(labelLen)\n\n\t\t\tif bs == nil {\n\t\t\t\tcopy(msg[off+1:], s[begin:i])\n\t\t\t} else {\n\t\t\t\tcopy(msg[off+1:], bs[begin:i])\n\t\t\t}\n\t\t\toff += 1 + labelLen\n\n\t\t\tbegin = i + 1\n\t\t\tcompBegin = begin + compOff\n\t\tdefault:\n\t\t\twasDot = false\n\t\t}\n\t}\n\n\t// Root label is special\n\tif isRootLabel(s, bs, 0, ls) {\n\t\treturn off, nil\n\t}\n\n\t// If we did compression and we find something add the pointer here\n\tif pointer != -1 {\n\t\t// We have two bytes (14 bits) to put the pointer in\n\t\tbinary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000))\n\t\treturn off + 2, nil\n\t}\n\n\t// Trailing root label\n\tif off < len(msg) {\n\t\tmsg[off] = 0\n\t\treturn off + 1, nil\n\t}\n\n\treturn off, ErrBuf\n}\n\n// isRootLabel returns whether s or bs, from off to end, is the root\n// label \".\".\n//\n// If bs is nil, s will be checked, otherwise bs will be checked.\nfunc isRootLabel(s string, bs []byte, off, end int) bool {\n\tif bs == nil {\n\t\treturn s[off:end] == \".\"\n\t}\n\n\treturn end-off == 1 && bs[off] == '.'\n}\n\n// Unpack a domain name.\n// In addition to the simple sequences of counted strings above,\n// domain names are allowed to refer to strings elsewhere in the\n// packet, to avoid repeating common suffixes when returning\n// many entries in a single domain.  The pointers are marked\n// by a length byte with the top two bits set.  Ignoring those\n// two bits, that byte and the next give a 14 bit offset from msg[0]\n// where we should pick up the trail.\n// Note that if we jump elsewhere in the packet,\n// we return off1 == the offset after the first pointer we found,\n// which is where the next record will start.\n// In theory, the pointers are only allowed to jump backward.\n// We let them jump anywhere and stop jumping after a while.\n\n// UnpackDomainName unpacks a domain name into a string. It returns\n// the name, the new offset into msg and any error that occurred.\n//\n// When an error is encountered, the unpacked name will be discarded\n// and len(msg) will be returned as the offset.\nfunc UnpackDomainName(msg []byte, off int) (string, int, error) {\n\ts := make([]byte, 0, maxDomainNamePresentationLength)\n\toff1 := 0\n\tlenmsg := len(msg)\n\tbudget := maxDomainNameWireOctets\n\tptr := 0 // number of pointers followed\nLoop:\n\tfor {\n\t\tif off >= lenmsg {\n\t\t\treturn \"\", lenmsg, ErrBuf\n\t\t}\n\t\tc := int(msg[off])\n\t\toff++\n\t\tswitch c & 0xC0 {\n\t\tcase 0x00:\n\t\t\tif c == 0x00 {\n\t\t\t\t// end of name\n\t\t\t\tbreak Loop\n\t\t\t}\n\t\t\t// literal string\n\t\t\tif off+c > lenmsg {\n\t\t\t\treturn \"\", lenmsg, ErrBuf\n\t\t\t}\n\t\t\tbudget -= c + 1 // +1 for the label separator\n\t\t\tif budget <= 0 {\n\t\t\t\treturn \"\", lenmsg, ErrLongDomain\n\t\t\t}\n\t\t\tfor _, b := range msg[off : off+c] {\n\t\t\t\tif isDomainNameLabelSpecial(b) {\n\t\t\t\t\ts = append(s, '\\\\', b)\n\t\t\t\t} else if b < ' ' || b > '~' {\n\t\t\t\t\ts = append(s, escapeByte(b)...)\n\t\t\t\t} else {\n\t\t\t\t\ts = append(s, b)\n\t\t\t\t}\n\t\t\t}\n\t\t\ts = append(s, '.')\n\t\t\toff += c\n\t\tcase 0xC0:\n\t\t\t// pointer to somewhere else in msg.\n\t\t\t// remember location after first ptr,\n\t\t\t// since that's how many bytes we consumed.\n\t\t\t// also, don't follow too many pointers --\n\t\t\t// maybe there's a loop.\n\t\t\tif off >= lenmsg {\n\t\t\t\treturn \"\", lenmsg, ErrBuf\n\t\t\t}\n\t\t\tc1 := msg[off]\n\t\t\toff++\n\t\t\tif ptr == 0 {\n\t\t\t\toff1 = off\n\t\t\t}\n\t\t\tif ptr++; ptr > maxCompressionPointers {\n\t\t\t\treturn \"\", lenmsg, &Error{err: \"too many compression pointers\"}\n\t\t\t}\n\t\t\t// pointer should guarantee that it advances and points forwards at least\n\t\t\t// but the condition on previous three lines guarantees that it's\n\t\t\t// at least loop-free\n\t\t\toff = (c^0xC0)<<8 | int(c1)\n\t\tdefault:\n\t\t\t// 0x80 and 0x40 are reserved\n\t\t\treturn \"\", lenmsg, ErrRdata\n\t\t}\n\t}\n\tif ptr == 0 {\n\t\toff1 = off\n\t}\n\tif len(s) == 0 {\n\t\treturn \".\", off1, nil\n\t}\n\treturn string(s), off1, nil\n}\n\nfunc packTxt(txt []string, msg []byte, offset int) (int, error) {\n\tif len(txt) == 0 {\n\t\tif offset >= len(msg) {\n\t\t\treturn offset, ErrBuf\n\t\t}\n\t\tmsg[offset] = 0\n\t\treturn offset, nil\n\t}\n\tvar err error\n\tfor _, s := range txt {\n\t\toffset, err = packTxtString(s, msg, offset)\n\t\tif err != nil {\n\t\t\treturn offset, err\n\t\t}\n\t}\n\treturn offset, nil\n}\n\nfunc packTxtString(s string, msg []byte, offset int) (int, error) {\n\tlenByteOffset := offset\n\tif offset >= len(msg) || len(s) > 256*4+1 /* If all \\DDD */ {\n\t\treturn offset, ErrBuf\n\t}\n\toffset++\n\tfor i := 0; i < len(s); i++ {\n\t\tif len(msg) <= offset {\n\t\t\treturn offset, ErrBuf\n\t\t}\n\t\tif s[i] == '\\\\' {\n\t\t\ti++\n\t\t\tif i == len(s) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t// check for \\DDD\n\t\t\tif isDDD(s[i:]) {\n\t\t\t\tmsg[offset] = dddToByte(s[i:])\n\t\t\t\ti += 2\n\t\t\t} else {\n\t\t\t\tmsg[offset] = s[i]\n\t\t\t}\n\t\t} else {\n\t\t\tmsg[offset] = s[i]\n\t\t}\n\t\toffset++\n\t}\n\tl := offset - lenByteOffset - 1\n\tif l > 255 {\n\t\treturn offset, &Error{err: \"string exceeded 255 bytes in txt\"}\n\t}\n\tmsg[lenByteOffset] = byte(l)\n\treturn offset, nil\n}\n\nfunc packOctetString(s string, msg []byte, offset int) (int, error) {\n\tif offset >= len(msg) || len(s) > 256*4+1 {\n\t\treturn offset, ErrBuf\n\t}\n\tfor i := 0; i < len(s); i++ {\n\t\tif len(msg) <= offset {\n\t\t\treturn offset, ErrBuf\n\t\t}\n\t\tif s[i] == '\\\\' {\n\t\t\ti++\n\t\t\tif i == len(s) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t// check for \\DDD\n\t\t\tif isDDD(s[i:]) {\n\t\t\t\tmsg[offset] = dddToByte(s[i:])\n\t\t\t\ti += 2\n\t\t\t} else {\n\t\t\t\tmsg[offset] = s[i]\n\t\t\t}\n\t\t} else {\n\t\t\tmsg[offset] = s[i]\n\t\t}\n\t\toffset++\n\t}\n\treturn offset, nil\n}\n\nfunc unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {\n\toff = off0\n\tvar s string\n\tfor off < len(msg) && err == nil {\n\t\ts, off, err = unpackString(msg, off)\n\t\tif err == nil {\n\t\t\tss = append(ss, s)\n\t\t}\n\t}\n\treturn\n}\n\n// Helpers for dealing with escaped bytes\nfunc isDigit(b byte) bool { return b >= '0' && b <= '9' }\n\nfunc isDDD[T ~[]byte | ~string](s T) bool {\n\treturn len(s) >= 3 && isDigit(s[0]) && isDigit(s[1]) && isDigit(s[2])\n}\n\nfunc dddToByte[T ~[]byte | ~string](s T) byte {\n\t_ = s[2] // bounds check hint to compiler; see golang.org/issue/14808\n\treturn byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))\n}\n\n// Helper function for packing and unpacking\nfunc intToBytes(i *big.Int, length int) []byte {\n\tbuf := i.Bytes()\n\tif len(buf) < length {\n\t\tb := make([]byte, length)\n\t\tcopy(b[length-len(buf):], buf)\n\t\treturn b\n\t}\n\treturn buf\n}\n\n// PackRR packs a resource record rr into msg[off:].\n// See PackDomainName for documentation about the compression.\nfunc PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {\n\theaderEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress)\n\tif err == nil {\n\t\t// packRR no longer sets the Rdlength field on the rr, but\n\t\t// callers might be expecting it so we set it here.\n\t\trr.Header().Rdlength = uint16(off1 - headerEnd)\n\t}\n\treturn off1, err\n}\n\nfunc packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) {\n\tif rr == nil {\n\t\treturn len(msg), len(msg), &Error{err: \"nil rr\"}\n\t}\n\n\theaderEnd, err = rr.Header().packHeader(msg, off, compression, compress)\n\tif err != nil {\n\t\treturn headerEnd, len(msg), err\n\t}\n\n\toff1, err = rr.pack(msg, headerEnd, compression, compress)\n\tif err != nil {\n\t\treturn headerEnd, len(msg), err\n\t}\n\n\trdlength := off1 - headerEnd\n\tif int(uint16(rdlength)) != rdlength { // overflow\n\t\treturn headerEnd, len(msg), ErrRdata\n\t}\n\n\t// The RDLENGTH field is the last field in the header and we set it here.\n\tbinary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength))\n\treturn headerEnd, off1, nil\n}\n\n// UnpackRR unpacks msg[off:] into an RR.\nfunc UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {\n\th, off, msg, err := unpackHeader(msg, off)\n\tif err != nil {\n\t\treturn nil, len(msg), err\n\t}\n\n\treturn UnpackRRWithHeader(h, msg, off)\n}\n\n// UnpackRRWithHeader unpacks the record type specific payload given an existing\n// RR_Header.\nfunc UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) {\n\tif newFn, ok := TypeToRR[h.Rrtype]; ok {\n\t\trr = newFn()\n\t\t*rr.Header() = h\n\t} else {\n\t\trr = &RFC3597{Hdr: h}\n\t}\n\n\tif off < 0 || off > len(msg) {\n\t\treturn &h, off, &Error{err: \"bad off\"}\n\t}\n\n\tend := off + int(h.Rdlength)\n\tif end < off || end > len(msg) {\n\t\treturn &h, end, &Error{err: \"bad rdlength\"}\n\t}\n\n\tif noRdata(h) {\n\t\treturn rr, off, nil\n\t}\n\n\toff, err = rr.unpack(msg, off)\n\tif err != nil {\n\t\treturn nil, end, err\n\t}\n\tif off != end {\n\t\treturn &h, end, &Error{err: \"bad rdlength\"}\n\t}\n\n\treturn rr, off, nil\n}\n\n// unpackRRslice unpacks msg[off:] into an []RR.\n// If we cannot unpack the whole array, then it will return nil\nfunc unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) {\n\tvar r RR\n\t// Don't pre-allocate, l may be under attacker control\n\tvar dst []RR\n\tfor i := 0; i < l; i++ {\n\t\toff1 := off\n\t\tr, off, err = UnpackRR(msg, off)\n\t\tif err != nil {\n\t\t\toff = len(msg)\n\t\t\tbreak\n\t\t}\n\t\t// If offset does not increase anymore, l is a lie\n\t\tif off1 == off {\n\t\t\tbreak\n\t\t}\n\t\tdst = append(dst, r)\n\t}\n\tif err != nil && off == len(msg) {\n\t\tdst = nil\n\t}\n\treturn dst, off, err\n}\n\n// Convert a MsgHdr to a string, with dig-like headers:\n//\n// ;; opcode: QUERY, status: NOERROR, id: 48404\n//\n// ;; flags: qr aa rd ra;\nfunc (h *MsgHdr) String() string {\n\tif h == nil {\n\t\treturn \"<nil> MsgHdr\"\n\t}\n\n\ts := \";; opcode: \" + OpcodeToString[h.Opcode]\n\ts += \", status: \" + RcodeToString[h.Rcode]\n\ts += \", id: \" + strconv.Itoa(int(h.Id)) + \"\\n\"\n\n\ts += \";; flags:\"\n\tif h.Response {\n\t\ts += \" qr\"\n\t}\n\tif h.Authoritative {\n\t\ts += \" aa\"\n\t}\n\tif h.Truncated {\n\t\ts += \" tc\"\n\t}\n\tif h.RecursionDesired {\n\t\ts += \" rd\"\n\t}\n\tif h.RecursionAvailable {\n\t\ts += \" ra\"\n\t}\n\tif h.Zero { // Hmm\n\t\ts += \" z\"\n\t}\n\tif h.AuthenticatedData {\n\t\ts += \" ad\"\n\t}\n\tif h.CheckingDisabled {\n\t\ts += \" cd\"\n\t}\n\n\ts += \";\"\n\treturn s\n}\n\n// Pack packs a Msg: it is converted to wire format.\n// If the dns.Compress is true the message will be in compressed wire format.\nfunc (dns *Msg) Pack() (msg []byte, err error) {\n\treturn dns.PackBuffer(nil)\n}\n\n// PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated.\nfunc (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {\n\t// If this message can't be compressed, avoid filling the\n\t// compression map and creating garbage.\n\tif dns.Compress && dns.isCompressible() {\n\t\tcompression := make(map[string]uint16) // Compression pointer mappings.\n\t\treturn dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true)\n\t}\n\n\treturn dns.packBufferWithCompressionMap(buf, compressionMap{}, false)\n}\n\n// packBufferWithCompressionMap packs a Msg, using the given buffer buf.\nfunc (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) {\n\tif dns.Rcode < 0 || dns.Rcode > 0xFFF {\n\t\treturn nil, ErrRcode\n\t}\n\n\t// Set extended rcode unconditionally if we have an opt, this will allow\n\t// resetting the extended rcode bits if they need to.\n\tif opt := dns.IsEdns0(); opt != nil {\n\t\topt.SetExtendedRcode(uint16(dns.Rcode))\n\t} else if dns.Rcode > 0xF {\n\t\t// If Rcode is an extended one and opt is nil, error out.\n\t\treturn nil, ErrExtendedRcode\n\t}\n\n\t// Convert convenient Msg into wire-like Header.\n\tvar dh Header\n\tdh.Id = dns.Id\n\tdh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF)\n\tif dns.Response {\n\t\tdh.Bits |= _QR\n\t}\n\tif dns.Authoritative {\n\t\tdh.Bits |= _AA\n\t}\n\tif dns.Truncated {\n\t\tdh.Bits |= _TC\n\t}\n\tif dns.RecursionDesired {\n\t\tdh.Bits |= _RD\n\t}\n\tif dns.RecursionAvailable {\n\t\tdh.Bits |= _RA\n\t}\n\tif dns.Zero {\n\t\tdh.Bits |= _Z\n\t}\n\tif dns.AuthenticatedData {\n\t\tdh.Bits |= _AD\n\t}\n\tif dns.CheckingDisabled {\n\t\tdh.Bits |= _CD\n\t}\n\n\tdh.Qdcount = uint16(len(dns.Question))\n\tdh.Ancount = uint16(len(dns.Answer))\n\tdh.Nscount = uint16(len(dns.Ns))\n\tdh.Arcount = uint16(len(dns.Extra))\n\n\t// We need the uncompressed length here, because we first pack it and then compress it.\n\tmsg = buf\n\tuncompressedLen := msgLenWithCompressionMap(dns, nil)\n\tif packLen := uncompressedLen + 1; len(msg) < packLen {\n\t\tmsg = make([]byte, packLen)\n\t}\n\n\t// Pack it in: header and then the pieces.\n\toff := 0\n\toff, err = dh.pack(msg, off, compression, compress)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, r := range dns.Question {\n\t\toff, err = r.pack(msg, off, compression, compress)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tfor _, r := range dns.Answer {\n\t\t_, off, err = packRR(r, msg, off, compression, compress)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tfor _, r := range dns.Ns {\n\t\t_, off, err = packRR(r, msg, off, compression, compress)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tfor _, r := range dns.Extra {\n\t\t_, off, err = packRR(r, msg, off, compression, compress)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn msg[:off], nil\n}\n\nfunc (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) {\n\t// If we are at the end of the message we should return *just* the\n\t// header. This can still be useful to the caller. 9.9.9.9 sends these\n\t// when responding with REFUSED for instance.\n\tif off == len(msg) {\n\t\t// reset sections before returning\n\t\tdns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil\n\t\treturn nil\n\t}\n\n\t// Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are\n\t// attacker controlled. This means we can't use them to pre-allocate\n\t// slices.\n\tdns.Question = nil\n\tfor i := 0; i < int(dh.Qdcount); i++ {\n\t\toff1 := off\n\t\tvar q Question\n\t\tq, off, err = unpackQuestion(msg, off)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!\n\t\t\tdh.Qdcount = uint16(i)\n\t\t\tbreak\n\t\t}\n\t\tdns.Question = append(dns.Question, q)\n\t}\n\n\tdns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off)\n\t// The header counts might have been wrong so we need to update it\n\tdh.Ancount = uint16(len(dns.Answer))\n\tif err == nil {\n\t\tdns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off)\n\t}\n\t// The header counts might have been wrong so we need to update it\n\tdh.Nscount = uint16(len(dns.Ns))\n\tif err == nil {\n\t\tdns.Extra, _, err = unpackRRslice(int(dh.Arcount), msg, off)\n\t}\n\t// The header counts might have been wrong so we need to update it\n\tdh.Arcount = uint16(len(dns.Extra))\n\n\t// Set extended Rcode\n\tif opt := dns.IsEdns0(); opt != nil {\n\t\tdns.Rcode |= opt.ExtendedRcode()\n\t}\n\n\t// TODO(miek) make this an error?\n\t// use PackOpt to let people tell how detailed the error reporting should be?\n\t// if off != len(msg) {\n\t//\t// println(\"dns: extra bytes in dns packet\", off, \"<\", len(msg))\n\t// }\n\treturn err\n}\n\n// Unpack unpacks a binary message to a Msg structure.\nfunc (dns *Msg) Unpack(msg []byte) (err error) {\n\tdh, off, err := unpackMsgHdr(msg, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdns.setHdr(dh)\n\treturn dns.unpack(dh, msg, off)\n}\n\n// Convert a complete message to a string with dig-like output.\nfunc (dns *Msg) String() string {\n\tif dns == nil {\n\t\treturn \"<nil> MsgHdr\"\n\t}\n\ts := dns.MsgHdr.String() + \" \"\n\tif dns.MsgHdr.Opcode == OpcodeUpdate {\n\t\ts += \"ZONE: \" + strconv.Itoa(len(dns.Question)) + \", \"\n\t\ts += \"PREREQ: \" + strconv.Itoa(len(dns.Answer)) + \", \"\n\t\ts += \"UPDATE: \" + strconv.Itoa(len(dns.Ns)) + \", \"\n\t\ts += \"ADDITIONAL: \" + strconv.Itoa(len(dns.Extra)) + \"\\n\"\n\t} else {\n\t\ts += \"QUERY: \" + strconv.Itoa(len(dns.Question)) + \", \"\n\t\ts += \"ANSWER: \" + strconv.Itoa(len(dns.Answer)) + \", \"\n\t\ts += \"AUTHORITY: \" + strconv.Itoa(len(dns.Ns)) + \", \"\n\t\ts += \"ADDITIONAL: \" + strconv.Itoa(len(dns.Extra)) + \"\\n\"\n\t}\n\topt := dns.IsEdns0()\n\tif opt != nil {\n\t\t// OPT PSEUDOSECTION\n\t\ts += opt.String() + \"\\n\"\n\t}\n\tif len(dns.Question) > 0 {\n\t\tif dns.MsgHdr.Opcode == OpcodeUpdate {\n\t\t\ts += \"\\n;; ZONE SECTION:\\n\"\n\t\t} else {\n\t\t\ts += \"\\n;; QUESTION SECTION:\\n\"\n\t\t}\n\t\tfor _, r := range dns.Question {\n\t\t\ts += r.String() + \"\\n\"\n\t\t}\n\t}\n\tif len(dns.Answer) > 0 {\n\t\tif dns.MsgHdr.Opcode == OpcodeUpdate {\n\t\t\ts += \"\\n;; PREREQUISITE SECTION:\\n\"\n\t\t} else {\n\t\t\ts += \"\\n;; ANSWER SECTION:\\n\"\n\t\t}\n\t\tfor _, r := range dns.Answer {\n\t\t\tif r != nil {\n\t\t\t\ts += r.String() + \"\\n\"\n\t\t\t}\n\t\t}\n\t}\n\tif len(dns.Ns) > 0 {\n\t\tif dns.MsgHdr.Opcode == OpcodeUpdate {\n\t\t\ts += \"\\n;; UPDATE SECTION:\\n\"\n\t\t} else {\n\t\t\ts += \"\\n;; AUTHORITY SECTION:\\n\"\n\t\t}\n\t\tfor _, r := range dns.Ns {\n\t\t\tif r != nil {\n\t\t\t\ts += r.String() + \"\\n\"\n\t\t\t}\n\t\t}\n\t}\n\tif len(dns.Extra) > 0 && (opt == nil || len(dns.Extra) > 1) {\n\t\ts += \"\\n;; ADDITIONAL SECTION:\\n\"\n\t\tfor _, r := range dns.Extra {\n\t\t\tif r != nil && r.Header().Rrtype != TypeOPT {\n\t\t\t\ts += r.String() + \"\\n\"\n\t\t\t}\n\t\t}\n\t}\n\treturn s\n}\n\n// isCompressible returns whether the msg may be compressible.\nfunc (dns *Msg) isCompressible() bool {\n\t// If we only have one question, there is nothing we can ever compress.\n\treturn len(dns.Question) > 1 || len(dns.Answer) > 0 ||\n\t\tlen(dns.Ns) > 0 || len(dns.Extra) > 0\n}\n\n// Len returns the message length when in (un)compressed wire format.\n// If dns.Compress is true compression it is taken into account. Len()\n// is provided to be a faster way to get the size of the resulting packet,\n// than packing it, measuring the size and discarding the buffer.\nfunc (dns *Msg) Len() int {\n\t// If this message can't be compressed, avoid filling the\n\t// compression map and creating garbage.\n\tif dns.Compress && dns.isCompressible() {\n\t\tcompression := make(map[string]struct{})\n\t\treturn msgLenWithCompressionMap(dns, compression)\n\t}\n\n\treturn msgLenWithCompressionMap(dns, nil)\n}\n\nfunc msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int {\n\tl := headerSize\n\n\tfor _, r := range dns.Question {\n\t\tl += r.len(l, compression)\n\t}\n\tfor _, r := range dns.Answer {\n\t\tif r != nil {\n\t\t\tl += r.len(l, compression)\n\t\t}\n\t}\n\tfor _, r := range dns.Ns {\n\t\tif r != nil {\n\t\t\tl += r.len(l, compression)\n\t\t}\n\t}\n\tfor _, r := range dns.Extra {\n\t\tif r != nil {\n\t\t\tl += r.len(l, compression)\n\t\t}\n\t}\n\n\treturn l\n}\n\nfunc domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int {\n\tif s == \"\" || s == \".\" {\n\t\treturn 1\n\t}\n\n\tescaped := strings.Contains(s, \"\\\\\")\n\n\tif compression != nil && (compress || off < maxCompressionOffset) {\n\t\t// compressionLenSearch will insert the entry into the compression\n\t\t// map if it doesn't contain it.\n\t\tif l, ok := compressionLenSearch(compression, s, off); ok && compress {\n\t\t\tif escaped {\n\t\t\t\treturn escapedNameLen(s[:l]) + 2\n\t\t\t}\n\n\t\t\treturn l + 2\n\t\t}\n\t}\n\n\tif escaped {\n\t\treturn escapedNameLen(s) + 1\n\t}\n\n\treturn len(s) + 1\n}\n\nfunc escapedNameLen(s string) int {\n\tnameLen := len(s)\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] != '\\\\' {\n\t\t\tcontinue\n\t\t}\n\n\t\tif isDDD(s[i+1:]) {\n\t\t\tnameLen -= 3\n\t\t\ti += 3\n\t\t} else {\n\t\t\tnameLen--\n\t\t\ti++\n\t\t}\n\t}\n\n\treturn nameLen\n}\n\nfunc compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) {\n\tfor off, end := 0, false; !end; off, end = NextLabel(s, off) {\n\t\tif _, ok := c[s[off:]]; ok {\n\t\t\treturn off, true\n\t\t}\n\n\t\tif msgOff+off < maxCompressionOffset {\n\t\t\tc[s[off:]] = struct{}{}\n\t\t}\n\t}\n\n\treturn 0, false\n}\n\n// Copy returns a new RR which is a deep-copy of r.\nfunc Copy(r RR) RR { return r.copy() }\n\n// Len returns the length (in octets) of the uncompressed RR in wire format.\nfunc Len(r RR) int { return r.len(0, nil) }\n\n// Copy returns a new *Msg which is a deep-copy of dns.\nfunc (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) }\n\n// CopyTo copies the contents to the provided message using a deep-copy and returns the copy.\nfunc (dns *Msg) CopyTo(r1 *Msg) *Msg {\n\tr1.MsgHdr = dns.MsgHdr\n\tr1.Compress = dns.Compress\n\n\tif len(dns.Question) > 0 {\n\t\t// TODO(miek): Question is an immutable value, ok to do a shallow-copy\n\t\tr1.Question = cloneSlice(dns.Question)\n\t}\n\n\trrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra))\n\tr1.Answer, rrArr = rrArr[:0:len(dns.Answer)], rrArr[len(dns.Answer):]\n\tr1.Ns, rrArr = rrArr[:0:len(dns.Ns)], rrArr[len(dns.Ns):]\n\tr1.Extra = rrArr[:0:len(dns.Extra)]\n\n\tfor _, r := range dns.Answer {\n\t\tr1.Answer = append(r1.Answer, r.copy())\n\t}\n\n\tfor _, r := range dns.Ns {\n\t\tr1.Ns = append(r1.Ns, r.copy())\n\t}\n\n\tfor _, r := range dns.Extra {\n\t\tr1.Extra = append(r1.Extra, r.copy())\n\t}\n\n\treturn r1\n}\n\nfunc (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {\n\toff, err := packDomainName(q.Name, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(q.Qtype, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(q.Qclass, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc unpackQuestion(msg []byte, off int) (Question, int, error) {\n\tvar (\n\t\tq   Question\n\t\terr error\n\t)\n\tq.Name, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn q, off, fmt.Errorf(\"bad question name: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn q, off, nil\n\t}\n\tq.Qtype, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn q, off, fmt.Errorf(\"bad question qtype: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn q, off, nil\n\t}\n\tq.Qclass, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn q, off, fmt.Errorf(\"bad question qclass: %w\", err)\n\t}\n\n\tif off == len(msg) {\n\t\treturn q, off, nil\n\t}\n\n\treturn q, off, nil\n}\n\nfunc (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {\n\toff, err := packUint16(dh.Id, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(dh.Bits, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(dh.Qdcount, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(dh.Ancount, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(dh.Nscount, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(dh.Arcount, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc unpackMsgHdr(msg []byte, off int) (Header, int, error) {\n\tvar (\n\t\tdh  Header\n\t\terr error\n\t)\n\tdh.Id, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn dh, off, fmt.Errorf(\"bad header id: %w\", err)\n\t}\n\tdh.Bits, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn dh, off, fmt.Errorf(\"bad header bits: %w\", err)\n\t}\n\tdh.Qdcount, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn dh, off, fmt.Errorf(\"bad header question count: %w\", err)\n\t}\n\tdh.Ancount, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn dh, off, fmt.Errorf(\"bad header answer count: %w\", err)\n\t}\n\tdh.Nscount, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn dh, off, fmt.Errorf(\"bad header ns count: %w\", err)\n\t}\n\tdh.Arcount, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn dh, off, fmt.Errorf(\"bad header extra count: %w\", err)\n\t}\n\treturn dh, off, nil\n}\n\n// setHdr set the header in the dns using the binary data in dh.\nfunc (dns *Msg) setHdr(dh Header) {\n\tdns.Id = dh.Id\n\tdns.Response = dh.Bits&_QR != 0\n\tdns.Opcode = int(dh.Bits>>11) & 0xF\n\tdns.Authoritative = dh.Bits&_AA != 0\n\tdns.Truncated = dh.Bits&_TC != 0\n\tdns.RecursionDesired = dh.Bits&_RD != 0\n\tdns.RecursionAvailable = dh.Bits&_RA != 0\n\tdns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite.\n\tdns.AuthenticatedData = dh.Bits&_AD != 0\n\tdns.CheckingDisabled = dh.Bits&_CD != 0\n\tdns.Rcode = int(dh.Bits & 0xF)\n}\n"
  },
  {
    "path": "msg_generate.go",
    "content": "//go:build ignore\n// +build ignore\n\n// msg_generate.go is meant to run with go generate. It will use\n// go/{importer,types} to track down all the RR struct types. Then for each type\n// it will generate pack/unpack methods based on the struct tags. The generated source is\n// written to zmsg.go, and is meant to be checked into git.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nvar packageHdr = `\n// Code generated by \"go run msg_generate.go\"; DO NOT EDIT.\n\npackage dns\n\nimport \"fmt\"\n`\n\n// getTypeStruct will take a type and the package scope, and return the\n// (innermost) struct if the type is considered a RR type (currently defined as\n// those structs beginning with a RR_Header, could be redefined as implementing\n// the RR interface). The bool return value indicates if embedded structs were\n// resolved.\nfunc getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {\n\tst, ok := t.Underlying().(*types.Struct)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tif st.NumFields() == 0 {\n\t\treturn nil, false\n\t}\n\tif st.Field(0).Type() == scope.Lookup(\"RR_Header\").Type() {\n\t\treturn st, false\n\t}\n\tif st.Field(0).Anonymous() {\n\t\tst, _ := getTypeStruct(st.Field(0).Type(), scope)\n\t\treturn st, true\n\t}\n\treturn nil, false\n}\n\n// loadModule retrieves package description for a given module.\nfunc loadModule(name string) (*types.Package, error) {\n\tconf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo}\n\tpkgs, err := packages.Load(&conf, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pkgs[0].Types, nil\n}\n\nfunc main() {\n\t// Import and type-check the package\n\tpkg, err := loadModule(\"github.com/miekg/dns\")\n\tfatalIfErr(err)\n\tscope := pkg.Scope()\n\n\t// Collect actual types (*X)\n\tvar namedTypes []string\n\tfor _, name := range scope.Names() {\n\t\to := scope.Lookup(name)\n\t\tif o == nil || !o.Exported() {\n\t\t\tcontinue\n\t\t}\n\t\tif st, _ := getTypeStruct(o.Type(), scope); st == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif name == \"PrivateRR\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check if corresponding TypeX exists\n\t\tif scope.Lookup(\"Type\"+o.Name()) == nil && o.Name() != \"RFC3597\" {\n\t\t\tlog.Fatalf(\"Constant Type%s does not exist.\", o.Name())\n\t\t}\n\n\t\tnamedTypes = append(namedTypes, o.Name())\n\t}\n\n\tb := &bytes.Buffer{}\n\tb.WriteString(packageHdr)\n\n\tfmt.Fprint(b, \"// pack*() functions\\n\\n\")\n\tfor _, name := range namedTypes {\n\t\to := scope.Lookup(name)\n\t\tst, _ := getTypeStruct(o.Type(), scope)\n\n\t\tfmt.Fprintf(b, \"func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\\n\", name)\n\t\tfor i := 1; i < st.NumFields(); i++ {\n\t\t\to := func(s string) {\n\t\t\t\tfmt.Fprintf(b, s, st.Field(i).Name())\n\t\t\t\tfmt.Fprint(b, `if err != nil {\nreturn off, err\n}\n`)\n\t\t\t}\n\n\t\t\tif _, ok := st.Field(i).Type().(*types.Slice); ok {\n\t\t\t\tswitch st.Tag(i) {\n\t\t\t\tcase `dns:\"-\"`: // ignored\n\t\t\t\tcase `dns:\"txt\"`:\n\t\t\t\t\to(\"off, err = packStringTxt(rr.%s, msg, off)\\n\")\n\t\t\t\tcase `dns:\"opt\"`:\n\t\t\t\t\to(\"off, err = packDataOpt(rr.%s, msg, off)\\n\")\n\t\t\t\tcase `dns:\"nsec\"`:\n\t\t\t\t\to(\"off, err = packDataNsec(rr.%s, msg, off)\\n\")\n\t\t\t\tcase `dns:\"pairs\"`:\n\t\t\t\t\to(\"off, err = packDataSVCB(rr.%s, msg, off)\\n\")\n\t\t\t\tcase `dns:\"domain-name\"`:\n\t\t\t\t\to(\"off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\\n\")\n\t\t\t\tcase `dns:\"apl\"`:\n\t\t\t\t\to(\"off, err = packDataApl(rr.%s, msg, off)\\n\")\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch {\n\t\t\tcase st.Tag(i) == `dns:\"-\"`: // ignored\n\t\t\tcase st.Tag(i) == `dns:\"cdomain-name\"`:\n\t\t\t\to(\"off, err = packDomainName(rr.%s, msg, off, compression, compress)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"domain-name\"`:\n\t\t\t\to(\"off, err = packDomainName(rr.%s, msg, off, compression, false)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"a\"`:\n\t\t\t\to(\"off, err = packDataA(rr.%s, msg, off)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"aaaa\"`:\n\t\t\t\to(\"off, err = packDataAAAA(rr.%s, msg, off)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"uint48\"`:\n\t\t\t\to(\"off, err = packUint48(rr.%s, msg, off)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"txt\"`:\n\t\t\t\to(\"off, err = packString(rr.%s, msg, off)\\n\")\n\n\t\t\tcase strings.HasPrefix(st.Tag(i), `dns:\"size-base32`): // size-base32 can be packed just like base32\n\t\t\t\tfallthrough\n\t\t\tcase st.Tag(i) == `dns:\"base32\"`:\n\t\t\t\to(\"off, err = packStringBase32(rr.%s, msg, off)\\n\")\n\n\t\t\tcase strings.HasPrefix(st.Tag(i), `dns:\"size-base64`): // size-base64 can be packed just like base64\n\t\t\t\tfallthrough\n\t\t\tcase st.Tag(i) == `dns:\"base64\"`:\n\t\t\t\to(\"off, err = packStringBase64(rr.%s, msg, off)\\n\")\n\n\t\t\tcase strings.HasPrefix(st.Tag(i), `dns:\"size-hex:SaltLength`):\n\t\t\t\t// directly write instead of using o() so we get the error check in the correct place\n\t\t\t\tfield := st.Field(i).Name()\n\t\t\t\tfmt.Fprintf(b, `// Only pack salt if value is not \"-\", i.e. empty\nif rr.%s != \"-\" {\n  off, err = packStringHex(rr.%s, msg, off)\n  if err != nil {\n    return off, err\n  }\n}\n`, field, field)\n\t\t\t\tcontinue\n\t\t\tcase strings.HasPrefix(st.Tag(i), `dns:\"size-hex`): // size-hex can be packed just like hex\n\t\t\t\tfallthrough\n\t\t\tcase st.Tag(i) == `dns:\"hex\"`:\n\t\t\t\to(\"off, err = packStringHex(rr.%s, msg, off)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"any\"`:\n\t\t\t\to(\"off, err = packStringAny(rr.%s, msg, off)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"octet\"`:\n\t\t\t\to(\"off, err = packStringOctet(rr.%s, msg, off)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"ipsechost\"` || st.Tag(i) == `dns:\"amtrelayhost\"`:\n\t\t\t\to(\"off, err = packIPSECGateway(rr.GatewayAddr, rr.%s, msg, off, rr.GatewayType, compression, false)\\n\")\n\t\t\tcase st.Tag(i) == \"\":\n\t\t\t\tswitch st.Field(i).Type().(*types.Basic).Kind() {\n\t\t\t\tcase types.Uint8:\n\t\t\t\t\to(\"off, err = packUint8(rr.%s, msg, off)\\n\")\n\t\t\t\tcase types.Uint16:\n\t\t\t\t\to(\"off, err = packUint16(rr.%s, msg, off)\\n\")\n\t\t\t\tcase types.Uint32:\n\t\t\t\t\to(\"off, err = packUint32(rr.%s, msg, off)\\n\")\n\t\t\t\tcase types.Uint64:\n\t\t\t\t\to(\"off, err = packUint64(rr.%s, msg, off)\\n\")\n\t\t\t\tcase types.String:\n\t\t\t\t\to(\"off, err = packString(rr.%s, msg, off)\\n\")\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name())\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintln(b, \"return off, nil }\\n\")\n\t}\n\n\tfmt.Fprint(b, \"// unpack*() functions\\n\\n\")\n\tfor _, name := range namedTypes {\n\t\to := scope.Lookup(name)\n\t\tst, _ := getTypeStruct(o.Type(), scope)\n\n\t\tfmt.Fprintf(b, \"func (rr *%s) unpack(msg []byte, off int) (off1 int, err error) {\\n\", name)\n\t\tfmt.Fprint(b, `rdStart := off\n_ = rdStart\n\n`)\n\t\tfor i := 1; i < st.NumFields(); i++ {\n\t\t\to := func(s string) {\n\t\t\t\tfmt.Fprintf(b, s, st.Field(i).Name())\n\n\t\t\t\terr_name := name\n\t\t\t\tif name != st.Field(i).Name() {\n\t\t\t\t\terr_name += \".\" + st.Field(i).Name()\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(b, `if err != nil {\nreturn off, fmt.Errorf(\"%s: %%w\", err)\n}\n`, err_name)\n\t\t\t}\n\n\t\t\t// size-* are special, because they reference a struct member we should use for the length.\n\t\t\tif strings.HasPrefix(st.Tag(i), `dns:\"size-`) {\n\t\t\t\tstructMember := structMember(st.Tag(i))\n\t\t\t\tstructTag := structTag(st.Tag(i))\n\t\t\t\tswitch structTag {\n\t\t\t\tcase \"hex\":\n\t\t\t\t\tfmt.Fprintf(b, \"rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\\n\", st.Field(i).Name(), structMember)\n\t\t\t\tcase \"base32\":\n\t\t\t\t\tfmt.Fprintf(b, \"rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\\n\", st.Field(i).Name(), structMember)\n\t\t\t\tcase \"base64\":\n\t\t\t\t\tfmt.Fprintf(b, \"rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\\n\", st.Field(i).Name(), structMember)\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t\t}\n\t\t\t\tfmt.Fprint(b, `if err != nil {\nreturn off, err\n}\n`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif _, ok := st.Field(i).Type().(*types.Slice); ok {\n\t\t\t\tswitch st.Tag(i) {\n\t\t\t\tcase `dns:\"-\"`: // ignored\n\t\t\t\tcase `dns:\"txt\"`:\n\t\t\t\t\to(\"rr.%s, off, err = unpackStringTxt(msg, off)\\n\")\n\t\t\t\tcase `dns:\"opt\"`:\n\t\t\t\t\to(\"rr.%s, off, err = unpackDataOpt(msg, off)\\n\")\n\t\t\t\tcase `dns:\"nsec\"`:\n\t\t\t\t\to(\"rr.%s, off, err = unpackDataNsec(msg, off)\\n\")\n\t\t\t\tcase `dns:\"pairs\"`:\n\t\t\t\t\to(\"rr.%s, off, err = unpackDataSVCB(msg, off)\\n\")\n\t\t\t\tcase `dns:\"domain-name\"`:\n\t\t\t\t\to(\"rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\\n\")\n\t\t\t\tcase `dns:\"apl\"`:\n\t\t\t\t\to(\"rr.%s, off, err = unpackDataApl(msg, off)\\n\")\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch st.Tag(i) {\n\t\t\tcase `dns:\"-\"`: // ignored\n\t\t\tcase `dns:\"cdomain-name\"`:\n\t\t\t\tfallthrough\n\t\t\tcase `dns:\"domain-name\"`:\n\t\t\t\to(\"rr.%s, off, err = UnpackDomainName(msg, off)\\n\")\n\t\t\tcase `dns:\"a\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackDataA(msg, off)\\n\")\n\t\t\tcase `dns:\"aaaa\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackDataAAAA(msg, off)\\n\")\n\t\t\tcase `dns:\"uint48\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackUint48(msg, off)\\n\")\n\t\t\tcase `dns:\"txt\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackString(msg, off)\\n\")\n\t\t\tcase `dns:\"base32\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\\n\")\n\t\t\tcase `dns:\"base64\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\\n\")\n\t\t\tcase `dns:\"hex\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\\n\")\n\t\t\tcase `dns:\"any\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackStringAny(msg, off, rdStart + int(rr.Hdr.Rdlength))\\n\")\n\t\t\tcase `dns:\"octet\"`:\n\t\t\t\to(\"rr.%s, off, err = unpackStringOctet(msg, off)\\n\")\n\t\t\tcase `dns:\"ipsechost\"`, `dns:\"amtrelayhost\"`:\n\t\t\t\to(\"rr.GatewayAddr, rr.%s, off, err = unpackIPSECGateway(msg, off, rr.GatewayType)\\n\")\n\t\t\tcase \"\":\n\t\t\t\tswitch st.Field(i).Type().(*types.Basic).Kind() {\n\t\t\t\tcase types.Uint8:\n\t\t\t\t\to(\"rr.%s, off, err = unpackUint8(msg, off)\\n\")\n\t\t\t\tcase types.Uint16:\n\t\t\t\t\to(\"rr.%s, off, err = unpackUint16(msg, off)\\n\")\n\t\t\t\tcase types.Uint32:\n\t\t\t\t\to(\"rr.%s, off, err = unpackUint32(msg, off)\\n\")\n\t\t\t\tcase types.Uint64:\n\t\t\t\t\to(\"rr.%s, off, err = unpackUint64(msg, off)\\n\")\n\t\t\t\tcase types.String:\n\t\t\t\t\to(\"rr.%s, off, err = unpackString(msg, off)\\n\")\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name())\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t}\n\t\t\t// If we've hit len(msg) we return without error.\n\t\t\tif i < st.NumFields()-1 {\n\t\t\t\tfmt.Fprint(b, `if off == len(msg) {\nreturn off, nil\n\t}\n`)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintf(b, \"return off, nil }\\n\\n\")\n\t}\n\n\t// gofmt\n\tres, err := format.Source(b.Bytes())\n\tif err != nil {\n\t\tb.WriteTo(os.Stderr)\n\t\tlog.Fatal(err)\n\t}\n\n\t// write result\n\tf, err := os.Create(\"zmsg.go\")\n\tfatalIfErr(err)\n\tdefer f.Close()\n\tf.Write(res)\n}\n\n// structMember will take a tag like dns:\"size-base32:SaltLength\" and return the last part of this string.\nfunc structMember(s string) string {\n\tidx := strings.LastIndex(s, \":\")\n\treturn strings.TrimSuffix(s[idx+1:], `\"`)\n}\n\n// structTag will take a tag like dns:\"size-base32:SaltLength\" and return base32.\nfunc structTag(s string) string {\n\ts = strings.TrimPrefix(s, `dns:\"size-`)\n\ts, _, _ = strings.Cut(s, \":\")\n\treturn s\n}\n\nfunc fatalIfErr(err error) {\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "msg_helpers.go",
    "content": "package dns\n\nimport (\n\t\"encoding/base32\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"net\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// helper functions called from the generated zmsg.go\n\n// These function are named after the tag to help pack/unpack, if there is no tag it is the name\n// of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or\n// packDataDomainName.\n\nfunc unpackDataA(msg []byte, off int) (net.IP, int, error) {\n\tif off+net.IPv4len > len(msg) {\n\t\treturn nil, len(msg), &Error{err: \"overflow unpacking a\"}\n\t}\n\treturn cloneSlice(msg[off : off+net.IPv4len]), off + net.IPv4len, nil\n}\n\nfunc packDataA(a net.IP, msg []byte, off int) (int, error) {\n\tswitch len(a) {\n\tcase net.IPv4len, net.IPv6len:\n\t\t// It must be a slice of 4, even if it is 16, we encode only the first 4\n\t\tif off+net.IPv4len > len(msg) {\n\t\t\treturn len(msg), &Error{err: \"overflow packing a\"}\n\t\t}\n\n\t\tcopy(msg[off:], a.To4())\n\t\toff += net.IPv4len\n\tcase 0:\n\t\t// Allowed, for dynamic updates.\n\tdefault:\n\t\treturn len(msg), &Error{err: \"overflow packing a\"}\n\t}\n\treturn off, nil\n}\n\nfunc unpackDataAAAA(msg []byte, off int) (net.IP, int, error) {\n\tif off+net.IPv6len > len(msg) {\n\t\treturn nil, len(msg), &Error{err: \"overflow unpacking aaaa\"}\n\t}\n\treturn cloneSlice(msg[off : off+net.IPv6len]), off + net.IPv6len, nil\n}\n\nfunc packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {\n\tswitch len(aaaa) {\n\tcase net.IPv6len:\n\t\tif off+net.IPv6len > len(msg) {\n\t\t\treturn len(msg), &Error{err: \"overflow packing aaaa\"}\n\t\t}\n\n\t\tcopy(msg[off:], aaaa)\n\t\toff += net.IPv6len\n\tcase 0:\n\t\t// Allowed, dynamic updates.\n\tdefault:\n\t\treturn len(msg), &Error{err: \"overflow packing aaaa\"}\n\t}\n\treturn off, nil\n}\n\n// unpackHeader unpacks an RR header, returning the offset to the end of the header and a\n// re-sliced msg according to the expected length of the RR.\nfunc unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) {\n\thdr := RR_Header{}\n\tif off == len(msg) {\n\t\treturn hdr, off, msg, nil\n\t}\n\n\thdr.Name, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn hdr, len(msg), msg, err\n\t}\n\thdr.Rrtype, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn hdr, len(msg), msg, err\n\t}\n\thdr.Class, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn hdr, len(msg), msg, err\n\t}\n\thdr.Ttl, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn hdr, len(msg), msg, err\n\t}\n\thdr.Rdlength, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn hdr, len(msg), msg, err\n\t}\n\tmsg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength)\n\treturn hdr, off, msg, err\n}\n\n// packHeader packs an RR header, returning the offset to the end of the header.\n// See PackDomainName for documentation about the compression.\nfunc (hdr RR_Header) packHeader(msg []byte, off int, compression compressionMap, compress bool) (int, error) {\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\n\toff, err := packDomainName(hdr.Name, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\toff, err = packUint16(hdr.Rrtype, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\toff, err = packUint16(hdr.Class, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\toff, err = packUint32(hdr.Ttl, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\toff, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR.\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\treturn off, nil\n}\n\n// helper helper functions.\n\n// truncateMsgFromRdLength truncates msg to match the expected length of the RR.\n// Returns an error if msg is smaller than the expected size.\nfunc truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) {\n\tlenrd := off + int(rdlength)\n\tif lenrd > len(msg) {\n\t\treturn msg, &Error{err: \"overflowing header size\"}\n\t}\n\treturn msg[:lenrd], nil\n}\n\nvar base32HexNoPadEncoding = base32.HexEncoding.WithPadding(base32.NoPadding)\n\nfunc fromBase32(s []byte) (buf []byte, err error) {\n\tfor i, b := range s {\n\t\tif b >= 'a' && b <= 'z' {\n\t\t\ts[i] = b - 32\n\t\t}\n\t}\n\tbuflen := base32HexNoPadEncoding.DecodedLen(len(s))\n\tbuf = make([]byte, buflen)\n\tn, err := base32HexNoPadEncoding.Decode(buf, s)\n\tbuf = buf[:n]\n\treturn\n}\n\nfunc toBase32(b []byte) string {\n\treturn base32HexNoPadEncoding.EncodeToString(b)\n}\n\nfunc fromBase64(s []byte) (buf []byte, err error) {\n\tbuflen := base64.StdEncoding.DecodedLen(len(s))\n\tbuf = make([]byte, buflen)\n\tn, err := base64.StdEncoding.Decode(buf, s)\n\tbuf = buf[:n]\n\treturn\n}\n\nfunc toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) }\n\n// dynamicUpdate returns true if the Rdlength is zero.\nfunc noRdata(h RR_Header) bool { return h.Rdlength == 0 }\n\nfunc unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) {\n\tif off+1 > len(msg) {\n\t\treturn 0, len(msg), &Error{err: \"overflow unpacking uint8\"}\n\t}\n\treturn msg[off], off + 1, nil\n}\n\nfunc packUint8(i uint8, msg []byte, off int) (off1 int, err error) {\n\tif off+1 > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing uint8\"}\n\t}\n\tmsg[off] = i\n\treturn off + 1, nil\n}\n\nfunc unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) {\n\tif off+2 > len(msg) {\n\t\treturn 0, len(msg), &Error{err: \"overflow unpacking uint16\"}\n\t}\n\treturn binary.BigEndian.Uint16(msg[off:]), off + 2, nil\n}\n\nfunc packUint16(i uint16, msg []byte, off int) (off1 int, err error) {\n\tif off+2 > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing uint16\"}\n\t}\n\tbinary.BigEndian.PutUint16(msg[off:], i)\n\treturn off + 2, nil\n}\n\nfunc unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) {\n\tif off+4 > len(msg) {\n\t\treturn 0, len(msg), &Error{err: \"overflow unpacking uint32\"}\n\t}\n\treturn binary.BigEndian.Uint32(msg[off:]), off + 4, nil\n}\n\nfunc packUint32(i uint32, msg []byte, off int) (off1 int, err error) {\n\tif off+4 > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing uint32\"}\n\t}\n\tbinary.BigEndian.PutUint32(msg[off:], i)\n\treturn off + 4, nil\n}\n\nfunc unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) {\n\tif off+6 > len(msg) {\n\t\treturn 0, len(msg), &Error{err: \"overflow unpacking uint64 as uint48\"}\n\t}\n\t// Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)\n\ti = uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 |\n\t\tuint64(msg[off+4])<<8 | uint64(msg[off+5])\n\toff += 6\n\treturn i, off, nil\n}\n\nfunc packUint48(i uint64, msg []byte, off int) (off1 int, err error) {\n\tif off+6 > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing uint64 as uint48\"}\n\t}\n\tmsg[off] = byte(i >> 40)\n\tmsg[off+1] = byte(i >> 32)\n\tmsg[off+2] = byte(i >> 24)\n\tmsg[off+3] = byte(i >> 16)\n\tmsg[off+4] = byte(i >> 8)\n\tmsg[off+5] = byte(i)\n\toff += 6\n\treturn off, nil\n}\n\nfunc unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) {\n\tif off+8 > len(msg) {\n\t\treturn 0, len(msg), &Error{err: \"overflow unpacking uint64\"}\n\t}\n\treturn binary.BigEndian.Uint64(msg[off:]), off + 8, nil\n}\n\nfunc packUint64(i uint64, msg []byte, off int) (off1 int, err error) {\n\tif off+8 > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing uint64\"}\n\t}\n\tbinary.BigEndian.PutUint64(msg[off:], i)\n\toff += 8\n\treturn off, nil\n}\n\nfunc unpackString(msg []byte, off int) (string, int, error) {\n\tif off+1 > len(msg) {\n\t\treturn \"\", off, &Error{err: \"overflow unpacking txt\"}\n\t}\n\tl := int(msg[off])\n\toff++\n\tif off+l > len(msg) {\n\t\treturn \"\", off, &Error{err: \"overflow unpacking txt\"}\n\t}\n\tvar s strings.Builder\n\tconsumed := 0\n\tfor i, b := range msg[off : off+l] {\n\t\tswitch {\n\t\tcase b == '\"' || b == '\\\\':\n\t\t\tif consumed == 0 {\n\t\t\t\ts.Grow(l * 2)\n\t\t\t}\n\t\t\ts.Write(msg[off+consumed : off+i])\n\t\t\ts.WriteByte('\\\\')\n\t\t\ts.WriteByte(b)\n\t\t\tconsumed = i + 1\n\t\tcase b < ' ' || b > '~': // unprintable\n\t\t\tif consumed == 0 {\n\t\t\t\ts.Grow(l * 2)\n\t\t\t}\n\t\t\ts.Write(msg[off+consumed : off+i])\n\t\t\ts.WriteString(escapeByte(b))\n\t\t\tconsumed = i + 1\n\t\t}\n\t}\n\tif consumed == 0 { // no escaping needed\n\t\treturn string(msg[off : off+l]), off + l, nil\n\t}\n\ts.Write(msg[off+consumed : off+l])\n\treturn s.String(), off + l, nil\n}\n\nfunc packString(s string, msg []byte, off int) (int, error) {\n\toff, err := packTxtString(s, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\treturn off, nil\n}\n\nfunc unpackStringBase32(msg []byte, off, end int) (string, int, error) {\n\tif end > len(msg) {\n\t\treturn \"\", len(msg), &Error{err: \"overflow unpacking base32\"}\n\t}\n\ts := toBase32(msg[off:end])\n\treturn s, end, nil\n}\n\nfunc packStringBase32(s string, msg []byte, off int) (int, error) {\n\tb32, err := fromBase32([]byte(s))\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\tif off+len(b32) > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing base32\"}\n\t}\n\tcopy(msg[off:off+len(b32)], b32)\n\toff += len(b32)\n\treturn off, nil\n}\n\nfunc unpackStringBase64(msg []byte, off, end int) (string, int, error) {\n\t// Rest of the RR is base64 encoded value, so we don't need an explicit length\n\t// to be set. Thus far all RR's that have base64 encoded fields have those as their\n\t// last one. What we do need is the end of the RR!\n\tif end > len(msg) {\n\t\treturn \"\", len(msg), &Error{err: \"overflow unpacking base64\"}\n\t}\n\ts := toBase64(msg[off:end])\n\treturn s, end, nil\n}\n\nfunc packStringBase64(s string, msg []byte, off int) (int, error) {\n\tb64, err := fromBase64([]byte(s))\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\tif off+len(b64) > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing base64\"}\n\t}\n\tcopy(msg[off:off+len(b64)], b64)\n\toff += len(b64)\n\treturn off, nil\n}\n\nfunc unpackStringHex(msg []byte, off, end int) (string, int, error) {\n\t// Rest of the RR is hex encoded value, so we don't need an explicit length\n\t// to be set. NSEC and TSIG have hex fields with a length field.\n\t// What we do need is the end of the RR!\n\tif end > len(msg) {\n\t\treturn \"\", len(msg), &Error{err: \"overflow unpacking hex\"}\n\t}\n\n\ts := hex.EncodeToString(msg[off:end])\n\treturn s, end, nil\n}\n\nfunc packStringHex(s string, msg []byte, off int) (int, error) {\n\th, err := hex.DecodeString(s)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\tif off+len(h) > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing hex\"}\n\t}\n\tcopy(msg[off:off+len(h)], h)\n\toff += len(h)\n\treturn off, nil\n}\n\nfunc unpackStringAny(msg []byte, off, end int) (string, int, error) {\n\tif end > len(msg) {\n\t\treturn \"\", len(msg), &Error{err: \"overflow unpacking anything\"}\n\t}\n\treturn string(msg[off:end]), end, nil\n}\n\nfunc packStringAny(s string, msg []byte, off int) (int, error) {\n\tif off+len(s) > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing anything\"}\n\t}\n\tcopy(msg[off:off+len(s)], s)\n\toff += len(s)\n\treturn off, nil\n}\n\nfunc unpackStringTxt(msg []byte, off int) ([]string, int, error) {\n\ttxt, off, err := unpackTxt(msg, off)\n\tif err != nil {\n\t\treturn nil, len(msg), err\n\t}\n\treturn txt, off, nil\n}\n\nfunc packStringTxt(s []string, msg []byte, off int) (int, error) {\n\toff, err := packTxt(s, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\treturn off, nil\n}\n\nfunc unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {\n\tvar edns []EDNS0\n\tfor off < len(msg) {\n\t\tif off+4 > len(msg) {\n\t\t\treturn nil, len(msg), &Error{err: \"overflow unpacking opt\"}\n\t\t}\n\t\tcode := binary.BigEndian.Uint16(msg[off:])\n\t\toff += 2\n\t\toptlen := binary.BigEndian.Uint16(msg[off:])\n\t\toff += 2\n\t\tif off+int(optlen) > len(msg) {\n\t\t\treturn nil, len(msg), &Error{err: \"overflow unpacking opt\"}\n\t\t}\n\t\topt := makeDataOpt(code)\n\t\tif err := opt.unpack(msg[off : off+int(optlen)]); err != nil {\n\t\t\treturn nil, len(msg), err\n\t\t}\n\t\tedns = append(edns, opt)\n\t\toff += int(optlen)\n\t}\n\treturn edns, off, nil\n}\n\nfunc packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {\n\tfor _, el := range options {\n\t\tb, err := el.pack()\n\t\tif err != nil || off+4 > len(msg) {\n\t\t\treturn len(msg), &Error{err: \"overflow packing opt\"}\n\t\t}\n\t\tbinary.BigEndian.PutUint16(msg[off:], el.Option())      // Option code\n\t\tbinary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length\n\t\toff += 4\n\t\tif off+len(b) > len(msg) {\n\t\t\treturn len(msg), &Error{err: \"overflow packing opt\"}\n\t\t}\n\t\t// Actual data\n\t\tcopy(msg[off:off+len(b)], b)\n\t\toff += len(b)\n\t}\n\treturn off, nil\n}\n\nfunc unpackStringOctet(msg []byte, off int) (string, int, error) {\n\ts := string(msg[off:])\n\treturn s, len(msg), nil\n}\n\nfunc packStringOctet(s string, msg []byte, off int) (int, error) {\n\toff, err := packOctetString(s, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\treturn off, nil\n}\n\nfunc unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {\n\tvar nsec []uint16\n\tlength, window, lastwindow := 0, 0, -1\n\tfor off < len(msg) {\n\t\tif off+2 > len(msg) {\n\t\t\treturn nsec, len(msg), &Error{err: \"overflow unpacking NSEC(3)\"}\n\t\t}\n\t\twindow = int(msg[off])\n\t\tlength = int(msg[off+1])\n\t\toff += 2\n\t\tif window <= lastwindow {\n\t\t\t// RFC 4034: Blocks are present in the NSEC RR RDATA in\n\t\t\t// increasing numerical order.\n\t\t\treturn nsec, len(msg), &Error{err: \"out of order NSEC(3) block in type bitmap\"}\n\t\t}\n\t\tif length == 0 {\n\t\t\t// RFC 4034: Blocks with no types present MUST NOT be included.\n\t\t\treturn nsec, len(msg), &Error{err: \"empty NSEC(3) block in type bitmap\"}\n\t\t}\n\t\tif length > 32 {\n\t\t\treturn nsec, len(msg), &Error{err: \"NSEC(3) block too long in type bitmap\"}\n\t\t}\n\t\tif off+length > len(msg) {\n\t\t\treturn nsec, len(msg), &Error{err: \"overflowing NSEC(3) block in type bitmap\"}\n\t\t}\n\n\t\t// Walk the bytes in the window and extract the type bits\n\t\tfor j, b := range msg[off : off+length] {\n\t\t\t// Check the bits one by one, and set the type\n\t\t\tif b&0x80 == 0x80 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+0))\n\t\t\t}\n\t\t\tif b&0x40 == 0x40 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+1))\n\t\t\t}\n\t\t\tif b&0x20 == 0x20 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+2))\n\t\t\t}\n\t\t\tif b&0x10 == 0x10 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+3))\n\t\t\t}\n\t\t\tif b&0x8 == 0x8 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+4))\n\t\t\t}\n\t\t\tif b&0x4 == 0x4 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+5))\n\t\t\t}\n\t\t\tif b&0x2 == 0x2 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+6))\n\t\t\t}\n\t\t\tif b&0x1 == 0x1 {\n\t\t\t\tnsec = append(nsec, uint16(window*256+j*8+7))\n\t\t\t}\n\t\t}\n\t\toff += length\n\t\tlastwindow = window\n\t}\n\treturn nsec, off, nil\n}\n\n// typeBitMapLen is a helper function which computes the \"maximum\" length of\n// a the NSEC Type BitMap field.\nfunc typeBitMapLen(bitmap []uint16) int {\n\tvar l int\n\tvar lastwindow, lastlength uint16\n\tfor _, t := range bitmap {\n\t\twindow := t / 256\n\t\tlength := (t-window*256)/8 + 1\n\t\tif window > lastwindow && lastlength != 0 { // New window, jump to the new offset\n\t\t\tl += int(lastlength) + 2\n\t\t\tlastlength = 0\n\t\t}\n\t\tif window < lastwindow || length < lastlength {\n\t\t\t// packDataNsec would return Error{err: \"nsec bits out of order\"} here, but\n\t\t\t// when computing the length, we want do be liberal.\n\t\t\tcontinue\n\t\t}\n\t\tlastwindow, lastlength = window, length\n\t}\n\tl += int(lastlength) + 2\n\treturn l\n}\n\nfunc packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {\n\tif len(bitmap) == 0 {\n\t\treturn off, nil\n\t}\n\tif off > len(msg) {\n\t\treturn off, &Error{err: \"overflow packing nsec\"}\n\t}\n\ttoZero := msg[off:]\n\tif maxLen := typeBitMapLen(bitmap); maxLen < len(toZero) {\n\t\ttoZero = toZero[:maxLen]\n\t}\n\tfor i := range toZero {\n\t\ttoZero[i] = 0\n\t}\n\tvar lastwindow, lastlength uint16\n\tfor _, t := range bitmap {\n\t\twindow := t / 256\n\t\tlength := (t-window*256)/8 + 1\n\t\tif window > lastwindow && lastlength != 0 { // New window, jump to the new offset\n\t\t\toff += int(lastlength) + 2\n\t\t\tlastlength = 0\n\t\t}\n\t\tif window < lastwindow || length < lastlength {\n\t\t\treturn len(msg), &Error{err: \"nsec bits out of order\"}\n\t\t}\n\t\tif off+2+int(length) > len(msg) {\n\t\t\treturn len(msg), &Error{err: \"overflow packing nsec\"}\n\t\t}\n\t\t// Setting the window #\n\t\tmsg[off] = byte(window)\n\t\t// Setting the octets length\n\t\tmsg[off+1] = byte(length)\n\t\t// Setting the bit value for the type in the right octet\n\t\tmsg[off+1+int(length)] |= byte(1 << (7 - t%8))\n\t\tlastwindow, lastlength = window, length\n\t}\n\toff += int(lastlength) + 2\n\treturn off, nil\n}\n\nfunc unpackDataSVCB(msg []byte, off int) ([]SVCBKeyValue, int, error) {\n\tvar xs []SVCBKeyValue\n\tvar code uint16\n\tvar length uint16\n\tvar err error\n\tfor off < len(msg) {\n\t\tcode, off, err = unpackUint16(msg, off)\n\t\tif err != nil {\n\t\t\treturn nil, len(msg), &Error{err: \"overflow unpacking SVCB\"}\n\t\t}\n\t\tlength, off, err = unpackUint16(msg, off)\n\t\tif err != nil || off+int(length) > len(msg) {\n\t\t\treturn nil, len(msg), &Error{err: \"overflow unpacking SVCB\"}\n\t\t}\n\t\te := makeSVCBKeyValue(SVCBKey(code))\n\t\tif e == nil {\n\t\t\treturn nil, len(msg), &Error{err: \"bad SVCB key\"}\n\t\t}\n\t\tif err := e.unpack(msg[off : off+int(length)]); err != nil {\n\t\t\treturn nil, len(msg), err\n\t\t}\n\t\tif len(xs) > 0 && e.Key() <= xs[len(xs)-1].Key() {\n\t\t\treturn nil, len(msg), &Error{err: \"SVCB keys not in strictly increasing order\"}\n\t\t}\n\t\txs = append(xs, e)\n\t\toff += int(length)\n\t}\n\treturn xs, off, nil\n}\n\nfunc packDataSVCB(pairs []SVCBKeyValue, msg []byte, off int) (int, error) {\n\tpairs = cloneSlice(pairs)\n\tsort.Slice(pairs, func(i, j int) bool {\n\t\treturn pairs[i].Key() < pairs[j].Key()\n\t})\n\tprev := svcb_RESERVED\n\tfor _, el := range pairs {\n\t\tif el.Key() == prev {\n\t\t\treturn len(msg), &Error{err: \"repeated SVCB keys are not allowed\"}\n\t\t}\n\t\tprev = el.Key()\n\t\tpacked, err := el.pack()\n\t\tif err != nil {\n\t\t\treturn len(msg), err\n\t\t}\n\t\toff, err = packUint16(uint16(el.Key()), msg, off)\n\t\tif err != nil {\n\t\t\treturn len(msg), &Error{err: \"overflow packing SVCB\"}\n\t\t}\n\t\toff, err = packUint16(uint16(len(packed)), msg, off)\n\t\tif err != nil || off+len(packed) > len(msg) {\n\t\t\treturn len(msg), &Error{err: \"overflow packing SVCB\"}\n\t\t}\n\t\tcopy(msg[off:off+len(packed)], packed)\n\t\toff += len(packed)\n\t}\n\treturn off, nil\n}\n\nfunc unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {\n\tvar (\n\t\tservers []string\n\t\ts       string\n\t\terr     error\n\t)\n\tif end > len(msg) {\n\t\treturn nil, len(msg), &Error{err: \"overflow unpacking domain names\"}\n\t}\n\tfor off < end {\n\t\ts, off, err = UnpackDomainName(msg, off)\n\t\tif err != nil {\n\t\t\treturn servers, len(msg), err\n\t\t}\n\t\tservers = append(servers, s)\n\t}\n\treturn servers, off, nil\n}\n\nfunc packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) {\n\tvar err error\n\tfor _, name := range names {\n\t\toff, err = packDomainName(name, msg, off, compression, compress)\n\t\tif err != nil {\n\t\t\treturn len(msg), err\n\t\t}\n\t}\n\treturn off, nil\n}\n\nfunc packDataApl(data []APLPrefix, msg []byte, off int) (int, error) {\n\tvar err error\n\tfor i := range data {\n\t\toff, err = packDataAplPrefix(&data[i], msg, off)\n\t\tif err != nil {\n\t\t\treturn len(msg), err\n\t\t}\n\t}\n\treturn off, nil\n}\n\nfunc packDataAplPrefix(p *APLPrefix, msg []byte, off int) (int, error) {\n\tif len(p.Network.IP) != len(p.Network.Mask) {\n\t\treturn len(msg), &Error{err: \"address and mask lengths don't match\"}\n\t}\n\n\tvar err error\n\tprefix, _ := p.Network.Mask.Size()\n\taddr := p.Network.IP.Mask(p.Network.Mask)[:(prefix+7)/8]\n\n\tswitch len(p.Network.IP) {\n\tcase net.IPv4len:\n\t\toff, err = packUint16(1, msg, off)\n\tcase net.IPv6len:\n\t\toff, err = packUint16(2, msg, off)\n\tdefault:\n\t\terr = &Error{err: \"unrecognized address family\"}\n\t}\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\n\toff, err = packUint8(uint8(prefix), msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\n\tvar n uint8\n\tif p.Negation {\n\t\tn = 0x80\n\t}\n\n\t// trim trailing zero bytes as specified in RFC3123 Sections 4.1 and 4.2.\n\ti := len(addr) - 1\n\tfor ; i >= 0 && addr[i] == 0; i-- {\n\t}\n\taddr = addr[:i+1]\n\n\tadflen := uint8(len(addr)) & 0x7f\n\toff, err = packUint8(n|adflen, msg, off)\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\n\tif off+len(addr) > len(msg) {\n\t\treturn len(msg), &Error{err: \"overflow packing APL prefix\"}\n\t}\n\toff += copy(msg[off:], addr)\n\n\treturn off, nil\n}\n\nfunc unpackDataApl(msg []byte, off int) ([]APLPrefix, int, error) {\n\tvar result []APLPrefix\n\tfor off < len(msg) {\n\t\tprefix, end, err := unpackDataAplPrefix(msg, off)\n\t\tif err != nil {\n\t\t\treturn nil, len(msg), err\n\t\t}\n\t\toff = end\n\t\tresult = append(result, prefix)\n\t}\n\treturn result, off, nil\n}\n\nfunc unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) {\n\tfamily, off, err := unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"overflow unpacking APL prefix\"}\n\t}\n\tprefix, off, err := unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"overflow unpacking APL prefix\"}\n\t}\n\tnlen, off, err := unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"overflow unpacking APL prefix\"}\n\t}\n\n\tvar ip []byte\n\tswitch family {\n\tcase 1:\n\t\tip = make([]byte, net.IPv4len)\n\tcase 2:\n\t\tip = make([]byte, net.IPv6len)\n\tdefault:\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"unrecognized APL address family\"}\n\t}\n\tif int(prefix) > 8*len(ip) {\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"APL prefix too long\"}\n\t}\n\tafdlen := int(nlen & 0x7f)\n\tif afdlen > len(ip) {\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"APL length too long\"}\n\t}\n\tif off+afdlen > len(msg) {\n\t\treturn APLPrefix{}, len(msg), &Error{err: \"overflow unpacking APL address\"}\n\t}\n\n\t// Address MUST NOT contain trailing zero bytes per RFC3123 Sections 4.1 and 4.2.\n\toff += copy(ip, msg[off:off+afdlen])\n\tif afdlen > 0 {\n\t\tlast := ip[afdlen-1]\n\t\tif last == 0 {\n\t\t\treturn APLPrefix{}, len(msg), &Error{err: \"extra APL address bits\"}\n\t\t}\n\t}\n\tipnet := net.IPNet{\n\t\tIP:   ip,\n\t\tMask: net.CIDRMask(int(prefix), 8*len(ip)),\n\t}\n\n\treturn APLPrefix{\n\t\tNegation: (nlen & 0x80) != 0,\n\t\tNetwork:  ipnet,\n\t}, off, nil\n}\n\nfunc unpackIPSECGateway(msg []byte, off int, gatewayType uint8) (net.IP, string, int, error) {\n\tvar retAddr net.IP\n\tvar retString string\n\tvar err error\n\n\tswitch gatewayType {\n\tcase IPSECGatewayNone: // do nothing\n\tcase IPSECGatewayIPv4:\n\t\tretAddr, off, err = unpackDataA(msg, off)\n\tcase IPSECGatewayIPv6:\n\t\tretAddr, off, err = unpackDataAAAA(msg, off)\n\tcase IPSECGatewayHost:\n\t\tretString, off, err = UnpackDomainName(msg, off)\n\t}\n\n\treturn retAddr, retString, off, err\n}\n\nfunc packIPSECGateway(gatewayAddr net.IP, gatewayString string, msg []byte, off int, gatewayType uint8, compression compressionMap, compress bool) (int, error) {\n\tvar err error\n\n\tswitch gatewayType {\n\tcase IPSECGatewayNone: // do nothing\n\tcase IPSECGatewayIPv4:\n\t\toff, err = packDataA(gatewayAddr, msg, off)\n\tcase IPSECGatewayIPv6:\n\t\toff, err = packDataAAAA(gatewayAddr, msg, off)\n\tcase IPSECGatewayHost:\n\t\toff, err = packDomainName(gatewayString, msg, off, compression, compress)\n\t}\n\n\treturn off, err\n}\n"
  },
  {
    "path": "msg_helpers_test.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"testing\"\n)\n\n// TestPacketDataNsec tests generated using fuzz.go and with a message pack\n// containing the following bytes: 0000\\x00\\x00000000\\x00\\x002000000\\x0060000\\x00\\x130000000000000000000\"\n// That bytes sequence created the overflow error and further permutations of that sequence were able to trigger\n// the other code paths.\nfunc TestPackDataNsec(t *testing.T) {\n\ttype args struct {\n\t\tbitmap []uint16\n\t\tmsg    []byte\n\t\toff    int\n\t}\n\ttests := []struct {\n\t\tname       string\n\t\targs       args\n\t\twantOff    int\n\t\twantBytes  []byte\n\t\twantErr    bool\n\t\twantErrMsg string\n\t}{\n\t\t{\n\t\t\tname: \"overflow\",\n\t\t\targs: args{\n\t\t\t\tbitmap: []uint16{\n\t\t\t\t\t8962, 8963, 8970, 8971, 8978, 8979,\n\t\t\t\t\t8986, 8987, 8994, 8995, 9002, 9003,\n\t\t\t\t\t9010, 9011, 9018, 9019, 9026, 9027,\n\t\t\t\t\t9034, 9035, 9042, 9043, 9050, 9051,\n\t\t\t\t\t9058, 9059, 9066,\n\t\t\t\t},\n\t\t\t\tmsg: []byte{\n\t\t\t\t\t48, 48, 48, 48, 0, 0, 0,\n\t\t\t\t\t1, 0, 0, 0, 0, 0, 0, 50,\n\t\t\t\t\t48, 48, 48, 48, 48, 48,\n\t\t\t\t\t0, 54, 48, 48, 48, 48,\n\t\t\t\t\t0, 19, 48, 48,\n\t\t\t\t},\n\t\t\t\toff: 48,\n\t\t\t},\n\t\t\twantErr:    true,\n\t\t\twantErrMsg: \"dns: overflow packing nsec\",\n\t\t\twantOff:    48,\n\t\t},\n\t\t{\n\t\t\tname: \"disordered nsec bits\",\n\t\t\targs: args{\n\t\t\t\tbitmap: []uint16{\n\t\t\t\t\t8962,\n\t\t\t\t\t1,\n\t\t\t\t},\n\t\t\t\tmsg: []byte{\n\t\t\t\t\t48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0,\n\t\t\t\t\t0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48,\n\t\t\t\t\t48, 48, 48, 0, 19, 48, 48, 48, 48, 48, 48, 0,\n\t\t\t\t\t0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48,\n\t\t\t\t\t48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19,\n\t\t\t\t\t48, 48, 48, 48, 48, 48, 0, 0, 0, 1, 0, 0,\n\t\t\t\t\t0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0,\n\t\t\t\t\t54, 48, 48, 48, 48, 0, 19, 48, 48, 48, 48, 48,\n\t\t\t\t\t48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50,\n\t\t\t\t\t48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48,\n\t\t\t\t\t0, 19, 48, 48, 48, 48, 48, 48, 0, 0, 0, 1,\n\t\t\t\t\t0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48,\n\t\t\t\t\t48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48,\n\t\t\t\t},\n\t\t\t\toff: 0,\n\t\t\t},\n\t\t\twantErr:    true,\n\t\t\twantErrMsg: \"dns: nsec bits out of order\",\n\t\t\twantOff:    155,\n\t\t},\n\t\t{\n\t\t\tname: \"simple message with only one window\",\n\t\t\targs: args{\n\t\t\t\tbitmap: []uint16{\n\t\t\t\t\t1,\n\t\t\t\t},\n\t\t\t\tmsg: []byte{\n\t\t\t\t\t48, 48, 48, 48, 0, 0,\n\t\t\t\t\t0, 1, 0, 0, 0, 0,\n\t\t\t\t\t0, 0, 50, 48, 48, 48,\n\t\t\t\t\t48, 48, 48, 0, 54, 48,\n\t\t\t\t\t48, 48, 48, 0, 19, 48, 48,\n\t\t\t\t},\n\t\t\t\toff: 0,\n\t\t\t},\n\t\t\twantErr:   false,\n\t\t\twantOff:   3,\n\t\t\twantBytes: []byte{0, 1, 64},\n\t\t},\n\t\t{\n\t\t\tname: \"multiple types\",\n\t\t\targs: args{\n\t\t\t\tbitmap: []uint16{\n\t\t\t\t\tTypeNS, TypeSOA, TypeRRSIG, TypeDNSKEY, TypeNSEC3PARAM,\n\t\t\t\t},\n\t\t\t\tmsg: []byte{\n\t\t\t\t\t48, 48, 48, 48, 0, 0,\n\t\t\t\t\t0, 1, 0, 0, 0, 0,\n\t\t\t\t\t0, 0, 50, 48, 48, 48,\n\t\t\t\t\t48, 48, 48, 0, 54, 48,\n\t\t\t\t\t48, 48, 48, 0, 19, 48, 48,\n\t\t\t\t},\n\t\t\t\toff: 0,\n\t\t\t},\n\t\t\twantErr:   false,\n\t\t\twantOff:   9,\n\t\t\twantBytes: []byte{0, 7, 34, 0, 0, 0, 0, 2, 144},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotOff, err := packDataNsec(tt.args.bitmap, tt.args.msg, tt.args.off)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"packDataNsec() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil && tt.wantErrMsg != err.Error() {\n\t\t\t\tt.Errorf(\"packDataNsec() error msg = %v, wantErrMsg %v\", err.Error(), tt.wantErrMsg)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif gotOff != tt.wantOff {\n\t\t\t\tt.Errorf(\"packDataNsec() = %v, want off %v\", gotOff, tt.wantOff)\n\t\t\t}\n\t\t\tif err == nil && tt.args.off < len(tt.args.msg) && gotOff < len(tt.args.msg) {\n\t\t\t\tif want, got := tt.wantBytes, tt.args.msg[tt.args.off:gotOff]; !bytes.Equal(got, want) {\n\t\t\t\t\tt.Errorf(\"packDataNsec() = %v, want bytes %v\", got, want)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPackDataNsecDirtyBuffer(t *testing.T) {\n\tzeroBuf := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0}\n\tdirtyBuf := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}\n\toff1, _ := packDataNsec([]uint16{TypeNS, TypeSOA, TypeRRSIG}, zeroBuf, 0)\n\toff2, _ := packDataNsec([]uint16{TypeNS, TypeSOA, TypeRRSIG}, dirtyBuf, 0)\n\tif off1 != off2 {\n\t\tt.Errorf(\"off1 %v != off2 %v\", off1, off2)\n\t}\n\tif !bytes.Equal(zeroBuf[:off1], dirtyBuf[:off2]) {\n\t\tt.Errorf(\"dirty buffer differs from zero buffer: %v, %v\", zeroBuf[:off1], dirtyBuf[:off2])\n\t}\n}\n\nfunc BenchmarkPackDataNsec(b *testing.B) {\n\tbenches := []struct {\n\t\tname  string\n\t\ttypes []uint16\n\t}{\n\t\t{\"empty\", nil},\n\t\t{\"typical\", []uint16{TypeNS, TypeSOA, TypeRRSIG, TypeDNSKEY, TypeNSEC3PARAM}},\n\t\t{\"multiple_windows\", []uint16{1, 300, 350, 10000, 20000}},\n\t}\n\tfor _, bb := range benches {\n\t\tb.Run(bb.name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 100)\n\t\t\tfor n := 0; n < b.N; n++ {\n\t\t\t\tpackDataNsec(bb.types, buf, 0)\n\t\t\t}\n\t\t})\n\t}\n}\nfunc TestUnpackString(t *testing.T) {\n\tmsg := []byte(\"\\x00abcdef\\x0f\\\\\\\"ghi\\x04mmm\\x7f\")\n\tmsg[0] = byte(len(msg) - 1)\n\n\tgot, _, err := unpackString(msg, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want := `abcdef\\015\\\\\\\"ghi\\004mmm\\127`; want != got {\n\t\tt.Errorf(\"expected %q, got %q\", want, got)\n\t}\n}\n\nfunc BenchmarkUnpackString(b *testing.B) {\n\tb.Run(\"Escaped\", func(b *testing.B) {\n\t\tmsg := []byte(\"\\x00abcdef\\x0f\\\\\\\"ghi\\x04mmm\")\n\t\tmsg[0] = byte(len(msg) - 1)\n\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tgot, _, err := unpackString(msg, 0)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\n\t\t\tif want := `abcdef\\015\\\\\\\"ghi\\004mmm`; want != got {\n\t\t\t\tb.Errorf(\"expected %q, got %q\", want, got)\n\t\t\t}\n\t\t}\n\t})\n\tb.Run(\"Unescaped\", func(b *testing.B) {\n\t\tmsg := []byte(\"\\x00large.example.com\")\n\t\tmsg[0] = byte(len(msg) - 1)\n\n\t\tfor n := 0; n < b.N; n++ {\n\t\t\tgot, _, err := unpackString(msg, 0)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\n\t\t\tif want := \"large.example.com\"; want != got {\n\t\t\t\tb.Errorf(\"expected %q, got %q\", want, got)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestPackDataAplPrefix(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tnegation bool\n\t\tip       net.IP\n\t\tmask     net.IPMask\n\t\texpect   []byte\n\t}{\n\t\t{\n\t\t\t\"1:192.0.2.0/24\",\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"192.0.2.0\").To4(),\n\t\t\tnet.CIDRMask(24, 32),\n\t\t\t[]byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2},\n\t\t},\n\t\t{\n\t\t\t\"2:2001:db8:cafe::0/48\",\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"2001:db8:cafe::\"),\n\t\t\tnet.CIDRMask(48, 128),\n\t\t\t[]byte{0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe},\n\t\t},\n\t\t{\n\t\t\t\"with trailing zero bytes 2:2001:db8:cafe::0/64\",\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"2001:db8:cafe::\"),\n\t\t\tnet.CIDRMask(64, 128),\n\t\t\t[]byte{0x00, 0x02, 0x40, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe},\n\t\t},\n\t\t{\n\t\t\t\"no non-zero bytes 2::/16\",\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"::\"),\n\t\t\tnet.CIDRMask(16, 128),\n\t\t\t[]byte{0x00, 0x02, 0x10, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"!2:2001:db8::/32\",\n\t\t\ttrue,\n\t\t\tnet.ParseIP(\"2001:db8::\"),\n\t\t\tnet.CIDRMask(32, 128),\n\t\t\t[]byte{0x00, 0x02, 0x20, 0x84, 0x20, 0x01, 0x0d, 0xb8},\n\t\t},\n\t\t{\n\t\t\t\"normalize 1:198.51.103.255/22\",\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"198.51.103.255\").To4(),\n\t\t\tnet.CIDRMask(22, 32),\n\t\t\t[]byte{0x00, 0x01, 0x16, 0x03, 198, 51, 100}, // 1:198.51.100.0/22\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tap := &APLPrefix{\n\t\t\t\tNegation: tt.negation,\n\t\t\t\tNetwork:  net.IPNet{IP: tt.ip, Mask: tt.mask},\n\t\t\t}\n\t\t\tout := make([]byte, 16)\n\t\t\toff, err := packDataAplPrefix(ap, out, 0)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"expected no error, got %q\", err)\n\t\t\t}\n\t\t\tif !bytes.Equal(tt.expect, out[:off]) {\n\t\t\t\tt.Fatalf(\"expected output %02x, got %02x\", tt.expect, out[:off])\n\t\t\t}\n\t\t\t// Make sure the packed bytes would be accepted by its own unpack\n\t\t\t_, _, err = unpackDataAplPrefix(out, 0)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"expected no error, got %q\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPackDataAplPrefix_Failures(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tip   net.IP\n\t\tmask net.IPMask\n\t}{\n\t\t{\n\t\t\t\"family mismatch\",\n\t\t\tnet.ParseIP(\"2001:db8::\"),\n\t\t\tnet.CIDRMask(24, 32),\n\t\t},\n\t\t{\n\t\t\t\"unrecognized family\",\n\t\t\t[]byte{0x42},\n\t\t\t[]byte{0xff},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tap := &APLPrefix{Network: net.IPNet{IP: tt.ip, Mask: tt.mask}}\n\t\t\tmsg := make([]byte, 16)\n\t\t\toff, err := packDataAplPrefix(ap, msg, 0)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"expected error, got none\")\n\t\t\t}\n\t\t\tif off != len(msg) {\n\t\t\t\tt.Fatalf(\"expected %d, got %d\", len(msg), off)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPackDataAplPrefix_BufferBounds(t *testing.T) {\n\tap := &APLPrefix{\n\t\tNegation: false,\n\t\tNetwork: net.IPNet{\n\t\t\tIP:   net.ParseIP(\"2001:db8::\"),\n\t\t\tMask: net.CIDRMask(32, 128),\n\t\t},\n\t}\n\twire := []byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8}\n\n\tt.Run(\"small\", func(t *testing.T) {\n\t\tmsg := make([]byte, len(wire))\n\t\t_, err := packDataAplPrefix(ap, msg, 1) // offset\n\t\tif err == nil {\n\t\t\tt.Fatal(\"expected error, got none\")\n\t\t}\n\t})\n\n\tt.Run(\"exact fit\", func(t *testing.T) {\n\t\tmsg := make([]byte, len(wire))\n\t\toff, err := packDataAplPrefix(ap, msg, 0)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"expected no error, got %q\", err)\n\t\t}\n\t\tif !bytes.Equal(wire, msg[:off]) {\n\t\t\tt.Fatalf(\"expected %02x, got %02x\", wire, msg[:off])\n\t\t}\n\t})\n}\n\nfunc TestPackDataApl(t *testing.T) {\n\tin := []APLPrefix{\n\t\t{\n\t\t\tNegation: true,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"198.51.0.0\").To4(),\n\t\t\t\tMask: net.CIDRMask(16, 32),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: false,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"2001:db8:beef::\"),\n\t\t\t\tMask: net.CIDRMask(48, 128),\n\t\t\t},\n\t\t},\n\t}\n\texpect := []byte{\n\t\t// 1:192.51.0.0/16\n\t\t0x00, 0x01, 0x10, 0x82, 0xc6, 0x33,\n\t\t// 2:2001:db8:beef::0/48\n\t\t0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xbe, 0xef,\n\t}\n\n\tmsg := make([]byte, 32)\n\toff, err := packDataApl(in, msg, 0)\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error, got %q\", err)\n\t}\n\tif !bytes.Equal(expect, msg[:off]) {\n\t\tt.Fatalf(\"expected %02x, got %02x\", expect, msg[:off])\n\t}\n}\n\nfunc TestUnpackDataAplPrefix(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\twire     []byte\n\t\tnegation bool\n\t\tip       net.IP\n\t\tmask     net.IPMask\n\t}{\n\t\t{\n\t\t\t\"1:192.0.2.0/24\",\n\t\t\t[]byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2},\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"192.0.2.0\").To4(),\n\t\t\tnet.CIDRMask(24, 32),\n\t\t},\n\t\t{\n\t\t\t\"2:2001:db8::/32\",\n\t\t\t[]byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8},\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"2001:db8::\"),\n\t\t\tnet.CIDRMask(32, 128),\n\t\t},\n\t\t{\n\t\t\t\"!2:2001:db8:8000::/33\",\n\t\t\t[]byte{0x00, 0x02, 0x21, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80},\n\t\t\ttrue,\n\t\t\tnet.ParseIP(\"2001:db8:8000::\"),\n\t\t\tnet.CIDRMask(33, 128),\n\t\t},\n\t\t{\n\t\t\t\"1:0.0.0.0/0\",\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00},\n\t\t\tfalse,\n\t\t\tnet.ParseIP(\"0.0.0.0\").To4(),\n\t\t\tnet.CIDRMask(0, 32),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, off, err := unpackDataAplPrefix(tt.wire, 0)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"expected no error, got %q\", err)\n\t\t\t}\n\t\t\tif off != len(tt.wire) {\n\t\t\t\tt.Fatalf(\"expected offset %d, got %d\", len(tt.wire), off)\n\t\t\t}\n\t\t\tif got.Negation != tt.negation {\n\t\t\t\tt.Errorf(\"expected negation %v, got %v\", tt.negation, got.Negation)\n\t\t\t}\n\t\t\tif !tt.ip.Equal(got.Network.IP) {\n\t\t\t\tt.Errorf(\"expected IP %02x, got %02x\", tt.ip, got.Network.IP)\n\t\t\t}\n\t\t\tif !bytes.Equal(got.Network.Mask, tt.mask) {\n\t\t\t\tt.Errorf(\"expected mask %02x, got %02x\", tt.mask, got.Network.Mask)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnpackDataAplPrefix_Errors(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twire []byte\n\t\twant string\n\t}{\n\t\t{\n\t\t\t\"incomplete header\",\n\t\t\t[]byte{0x00, 0x01, 0x18},\n\t\t\t\"dns: overflow unpacking APL prefix\",\n\t\t},\n\t\t{\n\t\t\t\"unrecognized family\",\n\t\t\t[]byte{0x00, 0x03, 0x00, 0x00},\n\t\t\t\"dns: unrecognized APL address family\",\n\t\t},\n\t\t{\n\t\t\t\"prefix too large for family IPv4\",\n\t\t\t[]byte{0x00, 0x01, 0x21, 0x04, 192, 0, 2, 0},\n\t\t\t\"dns: APL prefix too long\",\n\t\t},\n\t\t{\n\t\t\t\"prefix too large for family IPv6\",\n\t\t\t[]byte{0x00, 0x02, 0x81, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80},\n\t\t\t\"dns: APL prefix too long\",\n\t\t},\n\t\t{\n\t\t\t\"afdlen too long for address family IPv4\",\n\t\t\t[]byte{0x00, 0x01, 22, 0x05, 192, 0, 2, 0, 0},\n\t\t\t\"dns: APL length too long\",\n\t\t},\n\t\t{\n\t\t\t\"overflow unpacking APL address\",\n\t\t\t[]byte{0x00, 0x01, 0x10, 0x02, 192},\n\t\t\t\"dns: overflow unpacking APL address\",\n\t\t},\n\t\t{\n\t\t\t\"address included trailing zeros\",\n\t\t\t[]byte{0x00, 0x01, 0x10, 0x04, 192, 0, 2, 0},\n\t\t\t\"dns: extra APL address bits\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t_, _, err := unpackDataAplPrefix(tt.wire, 0)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"expected error, got none\")\n\t\t\t}\n\n\t\t\tif err.Error() != tt.want {\n\t\t\t\tt.Errorf(\"expected %s, got %s\", tt.want, err.Error())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnpackDataApl(t *testing.T) {\n\twire := []byte{\n\t\t// 2:2001:db8:cafe:4200:0/56\n\t\t0x00, 0x02, 0x38, 0x07, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0x42,\n\t\t// 1:192.0.2.0/24\n\t\t0x00, 0x01, 0x18, 0x03, 192, 0, 2,\n\t\t// !1:192.0.2.128/25\n\t\t0x00, 0x01, 0x19, 0x84, 192, 0, 2, 128,\n\t\t// 1:10.0.0.0/24\n\t\t0x00, 0x01, 0x18, 0x01, 0x0a,\n\t\t// !1:10.0.0.1/32\n\t\t0x00, 0x01, 0x20, 0x84, 0x0a, 0, 0, 1,\n\t\t// !1:0.0.0.0/0\n\t\t0x00, 0x01, 0x00, 0x80,\n\t\t// 2::0/0\n\t\t0x00, 0x02, 0x00, 0x00,\n\t}\n\texpect := []APLPrefix{\n\t\t{\n\t\t\tNegation: false,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"2001:db8:cafe:4200::\"),\n\t\t\t\tMask: net.CIDRMask(56, 128),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: false,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"192.0.2.0\").To4(),\n\t\t\t\tMask: net.CIDRMask(24, 32),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: true,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"192.0.2.128\").To4(),\n\t\t\t\tMask: net.CIDRMask(25, 32),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: false,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"10.0.0.0\").To4(),\n\t\t\t\tMask: net.CIDRMask(24, 32),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: true,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"10.0.0.1\").To4(),\n\t\t\t\tMask: net.CIDRMask(32, 32),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: true,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"0.0.0.0\").To4(),\n\t\t\t\tMask: net.CIDRMask(0, 32),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tNegation: false,\n\t\t\tNetwork: net.IPNet{\n\t\t\t\tIP:   net.ParseIP(\"::\").To16(),\n\t\t\t\tMask: net.CIDRMask(0, 128),\n\t\t\t},\n\t\t},\n\t}\n\n\tgot, off, err := unpackDataApl(wire, 0)\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error, got %q\", err)\n\t}\n\tif off != len(wire) {\n\t\tt.Fatalf(\"expected offset %d, got %d\", len(wire), off)\n\t}\n\tif len(got) != len(expect) {\n\t\tt.Fatalf(\"expected %d prefixes, got %d\", len(expect), len(got))\n\t}\n\tfor i, exp := range expect {\n\t\tif got[i].Negation != exp.Negation {\n\t\t\tt.Errorf(\"[%d] expected negation %v, got %v\", i, exp.Negation, got[i].Negation)\n\t\t}\n\t\tif !exp.Network.IP.Equal(got[i].Network.IP) {\n\t\t\tt.Errorf(\"[%d] expected IP %02x, got %02x\", i, exp.Network.IP, got[i].Network.IP)\n\t\t}\n\t\tif !bytes.Equal(got[i].Network.Mask, exp.Network.Mask) {\n\t\t\tt.Errorf(\"[%d] expected mask %02x, got %02x\", i, exp.Network.Mask, got[i].Network.Mask)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "msg_test.go",
    "content": "package dns\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n)\n\nconst maxPrintableLabel = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789x\"\n\nvar (\n\tlongDomain = maxPrintableLabel[:53] + strings.TrimSuffix(\n\t\tstrings.Join([]string{\".\", \".\", \".\", \".\", \".\"}, maxPrintableLabel[:49]), \".\")\n\n\treChar              = regexp.MustCompile(`.`)\n\ti                   = -1\n\tmaxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string {\n\t\tif i++; i >= 32 {\n\t\t\ti = 0\n\t\t}\n\t\treturn fmt.Sprintf(\"\\\\%03d\", i)\n\t})\n\n\t// These are the longest possible domain names in presentation format.\n\tlongestDomain            = maxPrintableLabel[:61] + strings.Join([]string{\".\", \".\", \".\", \".\"}, maxPrintableLabel)\n\tlongestUnprintableDomain = maxUnprintableLabel[:61*4] + strings.Join([]string{\".\", \".\", \".\", \".\"}, maxUnprintableLabel)\n)\n\nfunc TestPackNoSideEffect(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(Fqdn(\"example.com.\"), TypeNS)\n\n\ta := new(Msg)\n\to := &OPT{\n\t\tHdr: RR_Header{\n\t\t\tName:   \".\",\n\t\t\tRrtype: TypeOPT,\n\t\t},\n\t}\n\to.SetUDPSize(DefaultMsgSize)\n\n\ta.Extra = append(a.Extra, o)\n\ta.SetRcode(m, RcodeBadVers)\n\n\ta.Pack()\n\tif a.Rcode != RcodeBadVers {\n\t\tt.Errorf(\"after pack: Rcode is expected to be BADVERS\")\n\t}\n}\n\nfunc TestPackExtendedBadCookie(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(Fqdn(\"example.com.\"), TypeNS)\n\n\ta := new(Msg)\n\ta.SetReply(m)\n\to := &OPT{\n\t\tHdr: RR_Header{\n\t\t\tName:   \".\",\n\t\t\tRrtype: TypeOPT,\n\t\t},\n\t}\n\to.SetUDPSize(DefaultMsgSize)\n\ta.Extra = append(a.Extra, o)\n\n\ta.SetRcode(m, RcodeBadCookie)\n\n\tedns0 := a.IsEdns0()\n\tif edns0 == nil {\n\t\tt.Fatal(\"Expected OPT RR\")\n\t}\n\t// SetExtendedRcode is only called as part of `Pack()`, hence at this stage,\n\t// the OPT RR is not set yet.\n\tif edns0.ExtendedRcode() == RcodeBadCookie&0xFFFFFFF0 {\n\t\tt.Errorf(\"ExtendedRcode is expected to not be BADCOOKIE before Pack\")\n\t}\n\n\ta.Pack()\n\n\tedns0 = a.IsEdns0()\n\tif edns0 == nil {\n\t\tt.Fatal(\"Expected OPT RR\")\n\t}\n\n\tif edns0.ExtendedRcode() != RcodeBadCookie&0xFFFFFFF0 {\n\t\tt.Errorf(\"ExtendedRcode is expected to be BADCOOKIE after Pack\")\n\t}\n}\n\nfunc TestUnPackExtendedRcode(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(Fqdn(\"example.com.\"), TypeNS)\n\n\ta := new(Msg)\n\ta.SetReply(m)\n\to := &OPT{\n\t\tHdr: RR_Header{\n\t\t\tName:   \".\",\n\t\t\tRrtype: TypeOPT,\n\t\t},\n\t}\n\to.SetUDPSize(DefaultMsgSize)\n\ta.Extra = append(a.Extra, o)\n\n\ta.SetRcode(m, RcodeBadCookie)\n\n\tpacked, err := a.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"Could not unpack %v\", a)\n\t}\n\n\tunpacked := new(Msg)\n\tif err := unpacked.Unpack(packed); err != nil {\n\t\tt.Fatalf(\"Failed to unpack message\")\n\t}\n\n\tif unpacked.Rcode != RcodeBadCookie {\n\t\tt.Fatalf(\"Rcode should be matching RcodeBadCookie (%d), got (%d)\", RcodeBadCookie, unpacked.Rcode)\n\t}\n}\n\nfunc TestUnpackDomainName(t *testing.T) {\n\tvar cases = []struct {\n\t\tlabel          string\n\t\tinput          string\n\t\texpectedOutput string\n\t\texpectedError  string\n\t}{\n\t\t{\"empty domain\",\n\t\t\t\"\\x00\",\n\t\t\t\".\",\n\t\t\t\"\"},\n\t\t{\"long label\",\n\t\t\t\"?\" + maxPrintableLabel + \"\\x00\",\n\t\t\tmaxPrintableLabel + \".\",\n\t\t\t\"\"},\n\t\t{\"unprintable label\",\n\t\t\t\"?\" + regexp.MustCompile(`\\\\[0-9]+`).ReplaceAllStringFunc(maxUnprintableLabel,\n\t\t\t\tfunc(escape string) string {\n\t\t\t\t\tn, _ := strconv.ParseInt(escape[1:], 10, 8)\n\t\t\t\t\treturn string(rune(n))\n\t\t\t\t}) + \"\\x00\",\n\t\t\tmaxUnprintableLabel + \".\",\n\t\t\t\"\"},\n\t\t{\"long domain\",\n\t\t\t\"5\" + strings.Replace(longDomain, \".\", \"1\", -1) + \"\\x00\",\n\t\t\tlongDomain + \".\",\n\t\t\t\"\"},\n\t\t{\"compression pointer\",\n\t\t\t// an unrealistic but functional test referencing an offset _inside_ a label\n\t\t\t\"\\x03foo\" + \"\\x05\\x03com\\x00\" + \"\\x07example\" + \"\\xC0\\x05\",\n\t\t\t\"foo.\\\\003com\\\\000.example.com.\",\n\t\t\t\"\"},\n\t\t{\"too long domain\",\n\t\t\t\"6\" + \"x\" + strings.Replace(longDomain, \".\", \"1\", -1) + \"\\x00\",\n\t\t\t\"\",\n\t\t\tErrLongDomain.Error()},\n\t\t{\"too long by pointer\",\n\t\t\t// a matryoshka doll name to get over 255 octets after expansion via internal pointers\n\t\t\tstring([]byte{\n\t\t\t\t// 11 length values, first to last\n\t\t\t\t40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 0,\n\t\t\t\t// 12 filler values\n\t\t\t\t120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120,\n\t\t\t\t// 10 pointers, last to first\n\t\t\t\t192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,\n\t\t\t}),\n\t\t\t\"\",\n\t\t\tErrLongDomain.Error()},\n\t\t{\"long by pointer\",\n\t\t\t// a matryoshka doll name _not_ exceeding 255 octets after expansion\n\t\t\tstring([]byte{\n\t\t\t\t// 11 length values, first to last\n\t\t\t\t37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 0,\n\t\t\t\t// 9 filler values\n\t\t\t\t120, 120, 120, 120, 120, 120, 120, 120, 120,\n\t\t\t\t// 10 pointers, last to first\n\t\t\t\t192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,\n\t\t\t}),\n\t\t\t\"\" +\n\t\t\t\t(`\\\"\\031\\028\\025\\022\\019\\016\\013\\010\\000xxxxxxxxx` +\n\t\t\t\t\t`\\192\\010\\192\\009\\192\\008\\192\\007\\192\\006\\192\\005\\192\\004\\192\\003\\192\\002.`) +\n\t\t\t\t(`\\031\\028\\025\\022\\019\\016\\013\\010\\000xxxxxxxxx` +\n\t\t\t\t\t`\\192\\010\\192\\009\\192\\008\\192\\007\\192\\006\\192\\005\\192\\004\\192\\003.`) +\n\t\t\t\t(`\\028\\025\\022\\019\\016\\013\\010\\000xxxxxxxxx` +\n\t\t\t\t\t`\\192\\010\\192\\009\\192\\008\\192\\007\\192\\006\\192\\005\\192\\004.`) +\n\t\t\t\t(`\\025\\022\\019\\016\\013\\010\\000xxxxxxxxx` +\n\t\t\t\t\t`\\192\\010\\192\\009\\192\\008\\192\\007\\192\\006\\192\\005.`) +\n\t\t\t\t`\\022\\019\\016\\013\\010\\000xxxxxxxxx\\192\\010\\192\\009\\192\\008\\192\\007\\192\\006.` +\n\t\t\t\t`\\019\\016\\013\\010\\000xxxxxxxxx\\192\\010\\192\\009\\192\\008\\192\\007.` +\n\t\t\t\t`\\016\\013\\010\\000xxxxxxxxx\\192\\010\\192\\009\\192\\008.` +\n\t\t\t\t`\\013\\010\\000xxxxxxxxx\\192\\010\\192\\009.` +\n\t\t\t\t`\\010\\000xxxxxxxxx\\192\\010.` +\n\t\t\t\t`\\000xxxxxxxxx.`,\n\t\t\t\"\"},\n\t\t{\"truncated name\", \"\\x07example\\x03\", \"\", \"dns: buffer size too small\"},\n\t\t{\"non-absolute name\", \"\\x07example\\x03com\", \"\", \"dns: buffer size too small\"},\n\t\t{\"compression pointer cycle (too many)\", \"\\xC0\\x00\", \"\", \"dns: too many compression pointers\"},\n\t\t{\"compression pointer cycle (too long)\",\n\t\t\t\"\\x03foo\" + \"\\x03bar\" + \"\\x07example\" + \"\\xC0\\x04\",\n\t\t\t\"\",\n\t\t\tErrLongDomain.Error()},\n\t\t{\"forward compression pointer\", \"\\x02\\xC0\\xFF\\xC0\\x01\", \"\", ErrBuf.Error()},\n\t\t{\"reserved compression pointer 0b10\", \"\\x07example\\x80\", \"\", \"dns: bad rdata\"},\n\t\t{\"reserved compression pointer 0b01\", \"\\x07example\\x40\", \"\", \"dns: bad rdata\"},\n\t}\n\tfor _, test := range cases {\n\t\toutput, idx, err := UnpackDomainName([]byte(test.input), 0)\n\t\tif test.expectedOutput != \"\" && output != test.expectedOutput {\n\t\t\tt.Errorf(\"%s: expected %s, got %s\", test.label, test.expectedOutput, output)\n\t\t}\n\t\tif test.expectedError == \"\" && err != nil {\n\t\t\tt.Errorf(\"%s: expected no error, got %d %v\", test.label, idx, err)\n\t\t} else if test.expectedError != \"\" && (err == nil || err.Error() != test.expectedError) {\n\t\t\tt.Errorf(\"%s: expected error %s, got %d %v\", test.label, test.expectedError, idx, err)\n\t\t}\n\t}\n}\n\nfunc TestPackDomainNameCompressionMap(t *testing.T) {\n\texpected := map[string]struct{}{\n\t\t`www\\.this.is.\\131an.example.org.`: {},\n\t\t`is.\\131an.example.org.`:           {},\n\t\t`\\131an.example.org.`:              {},\n\t\t`example.org.`:                     {},\n\t\t`org.`:                             {},\n\t}\n\n\tmsg := make([]byte, 256)\n\tfor _, compress := range []bool{true, false} {\n\t\tcompression := make(map[string]int)\n\n\t\t_, err := PackDomainName(`www\\.this.is.\\131an.example.org.`, msg, 0, compression, compress)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"PackDomainName failed: %v\", err)\n\t\t}\n\n\t\tif !compressionMapsEqual(expected, compression) {\n\t\t\tt.Errorf(\"expected compression maps to be equal\\n%s\", compressionMapsDifference(expected, compression))\n\t\t}\n\t}\n}\n\nfunc TestPackDomainNameNSECTypeBitmap(t *testing.T) {\n\townername := \"some-very-long-ownername.com.\"\n\tmsg := &Msg{\n\t\tCompress: true,\n\t\tAnswer: []RR{\n\t\t\t&NS{\n\t\t\t\tHdr: RR_Header{\n\t\t\t\t\tName:   ownername,\n\t\t\t\t\tRrtype: TypeNS,\n\t\t\t\t\tClass:  ClassINET,\n\t\t\t\t},\n\t\t\t\tNs: \"ns1.server.com.\",\n\t\t\t},\n\t\t\t&NSEC{\n\t\t\t\tHdr: RR_Header{\n\t\t\t\t\tName:   ownername,\n\t\t\t\t\tRrtype: TypeNSEC,\n\t\t\t\t\tClass:  ClassINET,\n\t\t\t\t},\n\t\t\t\tNextDomain: \"a.com.\",\n\t\t\t\tTypeBitMap: []uint16{TypeNS, TypeNSEC},\n\t\t\t},\n\t\t},\n\t}\n\n\t// Pack msg and then unpack into msg2\n\tbuf, err := msg.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"msg.Pack failed: %v\", err)\n\t}\n\n\tvar msg2 Msg\n\tif err := msg2.Unpack(buf); err != nil {\n\t\tt.Fatalf(\"msg2.Unpack failed: %v\", err)\n\t}\n\n\tif !IsDuplicate(msg.Answer[1], msg2.Answer[1]) {\n\t\tt.Error(\"message differs after packing and unpacking\")\n\n\t\t// Print NSEC RR for both cases\n\t\tt.Logf(\"expected: %v\", msg.Answer[1])\n\t\tt.Logf(\"got:      %v\", msg2.Answer[1])\n\t}\n}\n\nfunc TestPackUnpackManyCompressionPointers(t *testing.T) {\n\tm := new(Msg)\n\tm.Compress = true\n\tm.SetQuestion(\"example.org.\", TypeNS)\n\n\tfor domain := \"a.\"; len(domain) < maxDomainNameWireOctets; domain += \"a.\" {\n\t\tm.Answer = append(m.Answer, &NS{Hdr: RR_Header{Name: domain, Rrtype: TypeNS, Class: ClassINET}, Ns: \"example.org.\"})\n\n\t\tb, err := m.Pack()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Pack failed for %q and %d records with: %v\", domain, len(m.Answer), err)\n\t\t}\n\n\t\tvar m2 Msg\n\t\tif err := m2.Unpack(b); err != nil {\n\t\t\tt.Fatalf(\"Unpack failed for %q and %d records with: %v\", domain, len(m.Answer), err)\n\t\t}\n\t}\n}\n\nfunc TestLenDynamicA(t *testing.T) {\n\tfor _, rr := range []RR{\n\t\ttestRR(\"example.org. A\"),\n\t\ttestRR(\"example.org. AAAA\"),\n\t\ttestRR(\"example.org. L32\"),\n\t} {\n\t\tmsg := make([]byte, Len(rr))\n\t\toff, err := PackRR(rr, msg, 0, nil, false)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"PackRR failed for %T: %v\", rr, err)\n\t\t}\n\t\tif off != len(msg) {\n\t\t\tt.Errorf(\"Len(rr) wrong for %T: Len(rr) = %d, PackRR(rr) = %d\", rr, len(msg), off)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "msg_truncate.go",
    "content": "package dns\n\n// Truncate ensures the reply message will fit into the requested buffer\n// size by removing records that exceed the requested size.\n//\n// It will first check if the reply fits without compression and then with\n// compression. If it won't fit with compression, Truncate then walks the\n// record adding as many records as possible without exceeding the\n// requested buffer size.\n//\n// If the message fits within the requested size without compression,\n// Truncate will set the message's Compress attribute to false. It is\n// the caller's responsibility to set it back to true if they wish to\n// compress the payload regardless of size.\n//\n// The TC bit will be set if any records were excluded from the message.\n// If the TC bit is already set on the message it will be retained.\n// TC indicates that the client should retry over TCP.\n//\n// According to RFC 2181, the TC bit should only be set if not all of the\n// \"required\" RRs can be included in the response. Unfortunately, we have\n// no way of knowing which RRs are required so we set the TC bit if any RR\n// had to be omitted from the response.\n//\n// The appropriate buffer size can be retrieved from the requests OPT\n// record, if present, and is transport specific otherwise. dns.MinMsgSize\n// should be used for UDP requests without an OPT record, and\n// dns.MaxMsgSize for TCP requests without an OPT record.\nfunc (dns *Msg) Truncate(size int) {\n\tif dns.IsTsig() != nil {\n\t\t// To simplify this implementation, we don't perform\n\t\t// truncation on responses with a TSIG record.\n\t\treturn\n\t}\n\n\t// RFC 6891 mandates that the payload size in an OPT record\n\t// less than 512 (MinMsgSize) bytes must be treated as equal to 512 bytes.\n\t//\n\t// For ease of use, we impose that restriction here.\n\tif size < MinMsgSize {\n\t\tsize = MinMsgSize\n\t}\n\n\tl := msgLenWithCompressionMap(dns, nil) // uncompressed length\n\tif l <= size {\n\t\t// Don't waste effort compressing this message.\n\t\tdns.Compress = false\n\t\treturn\n\t}\n\n\tdns.Compress = true\n\n\tedns0 := dns.popEdns0()\n\tif edns0 != nil {\n\t\t// Account for the OPT record that gets added at the end,\n\t\t// by subtracting that length from our budget.\n\t\t//\n\t\t// The EDNS(0) OPT record must have the root domain and\n\t\t// it's length is thus unaffected by compression.\n\t\tsize -= Len(edns0)\n\t}\n\n\tcompression := make(map[string]struct{})\n\n\tl = headerSize\n\tfor _, r := range dns.Question {\n\t\tl += r.len(l, compression)\n\t}\n\n\tvar numAnswer int\n\tif l < size {\n\t\tl, numAnswer = truncateLoop(dns.Answer, size, l, compression)\n\t}\n\n\tvar numNS int\n\tif l < size {\n\t\tl, numNS = truncateLoop(dns.Ns, size, l, compression)\n\t}\n\n\tvar numExtra int\n\tif l < size {\n\t\t_, numExtra = truncateLoop(dns.Extra, size, l, compression)\n\t}\n\n\t// See the function documentation for when we set this.\n\tdns.Truncated = dns.Truncated || len(dns.Answer) > numAnswer ||\n\t\tlen(dns.Ns) > numNS || len(dns.Extra) > numExtra\n\n\tdns.Answer = dns.Answer[:numAnswer]\n\tdns.Ns = dns.Ns[:numNS]\n\tdns.Extra = dns.Extra[:numExtra]\n\n\tif edns0 != nil {\n\t\t// Add the OPT record back onto the additional section.\n\t\tdns.Extra = append(dns.Extra, edns0)\n\t}\n}\n\nfunc truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {\n\tfor i, r := range rrs {\n\t\tif r == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tl += r.len(l, compression)\n\t\tif l > size {\n\t\t\t// Return size, rather than l prior to this record,\n\t\t\t// to prevent any further records being added.\n\t\t\treturn size, i\n\t\t}\n\t\tif l == size {\n\t\t\treturn l, i + 1\n\t\t}\n\t}\n\n\treturn l, len(rrs)\n}\n"
  },
  {
    "path": "msg_truncate_test.go",
    "content": "package dns\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestRequestTruncateAnswer(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(\"large.example.com.\", TypeSRV)\n\n\treply := new(Msg)\n\treply.SetReply(m)\n\tfor i := 1; i < 200; i++ {\n\t\treply.Answer = append(reply.Answer, testRR(\n\t\t\tfmt.Sprintf(\"large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.\", i)))\n\t}\n\n\treply.Truncate(MinMsgSize)\n\tif want, got := MinMsgSize, reply.Len(); want < got {\n\t\tt.Errorf(\"message length should be below %d bytes, got %d bytes\", want, got)\n\t}\n\tif !reply.Truncated {\n\t\tt.Errorf(\"truncated bit should be set\")\n\t}\n}\n\nfunc TestRequestTruncateExtra(t *testing.T) {\n\tm := new(Msg)\n\tm.SetQuestion(\"large.example.com.\", TypeSRV)\n\n\treply := new(Msg)\n\treply.SetReply(m)\n\tfor i := 1; i < 200; i++ {\n\t\treply.Extra = append(reply.Extra, testRR(\n\t\t\tfmt.Sprintf(\"large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.\", i)))\n\t}\n\n\treply.Truncate(MinMsgSize)\n\tif want, got := MinMsgSize, reply.Len(); want < got {\n\t\tt.Errorf(\"message length should be below %d bytes, got %d bytes\", want, got)\n\t}\n\tif !reply.Truncated {\n\t\tt.Errorf(\"truncated bit should be set\")\n\t}\n}\n\nfunc TestRequestTruncateExtraEdns0(t *testing.T) {\n\tconst size = 4096\n\n\tm := new(Msg)\n\tm.SetQuestion(\"large.example.com.\", TypeSRV)\n\tm.SetEdns0(size, true)\n\n\treply := new(Msg)\n\treply.SetReply(m)\n\tfor i := 1; i < 200; i++ {\n\t\treply.Extra = append(reply.Extra, testRR(\n\t\t\tfmt.Sprintf(\"large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.\", i)))\n\t}\n\treply.SetEdns0(size, true)\n\n\treply.Truncate(size)\n\tif want, got := size, reply.Len(); want < got {\n\t\tt.Errorf(\"message length should be below %d bytes, got %d bytes\", want, got)\n\t}\n\tif !reply.Truncated {\n\t\tt.Errorf(\"truncated bit should be set\")\n\t}\n\topt := reply.Extra[len(reply.Extra)-1]\n\tif opt.Header().Rrtype != TypeOPT {\n\t\tt.Errorf(\"expected last RR to be OPT\")\n\t}\n}\n\nfunc TestRequestTruncateExtraRegression(t *testing.T) {\n\tconst size = 2048\n\n\tm := new(Msg)\n\tm.SetQuestion(\"large.example.com.\", TypeSRV)\n\tm.SetEdns0(size, true)\n\n\treply := new(Msg)\n\treply.SetReply(m)\n\tfor i := 1; i < 33; i++ {\n\t\treply.Answer = append(reply.Answer, testRR(\n\t\t\tfmt.Sprintf(\"large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.\", i)))\n\t}\n\tfor i := 1; i < 33; i++ {\n\t\treply.Extra = append(reply.Extra, testRR(\n\t\t\tfmt.Sprintf(\"10-0-0-%d.default.pod.k8s.example.com. 10 IN A 10.0.0.%d\", i, i)))\n\t}\n\treply.SetEdns0(size, true)\n\n\treply.Truncate(size)\n\tif want, got := size, reply.Len(); want < got {\n\t\tt.Errorf(\"message length should be below %d bytes, got %d bytes\", want, got)\n\t}\n\tif !reply.Truncated {\n\t\tt.Errorf(\"truncated bit should be set\")\n\t}\n\topt := reply.Extra[len(reply.Extra)-1]\n\tif opt.Header().Rrtype != TypeOPT {\n\t\tt.Errorf(\"expected last RR to be OPT\")\n\t}\n}\n\nfunc TestTruncation(t *testing.T) {\n\treply := new(Msg)\n\n\tfor i := 0; i < 61; i++ {\n\t\treply.Answer = append(reply.Answer, testRR(fmt.Sprintf(\"http.service.tcp.srv.k8s.example.org. 5 IN SRV 0 0 80 10-144-230-%d.default.pod.k8s.example.org.\", i)))\n\t}\n\n\tfor i := 0; i < 5; i++ {\n\t\treply.Extra = append(reply.Extra, testRR(fmt.Sprintf(\"ip-10-10-52-5%d.subdomain.example.org. 5 IN A 10.10.52.5%d\", i, i)))\n\t}\n\n\tfor i := 0; i < 5; i++ {\n\t\treply.Ns = append(reply.Ns, testRR(fmt.Sprintf(\"srv.subdomain.example.org. 5 IN NS ip-10-10-33-6%d.subdomain.example.org.\", i)))\n\t}\n\n\tfor bufsize := 1024; bufsize <= 4096; bufsize += 12 {\n\t\tm := new(Msg)\n\t\tm.SetQuestion(\"http.service.tcp.srv.k8s.example.org.\", TypeSRV)\n\t\tm.SetEdns0(uint16(bufsize), true)\n\n\t\tcopy := reply.Copy()\n\t\tcopy.SetReply(m)\n\n\t\tcopy.Truncate(bufsize)\n\t\tif want, got := bufsize, copy.Len(); want < got {\n\t\t\tt.Errorf(\"message length should be below %d bytes, got %d bytes\", want, got)\n\t\t}\n\t}\n}\n\nfunc TestRequestTruncateAnswerExact(t *testing.T) {\n\tconst size = 867 // Bit fiddly, but this hits the rl == size break clause in Truncate, 52 RRs should remain.\n\n\tm := new(Msg)\n\tm.SetQuestion(\"large.example.com.\", TypeSRV)\n\tm.SetEdns0(size, false)\n\n\treply := new(Msg)\n\treply.SetReply(m)\n\tfor i := 1; i < 200; i++ {\n\t\treply.Answer = append(reply.Answer, testRR(fmt.Sprintf(\"large.example.com. 10 IN A 127.0.0.%d\", i)))\n\t}\n\n\treply.Truncate(size)\n\tif want, got := size, reply.Len(); want < got {\n\t\tt.Errorf(\"message length should be below %d bytes, got %d bytes\", want, got)\n\t}\n\tif expected := 52; len(reply.Answer) != expected {\n\t\tt.Errorf(\"wrong number of answers; expected %d, got %d\", expected, len(reply.Answer))\n\t}\n}\n\nfunc BenchmarkMsgTruncate(b *testing.B) {\n\tconst size = 2048\n\n\tm := new(Msg)\n\tm.SetQuestion(\"example.com.\", TypeA)\n\tm.SetEdns0(size, true)\n\n\treply := new(Msg)\n\treply.SetReply(m)\n\tfor i := 1; i < 33; i++ {\n\t\treply.Answer = append(reply.Answer, testRR(\n\t\t\tfmt.Sprintf(\"large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.\", i)))\n\t}\n\tfor i := 1; i < 33; i++ {\n\t\treply.Extra = append(reply.Extra, testRR(\n\t\t\tfmt.Sprintf(\"10-0-0-%d.default.pod.k8s.example.com. 10 IN A 10.0.0.%d\", i, i)))\n\t}\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tb.StopTimer()\n\t\tcopy := reply.Copy()\n\t\tb.StartTimer()\n\n\t\tcopy.Truncate(size)\n\t}\n}\n"
  },
  {
    "path": "nsecx.go",
    "content": "package dns\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"strings\"\n)\n\n// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase.\nfunc HashName(label string, ha uint8, iter uint16, salt string) string {\n\tif ha != SHA1 {\n\t\treturn \"\"\n\t}\n\n\twireSalt := make([]byte, hex.DecodedLen(len(salt)))\n\tn, err := packStringHex(salt, wireSalt, 0)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\twireSalt = wireSalt[:n]\n\n\tname := make([]byte, 255)\n\toff, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tname = name[:off]\n\n\ts := sha1.New()\n\t// k = 0\n\ts.Write(name)\n\ts.Write(wireSalt)\n\tnsec3 := s.Sum(nil)\n\n\t// k > 0\n\tfor k := uint16(0); k < iter; k++ {\n\t\ts.Reset()\n\t\ts.Write(nsec3)\n\t\ts.Write(wireSalt)\n\t\tnsec3 = s.Sum(nsec3[:0])\n\t}\n\n\treturn toBase32(nsec3)\n}\n\n// Cover returns true if a name is covered by the NSEC3 record.\nfunc (rr *NSEC3) Cover(name string) bool {\n\tnameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)\n\towner := strings.ToUpper(rr.Hdr.Name)\n\tlabelIndices := Split(owner)\n\tif len(labelIndices) < 2 {\n\t\treturn false\n\t}\n\townerHash := owner[:labelIndices[1]-1]\n\townerZone := owner[labelIndices[1]:]\n\tif !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone\n\t\treturn false\n\t}\n\n\tnextHash := rr.NextDomain\n\n\t// if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash\n\tif ownerHash == nextHash && nameHash != ownerHash { // empty interval\n\t\treturn true\n\t}\n\tif ownerHash > nextHash { // end of zone\n\t\tif nameHash > ownerHash { // covered since there is nothing after ownerHash\n\t\t\treturn true\n\t\t}\n\t\treturn nameHash < nextHash // if nameHash is before beginning of zone it is covered\n\t}\n\tif nameHash < ownerHash { // nameHash is before ownerHash, not covered\n\t\treturn false\n\t}\n\treturn nameHash < nextHash // if nameHash is before nextHash is it covered (between ownerHash and nextHash)\n}\n\n// Match returns true if a name matches the NSEC3 record\nfunc (rr *NSEC3) Match(name string) bool {\n\tnameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)\n\towner := strings.ToUpper(rr.Hdr.Name)\n\tlabelIndices := Split(owner)\n\tif len(labelIndices) < 2 {\n\t\treturn false\n\t}\n\townerHash := owner[:labelIndices[1]-1]\n\townerZone := owner[labelIndices[1]:]\n\tif !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone\n\t\treturn false\n\t}\n\tif ownerHash == nameHash {\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "nsecx_test.go",
    "content": "package dns\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc TestPackNsec3(t *testing.T) {\n\tnsec3 := HashName(\"dnsex.nl.\", SHA1, 0, \"DEAD\")\n\tif nsec3 != \"ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J\" {\n\t\tt.Error(nsec3)\n\t}\n\n\tnsec3 = HashName(\"a.b.c.example.org.\", SHA1, 2, \"DEAD\")\n\tif nsec3 != \"6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG\" {\n\t\tt.Error(nsec3)\n\t}\n}\n\nfunc TestNsec3(t *testing.T) {\n\tnsec3 := testRR(\"sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM\")\n\tif !nsec3.(*NSEC3).Match(\"nl.\") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r\n\t\tt.Fatal(\"sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.\")\n\t}\n\tif !nsec3.(*NSEC3).Match(\"NL.\") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r\n\t\tt.Fatal(\"sk4e8fj94u78smusb40o1n0oltbblu2r.NL. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.\")\n\t}\n\tif nsec3.(*NSEC3).Match(\"com.\") { //\n\t\tt.Fatal(\"com. is not in the zone nl.\")\n\t}\n\tif nsec3.(*NSEC3).Match(\"test.nl.\") { // name hash = gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q\n\t\tt.Fatal(\"gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q.nl. should not match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.\")\n\t}\n\tnsec3 = testRR(\"nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM\")\n\tif nsec3.(*NSEC3).Match(\"nl.\") {\n\t\tt.Fatal(\"sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should not match a record without a owner hash\")\n\t}\n\n\tfor _, tc := range []struct {\n\t\trr     *NSEC3\n\t\tname   string\n\t\tcovers bool\n\t}{\n\t\t// positive tests\n\t\t{ // name hash between owner hash and next hash\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"2N1TB3VAIRUOBL6RKDVII42N9TFMIALP.com.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"PT3RON8N7PM3A0OE989IB84OOSADP7O8\",\n\t\t\t},\n\t\t\tname:   \"bsd.com.\",\n\t\t\tcovers: true,\n\t\t},\n\t\t{ // end of zone, name hash is after owner hash\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"3v62ulr0nre83v0rja2vjgtlif9v6rab.com.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"2N1TB3VAIRUOBL6RKDVII42N9TFMIALP\",\n\t\t\t},\n\t\t\tname:   \"csd.com.\",\n\t\t\tcovers: true,\n\t\t},\n\t\t{ // end of zone, name hash is before beginning of zone\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"PT3RON8N7PM3A0OE989IB84OOSADP7O8.com.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB\",\n\t\t\t},\n\t\t\tname:   \"asd.com.\",\n\t\t\tcovers: true,\n\t\t},\n\t\t// negative tests\n\t\t{ // too short owner name\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"nl.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"39P99DCGG0MDLARTCRMCF6OFLLUL7PR6\",\n\t\t\t},\n\t\t\tname:   \"asd.com.\",\n\t\t\tcovers: false,\n\t\t},\n\t\t{ // outside of zone\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"39p91242oslggest5e6a7cci4iaeqvnk.nl.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"39P99DCGG0MDLARTCRMCF6OFLLUL7PR6\",\n\t\t\t},\n\t\t\tname:   \"asd.com.\",\n\t\t\tcovers: false,\n\t\t},\n\t\t{ // empty interval\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"2n1tb3vairuobl6rkdvii42n9tfmialp.com.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"2N1TB3VAIRUOBL6RKDVII42N9TFMIALP\",\n\t\t\t},\n\t\t\tname:   \"asd.com.\",\n\t\t\tcovers: false,\n\t\t},\n\t\t{ // empty interval wildcard\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"2n1tb3vairuobl6rkdvii42n9tfmialp.com.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"2N1TB3VAIRUOBL6RKDVII42N9TFMIALP\",\n\t\t\t},\n\t\t\tname:   \"*.asd.com.\",\n\t\t\tcovers: true,\n\t\t},\n\t\t{ // name hash is before owner hash, not covered\n\t\t\trr: &NSEC3{\n\t\t\t\tHdr:        RR_Header{Name: \"3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB.com.\"},\n\t\t\t\tHash:       1,\n\t\t\t\tFlags:      1,\n\t\t\t\tIterations: 5,\n\t\t\t\tSalt:       \"F10E9F7EA83FC8F3\",\n\t\t\t\tNextDomain: \"PT3RON8N7PM3A0OE989IB84OOSADP7O8\",\n\t\t\t},\n\t\t\tname:   \"asd.com.\",\n\t\t\tcovers: false,\n\t\t},\n\t} {\n\t\tcovers := tc.rr.Cover(tc.name)\n\t\tif tc.covers != covers {\n\t\t\tt.Fatalf(\"cover failed for %s: expected %t, got %t [record: %s]\", tc.name, tc.covers, covers, tc.rr)\n\t\t}\n\t}\n}\n\nfunc TestNsec3EmptySalt(t *testing.T) {\n\trr, _ := NewRR(\"CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A  NS SOA RRSIG DNSKEY NSEC3PARAM\")\n\n\tif !rr.(*NSEC3).Match(\"com.\") {\n\t\tt.Fatalf(\"expected record to match com. label\")\n\t}\n}\n\nfunc BenchmarkHashName(b *testing.B) {\n\tfor _, iter := range []uint16{\n\t\t150, 2500, 5000, 10000, ^uint16(0),\n\t} {\n\t\tb.Run(strconv.Itoa(int(iter)), func(b *testing.B) {\n\t\t\tfor n := 0; n < b.N; n++ {\n\t\t\t\tif HashName(\"some.example.org.\", SHA1, iter, \"deadbeef\") == \"\" {\n\t\t\t\t\tb.Fatalf(\"HashName failed\")\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "parse_test.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"crypto/rsa\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"testing/quick\"\n)\n\nfunc TestDotInName(t *testing.T) {\n\tbuf := make([]byte, 20)\n\tPackDomainName(\"aa\\\\.bb.nl.\", buf, 0, nil, false)\n\t// index 3 must be a real dot\n\tif buf[3] != '.' {\n\t\tt.Error(\"dot should be a real dot\")\n\t}\n\n\tif buf[6] != 2 {\n\t\tt.Error(\"this must have the value 2\")\n\t}\n\tdom, _, _ := UnpackDomainName(buf, 0)\n\t// printing it should yield the backspace again\n\tif dom != \"aa\\\\.bb.nl.\" {\n\t\tt.Error(\"dot should have been escaped: \", dom)\n\t}\n}\n\nfunc TestDotLastInLabel(t *testing.T) {\n\tsample := \"aa\\\\..au.\"\n\tbuf := make([]byte, 20)\n\t_, err := PackDomainName(sample, buf, 0, nil, false)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error packing domain: %v\", err)\n\t}\n\tdom, _, _ := UnpackDomainName(buf, 0)\n\tif dom != sample {\n\t\tt.Fatalf(\"unpacked domain `%s' doesn't match packed domain\", dom)\n\t}\n}\n\nfunc TestTooLongDomainName(t *testing.T) {\n\tl := \"aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt.\"\n\tdom := l + l + l + l + l + l + l\n\t_, err := NewRR(dom + \" IN A 127.0.0.1\")\n\tif err == nil {\n\t\tt.Error(\"should be too long\")\n\t}\n\t_, err = NewRR(\"..com. IN A 127.0.0.1\")\n\tif err == nil {\n\t\tt.Error(\"should fail\")\n\t}\n}\n\nfunc TestDomainName(t *testing.T) {\n\ttests := []string{\"r\\\\.gieben.miek.nl.\", \"www\\\\.www.miek.nl.\",\n\t\t\"www.*.miek.nl.\", \"www.*.miek.nl.\",\n\t}\n\tdbuff := make([]byte, 40)\n\n\tfor _, ts := range tests {\n\t\tif _, err := PackDomainName(ts, dbuff, 0, nil, false); err != nil {\n\t\t\tt.Error(\"not a valid domain name\")\n\t\t\tcontinue\n\t\t}\n\t\tn, _, err := UnpackDomainName(dbuff, 0)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to unpack packed domain name\")\n\t\t\tcontinue\n\t\t}\n\t\tif ts != n {\n\t\t\tt.Errorf(\"must be equal: in: %s, out: %s\", ts, n)\n\t\t}\n\t}\n}\n\nfunc TestDomainNameAndTXTEscapes(t *testing.T) {\n\ttests := []byte{'.', '(', ')', ';', ' ', '@', '\"', '\\\\', 9, 13, 10, 0, 255}\n\tfor _, b := range tests {\n\t\trrbytes := []byte{\n\t\t\t1, b, 0, // owner\n\t\t\tbyte(TypeTXT >> 8), byte(TypeTXT),\n\t\t\tbyte(ClassINET >> 8), byte(ClassINET),\n\t\t\t0, 0, 0, 1, // TTL\n\t\t\t0, 2, 1, b, // Data\n\t\t}\n\t\trr1, _, err := UnpackRR(rrbytes, 0)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\ts := rr1.String()\n\t\trr2, err := NewRR(s)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error parsing unpacked RR's string: %v\", err)\n\t\t}\n\t\trepacked := make([]byte, len(rrbytes))\n\t\tif _, err := PackRR(rr2, repacked, 0, nil, false); err != nil {\n\t\t\tt.Errorf(\"error packing parsed RR: %v\", err)\n\t\t}\n\t\tif !bytes.Equal(repacked, rrbytes) {\n\t\t\tt.Error(\"packed bytes don't match original bytes\")\n\t\t}\n\t}\n}\n\nfunc TestTXTEscapeParsing(t *testing.T) {\n\ttest := [][]string{\n\t\t{`\";\"`, `\";\"`},\n\t\t{`\\;`, `\";\"`},\n\t\t{`\"\\t\"`, `\"t\"`},\n\t\t{`\"\\r\"`, `\"r\"`},\n\t\t{`\"\\ \"`, `\" \"`},\n\t\t{`\"\\;\"`, `\";\"`},\n\t\t{`\"\\;\\\"\"`, `\";\\\"\"`},\n\t\t{`\"\\(a\\)\"`, `\"(a)\"`},\n\t\t{`\"\\(a)\"`, `\"(a)\"`},\n\t\t{`\"(a\\)\"`, `\"(a)\"`},\n\t\t{`\"(a)\"`, `\"(a)\"`},\n\t\t{`\"\\048\"`, `\"0\"`},\n\t\t{`\"\\` + \"\\t\" + `\"`, `\"\\009\"`},\n\t\t{`\"\\` + \"\\n\" + `\"`, `\"\\010\"`},\n\t\t{`\"\\` + \"\\r\" + `\"`, `\"\\013\"`},\n\t\t{`\"\\` + \"\\x11\" + `\"`, `\"\\017\"`},\n\t\t{`\"\\'\"`, `\"'\"`},\n\t}\n\tfor _, s := range test {\n\t\trr, err := NewRR(fmt.Sprintf(\"example.com. IN TXT %v\", s[0]))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"could not parse %v TXT: %s\", s[0], err)\n\t\t\tcontinue\n\t\t}\n\n\t\ttxt := sprintTxt(rr.(*TXT).Txt)\n\t\tif txt != s[1] {\n\t\t\tt.Errorf(\"mismatch after parsing `%v` TXT record: `%v` != `%v`\", s[0], txt, s[1])\n\t\t}\n\t}\n}\n\nfunc GenerateDomain(r *rand.Rand, size int) []byte {\n\tdnLen := size % 70 // artificially limit size so there's less to interpret if a failure occurs\n\tvar dn []byte\n\tdone := false\n\tfor i := 0; i < dnLen && !done; {\n\t\tmax := dnLen - i\n\t\tif max > 63 {\n\t\t\tmax = 63\n\t\t}\n\t\tlLen := max\n\t\tif lLen != 0 {\n\t\t\tlLen = int(r.Int31()) % max\n\t\t}\n\t\tdone = lLen == 0\n\t\tif done {\n\t\t\tcontinue\n\t\t}\n\t\tl := make([]byte, lLen+1)\n\t\tl[0] = byte(lLen)\n\t\tfor j := 0; j < lLen; j++ {\n\t\t\tl[j+1] = byte(rand.Int31())\n\t\t}\n\t\tdn = append(dn, l...)\n\t\ti += 1 + lLen\n\t}\n\treturn append(dn, 0)\n}\n\nfunc TestDomainQuick(t *testing.T) {\n\tr := rand.New(rand.NewSource(0))\n\tf := func(l int) bool {\n\t\tdb := GenerateDomain(r, l)\n\t\tds, _, err := UnpackDomainName(db, 0)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tbuf := make([]byte, 255)\n\t\toff, err := PackDomainName(ds, buf, 0, nil, false)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error packing domain: %v\", err)\n\t\t\tt.Errorf(\" bytes: %v\", db)\n\t\t\tt.Errorf(\"string: %v\", ds)\n\t\t\treturn false\n\t\t}\n\t\tif !bytes.Equal(db, buf[:off]) {\n\t\t\tt.Errorf(\"repacked domain doesn't match original:\")\n\t\t\tt.Errorf(\"src bytes: %v\", db)\n\t\t\tt.Errorf(\"   string: %v\", ds)\n\t\t\tt.Errorf(\"out bytes: %v\", buf[:off])\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\tif err := quick.Check(f, nil); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc GenerateTXT(r *rand.Rand, size int) []byte {\n\trdLen := size % 300 // artificially limit size so there's less to interpret if a failure occurs\n\tvar rd []byte\n\tfor i := 0; i < rdLen; {\n\t\tmax := rdLen - 1\n\t\tif max > 255 {\n\t\t\tmax = 255\n\t\t}\n\t\tsLen := max\n\t\tif max != 0 {\n\t\t\tsLen = int(r.Int31()) % max\n\t\t}\n\t\ts := make([]byte, sLen+1)\n\t\ts[0] = byte(sLen)\n\t\tfor j := 0; j < sLen; j++ {\n\t\t\ts[j+1] = byte(rand.Int31())\n\t\t}\n\t\trd = append(rd, s...)\n\t\ti += 1 + sLen\n\t}\n\treturn rd\n}\n\nfunc TestParseDirectiveMisc(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"$ORIGIN miek.nl.\\na IN NS b\": \"a.miek.nl.\\t3600\\tIN\\tNS\\tb.miek.nl.\",\n\t\t\"$TTL 2H\\nmiek.nl. IN NS b.\":  \"miek.nl.\\t7200\\tIN\\tNS\\tb.\",\n\t\t\"miek.nl. 1D IN NS b.\":        \"miek.nl.\\t86400\\tIN\\tNS\\tb.\",\n\t\t`name. IN SOA  a6.nstld.com. hostmaster.nic.name. (\n        203362132 ; serial\n        5m        ; refresh (5 minutes)\n        5m        ; retry (5 minutes)\n        2w        ; expire (2 weeks)\n        300       ; minimum (5 minutes)\n)`: \"name.\\t3600\\tIN\\tSOA\\ta6.nstld.com. hostmaster.nic.name. 203362132 300 300 1209600 300\",\n\t\t\". 3600000  IN  NS ONE.MY-ROOTS.NET.\":        \".\\t3600000\\tIN\\tNS\\tONE.MY-ROOTS.NET.\",\n\t\t\"ONE.MY-ROOTS.NET. 3600000 IN A 192.168.1.1\": \"ONE.MY-ROOTS.NET.\\t3600000\\tIN\\tA\\t192.168.1.1\",\n\t}\n\tfor i, o := range tests {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestNSEC(t *testing.T) {\n\tnsectests := map[string]string{\n\t\t\"nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F\": \"nl.\\t3600\\tIN\\tNSEC3PARAM\\t1 0 5 30923C44C6CBBB8F\",\n\t\t\"p2209hipbpnm681knjnu0m1febshlv4e.nl. IN NSEC3 1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM\": \"p2209hipbpnm681knjnu0m1febshlv4e.nl.\\t3600\\tIN\\tNSEC3\\t1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM\",\n\t\t\"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC\":                                                                                 \"localhost.dnssex.nl.\\t3600\\tIN\\tNSEC\\twww.dnssex.nl. A RRSIG NSEC\",\n\t\t\"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC TYPE65534\":                                                                       \"localhost.dnssex.nl.\\t3600\\tIN\\tNSEC\\twww.dnssex.nl. A RRSIG NSEC TYPE65534\",\n\t\t\"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSec Type65534\":                                                                       \"localhost.dnssex.nl.\\t3600\\tIN\\tNSEC\\twww.dnssex.nl. A RRSIG NSEC TYPE65534\",\n\t\t\"44ohaq2njb0idnvolt9ggthvsk1e1uv8.skydns.test. NSEC3 1 0 0 - 44OHAQ2NJB0IDNVOLT9GGTHVSK1E1UVA\":                                             \"44ohaq2njb0idnvolt9ggthvsk1e1uv8.skydns.test.\\t3600\\tIN\\tNSEC3\\t1 0 0 - 44OHAQ2NJB0IDNVOLT9GGTHVSK1E1UVA\",\n\t}\n\tfor i, o := range nsectests {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n\trr, err := NewRR(\"nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F\")\n\tif err != nil {\n\t\tt.Fatal(\"failed to parse RR: \", err)\n\t}\n\tif nsec3param, ok := rr.(*NSEC3PARAM); ok {\n\t\tif nsec3param.SaltLength != 8 {\n\t\t\tt.Fatalf(\"nsec3param saltlen %d != 8\", nsec3param.SaltLength)\n\t\t}\n\t} else {\n\t\tt.Fatal(\"not nsec3 param: \", err)\n\t}\n}\n\nfunc TestParseLOC(t *testing.T) {\n\tlt := map[string]string{\n\t\t\"SW1A2AA.find.me.uk.\tLOC\t51 30 12.748 N 00 07 39.611 W 0.00m 0.00m 0.00m 0.00m\": \"SW1A2AA.find.me.uk.\\t3600\\tIN\\tLOC\\t51 30 12.748 N 00 07 39.611 W 0m 0.00m 0.00m 0.00m\",\n\t\t\"SW1A2AA.find.me.uk.\tLOC\t51 0 0.0 N 00 07 39.611 W 0.00m 0.00m 0.00m 0.00m\":     \"SW1A2AA.find.me.uk.\\t3600\\tIN\\tLOC\\t51 00 0.000 N 00 07 39.611 W 0m 0.00m 0.00m 0.00m\",\n\t\t\"SW1A2AA.find.me.uk.\tLOC\t51 30 12.748 N 00 07 39.611 W 0.00m\":                   \"SW1A2AA.find.me.uk.\\t3600\\tIN\\tLOC\\t51 30 12.748 N 00 07 39.611 W 0m 1m 10000m 10m\",\n\t\t// Exercise boundary cases\n\t\t\"SW1A2AA.find.me.uk.\tLOC\t90 0 0.0 N 180 0 0.0 W 42849672.95 90000000.00m 90000000.00m 90000000.00m\":  \"SW1A2AA.find.me.uk.\\t3600\\tIN\\tLOC\\t90 00 0.000 N 180 00 0.000 W 42849672.95m 90000000m 90000000m 90000000m\",\n\t\t\"SW1A2AA.find.me.uk.\tLOC\t89 59 59.999 N 179 59 59.999 W -100000 90000000.00m 90000000.00m 90000000m\": \"SW1A2AA.find.me.uk.\\t3600\\tIN\\tLOC\\t89 59 59.999 N 179 59 59.999 W -100000m 90000000m 90000000m 90000000m\",\n\t\t// use float64 to have enough precision.\n\t\t\"example.com. LOC 42 21 43.952 N 71 5 6.344 W -24m 1m 200m 10m\": \"example.com.\\t3600\\tIN\\tLOC\\t42 21 43.952 N 71 05 6.344 W -24m 1m 200m 10m\",\n\t}\n\tfor i, o := range lt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n\n\t// Invalid cases (out of range values)\n\tlt = map[string]string{ // Pair of (invalid) RDATA and the bad field name\n\t\t// One of the subfields is out of range.\n\t\t\"91 0 0.0 N 00 07 39.611 W 0m\":   \"Latitude\",\n\t\t\"89 60 0.0 N 00 07 39.611 W 0m\":  \"Latitude\",\n\t\t\"89 00 60.0 N 00 07 39.611 W 0m\": \"Latitude\",\n\t\t\"1 00 -1 N 00 07 39.611 W 0m\":    \"Latitude\",\n\t\t\"0 0 0.0 N 181 00 0.0 W 0m\":      \"Longitude\",\n\t\t\"0 0 0.0 N 179 60 0.0 W 0m\":      \"Longitude\",\n\t\t\"0 0 0.0 N 179 00 60.0 W 0m\":     \"Longitude\",\n\t\t\"0 0 0.0 N 1 00 -1 W 0m\":         \"Longitude\",\n\n\t\t// Each subfield is valid, but resulting latitude would be out of range.\n\t\t\"90 01 00.0 N 00 07 39.611 W 0m\": \"Latitude\",\n\t\t\"0 0 0.0 N 180 01 0.0 W 0m\":      \"Longitude\",\n\t}\n\tfor rdata, field := range lt {\n\t\t_, err := NewRR(fmt.Sprintf(\"example.com. LOC %s\", rdata))\n\t\tif err == nil || !strings.Contains(err.Error(), field) {\n\t\t\tt.Errorf(\"expected error to contain %q, but got %v\", field, err)\n\t\t}\n\t}\n}\n\n// this tests a subroutine for the LOC RR parser.  It's complicated enough to test separately.\nfunc TestStringToCm(t *testing.T) {\n\ttests := []struct {\n\t\t// Test description: the input token and the expected return values from stringToCm.\n\t\ttoken string\n\t\te     uint8\n\t\tm     uint8\n\t\tok    bool\n\t}{\n\t\t{\"100\", 4, 1, true},\n\t\t{\"0100\", 4, 1, true}, // leading 0 (allowed)\n\t\t{\"100.99\", 4, 1, true},\n\t\t{\"90000000\", 9, 9, true},\n\t\t{\"90000000.00\", 9, 9, true},\n\t\t{\"0\", 0, 0, true},\n\t\t{\"0.00\", 0, 0, true},\n\t\t{\"0.01\", 0, 1, true},\n\t\t{\".01\", 0, 1, true}, // empty 'meter' part (allowed)\n\t\t{\"0.1\", 1, 1, true},\n\n\t\t// out of range (too large)\n\t\t{\"90000001\", 0, 0, false},\n\t\t{\"90000000.01\", 0, 0, false},\n\n\t\t// more than 2 digits in 'cmeter' part\n\t\t{\"0.000\", 0, 0, false},\n\t\t{\"0.001\", 0, 0, false},\n\t\t{\"0.999\", 0, 0, false},\n\t\t// with plus or minus sign (disallowed)\n\t\t{\"-100\", 0, 0, false},\n\t\t{\"+100\", 0, 0, false},\n\t\t{\"0.-10\", 0, 0, false},\n\t\t{\"0.+10\", 0, 0, false},\n\t\t{\"0a.00\", 0, 0, false}, // invalid string for 'meter' part\n\t\t{\".1x\", 0, 0, false},   // invalid string for 'cmeter' part\n\t\t{\".\", 0, 0, false},     // empty 'cmeter' part (disallowed)\n\t\t{\"1.\", 0, 0, false},    // ditto\n\t\t{\"m\", 0, 0, false},     // only the \"m\" suffix\n\t}\n\tfor _, tc := range tests {\n\t\ttc := tc\n\t\tt.Run(tc.token, func(t *testing.T) {\n\t\t\t// In all cases the expected result is the same with or without the 'm' suffix.\n\t\t\t// So we test both cases using the same test code.\n\t\t\tfor _, sfx := range []string{\"\", \"m\"} {\n\t\t\t\ttoken := tc.token + sfx\n\t\t\t\te, m, ok := stringToCm(token)\n\t\t\t\tif ok != tc.ok {\n\t\t\t\t\tt.Fatal(\"unexpected validation result\")\n\t\t\t\t}\n\t\t\t\tif m != tc.m {\n\t\t\t\t\tt.Fatalf(\"Expected %d, got %d\", tc.m, m)\n\t\t\t\t}\n\t\t\t\tif e != tc.e {\n\t\t\t\t\tt.Fatalf(\"Expected %d, got %d\", tc.e, e)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseDS(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"example.net. 3600 IN DS 40692 12 3 22261A8B0E0D799183E35E24E2AD6BB58533CBA7E3B14D659E9CA09B 2071398F\": \"example.net.\\t3600\\tIN\\tDS\\t40692 12 3 22261A8B0E0D799183E35E24E2AD6BB58533CBA7E3B14D659E9CA09B2071398F\",\n\t}\n\tfor i, o := range dt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestQuotes(t *testing.T) {\n\ttests := map[string]string{\n\t\t`t.example.com. IN TXT \"a bc\"`: \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"a bc\\\"\",\n\t\t`t.example.com. IN TXT \"a\n bc\"`: \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"a\\\\010 bc\\\"\",\n\t\t`t.example.com. IN TXT \"\"`:              \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"\\\"\",\n\t\t`t.example.com. IN TXT \"a\"`:             \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"a\\\"\",\n\t\t`t.example.com. IN TXT \"aa\"`:            \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"aa\\\"\",\n\t\t`t.example.com. IN TXT \"aaa\" ;`:         \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"aaa\\\"\",\n\t\t`t.example.com. IN TXT \"abc\" \"DEF\"`:     \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"abc\\\" \\\"DEF\\\"\",\n\t\t`t.example.com. IN TXT \"abc\" ( \"DEF\" )`: \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"abc\\\" \\\"DEF\\\"\",\n\t\t`t.example.com. IN TXT aaa ;`:           \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"aaa\\\"\",\n\t\t`t.example.com. IN TXT aaa aaa;`:        \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"aaa\\\" \\\"aaa\\\"\",\n\t\t`t.example.com. IN TXT aaa aaa`:         \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"aaa\\\" \\\"aaa\\\"\",\n\t\t`t.example.com. IN TXT aaa`:             \"t.example.com.\\t3600\\tIN\\tTXT\\t\\\"aaa\\\"\",\n\t\t\"cid.urn.arpa. NAPTR 100 50 \\\"s\\\" \\\"z3950+I2L+I2C\\\"    \\\"\\\" _z3950._tcp.gatech.edu.\": \"cid.urn.arpa.\\t3600\\tIN\\tNAPTR\\t100 50 \\\"s\\\" \\\"z3950+I2L+I2C\\\" \\\"\\\" _z3950._tcp.gatech.edu.\",\n\t\t\"cid.urn.arpa. NAPTR 100 50 \\\"s\\\" \\\"rcds+I2C\\\"         \\\"\\\" _rcds._udp.gatech.edu.\":  \"cid.urn.arpa.\\t3600\\tIN\\tNAPTR\\t100 50 \\\"s\\\" \\\"rcds+I2C\\\" \\\"\\\" _rcds._udp.gatech.edu.\",\n\t\t\"cid.urn.arpa. NAPTR 100 50 \\\"s\\\" \\\"http+I2L+I2C+I2R\\\" \\\"\\\" _http._tcp.gatech.edu.\":  \"cid.urn.arpa.\\t3600\\tIN\\tNAPTR\\t100 50 \\\"s\\\" \\\"http+I2L+I2C+I2R\\\" \\\"\\\" _http._tcp.gatech.edu.\",\n\t\t\"cid.urn.arpa. NAPTR 100 10 \\\"\\\" \\\"\\\" \\\"/urn:cid:.+@([^\\\\.]+\\\\.)(.*)$/\\\\2/i\\\" .\":     \"cid.urn.arpa.\\t3600\\tIN\\tNAPTR\\t100 10 \\\"\\\" \\\"\\\" \\\"/urn:cid:.+@([^\\\\.]+\\\\.)(.*)$/\\\\2/i\\\" .\",\n\t}\n\tfor i, o := range tests {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is\\n`%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseClass(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"t.example.com. IN A 127.0.0.1\": \"t.example.com.\t3600\tIN\tA\t127.0.0.1\",\n\t\t\"t.example.com. CS A 127.0.0.1\": \"t.example.com.\t3600\tCS\tA\t127.0.0.1\",\n\t\t\"t.example.com. CH A 127.0.0.1\": \"t.example.com.\t3600\tCH\tA\t127.0.0.1\",\n\t\t// ClassANY can not occur in zone files\n\t\t// \"t.example.com. ANY A 127.0.0.1\": \"t.example.com.\t3600\tANY\tA\t127.0.0.1\",\n\t\t\"t.example.com. NONE A 127.0.0.1\":     \"t.example.com.\t3600\tNONE\tA\t127.0.0.1\",\n\t\t\"t.example.com. CLASS255 A 127.0.0.1\": \"t.example.com.\t3600\tCLASS255\tA\t127.0.0.1\",\n\t}\n\tfor i, o := range tests {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is\\n`%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestBrace(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"(miek.nl.) 3600 IN A 127.0.1.1\":                 \"miek.nl.\\t3600\\tIN\\tA\\t127.0.1.1\",\n\t\t\"miek.nl. (3600) IN MX (10) elektron.atoom.net.\": \"miek.nl.\\t3600\\tIN\\tMX\\t10 elektron.atoom.net.\",\n\t\t`miek.nl. IN (\n                        3600 A 127.0.0.1)`: \"miek.nl.\\t3600\\tIN\\tA\\t127.0.0.1\",\n\t\t\"(miek.nl.) (A) (127.0.2.1)\":                          \"miek.nl.\\t3600\\tIN\\tA\\t127.0.2.1\",\n\t\t\"miek.nl A 127.0.3.1\":                                 \"miek.nl.\\t3600\\tIN\\tA\\t127.0.3.1\",\n\t\t\"_ssh._tcp.local. 60 IN (PTR) stora._ssh._tcp.local.\": \"_ssh._tcp.local.\\t60\\tIN\\tPTR\\tstora._ssh._tcp.local.\",\n\t\t\"miek.nl. NS ns.miek.nl\":                              \"miek.nl.\\t3600\\tIN\\tNS\\tns.miek.nl.\",\n\t\t`(miek.nl.) (\n                        (IN)\n                        (AAAA)\n                        (::1) )`: \"miek.nl.\\t3600\\tIN\\tAAAA\\t::1\",\n\t\t`(miek.nl.) (\n                        (IN)\n                        (AAAA)\n                        (::1))`: \"miek.nl.\\t3600\\tIN\\tAAAA\\t::1\",\n\t\t\"miek.nl. IN AAAA ::2\": \"miek.nl.\\t3600\\tIN\\tAAAA\\t::2\",\n\t\t`((m)(i)ek.(n)l.) (SOA) (soa.) (soa.) (\n                                2009032802 ; serial\n                                21600      ; refresh (6 hours)\n                                7(2)00       ; retry (2 hours)\n                                604()800     ; expire (1 week)\n                                3600       ; minimum (1 hour)\n                        )`: \"miek.nl.\\t3600\\tIN\\tSOA\\tsoa. soa. 2009032802 21600 7200 604800 3600\",\n\t\t\"miek\\\\.nl. IN A 127.0.0.10\": \"miek\\\\.nl.\\t3600\\tIN\\tA\\t127.0.0.10\",\n\t\t\"miek.nl. IN A 127.0.0.11\":   \"miek.nl.\\t3600\\tIN\\tA\\t127.0.0.11\",\n\t\t\"miek.nl. A 127.0.0.12\":      \"miek.nl.\\t3600\\tIN\\tA\\t127.0.0.12\",\n\t\t`miek.nl.       86400 IN SOA elektron.atoom.net. miekg.atoom.net. (\n                                2009032802 ; serial\n                                21600      ; refresh (6 hours)\n                                7200       ; retry (2 hours)\n                                604800     ; expire (1 week)\n                                3600       ; minimum (1 hour)\n                        )`: \"miek.nl.\\t86400\\tIN\\tSOA\\telektron.atoom.net. miekg.atoom.net. 2009032802 21600 7200 604800 3600\",\n\t}\n\tfor i, o := range tests {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to parse RR: %v\\n\\t%s\", err, i)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseFailure(t *testing.T) {\n\ttests := []string{\"miek.nl. IN A 327.0.0.1\",\n\t\t\"miek.nl. IN AAAA ::x\",\n\t\t\"miek.nl. IN MX a0 miek.nl.\",\n\t\t\"miek.nl aap IN MX mx.miek.nl.\",\n\t\t\"miek.nl 200 IN mxx 10 mx.miek.nl.\",\n\t\t\"miek.nl. inn MX 10 mx.miek.nl.\",\n\t\t// \"miek.nl. IN CNAME \", // actually valid nowadays, zero size rdata\n\t\t\"miek.nl. IN CNAME ..\",\n\t\t\"miek.nl. PA MX 10 miek.nl.\",\n\t\t\"miek.nl. ) IN MX 10 miek.nl.\",\n\t}\n\n\tfor _, s := range tests {\n\t\t_, err := NewRR(s)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"should have triggered an error: \\\"%s\\\"\", s)\n\t\t}\n\t}\n}\n\nfunc TestOmittedTTL(t *testing.T) {\n\tzone := `\n$ORIGIN example.com.\nexample.com. 42 IN SOA ns1.example.com. hostmaster.example.com. 1 86400 60 86400 3600 ; TTL=42 SOA\nexample.com.        NS 2 ; TTL=42 absolute owner name\n@                   MD 3 ; TTL=42 current-origin owner name\n                    MF 4 ; TTL=42 leading-space implied owner name\n\t43 TYPE65280 \\# 1 05 ; TTL=43 implied owner name explicit TTL\n\t          MB 6       ; TTL=43 leading-tab implied owner name\n$TTL 1337\nexample.com. 88 MG 7 ; TTL=88 explicit TTL\nexample.com.    MR 8 ; TTL=1337 after first $TTL\n$TTL 314\n             1 TXT 9 ; TTL=1 implied owner name explicit TTL\nexample.com.   DNAME 10 ; TTL=314 after second $TTL\n`\n\treCaseFromComment := regexp.MustCompile(`TTL=(\\d+)\\s+(.*)`)\n\tz := NewZoneParser(strings.NewReader(zone), \"\", \"\")\n\tvar i int\n\n\tfor rr, ok := z.Next(); ok; rr, ok = z.Next() {\n\t\ti++\n\t\texpected := reCaseFromComment.FindStringSubmatch(z.Comment())\n\t\tif len(expected) != 3 {\n\t\t\tt.Errorf(\"regexp didn't match for record %d\", i)\n\t\t\tcontinue\n\t\t}\n\t\texpectedTTL, _ := strconv.ParseUint(expected[1], 10, 32)\n\t\tttl := rr.Header().Ttl\n\t\tif ttl != uint32(expectedTTL) {\n\t\t\tt.Errorf(\"%s: expected TTL %d, got %d\", expected[2], expectedTTL, ttl)\n\t\t}\n\t}\n\tif err := z.Err(); err != nil {\n\t\tt.Error(err)\n\t}\n\tif i != 10 {\n\t\tt.Errorf(\"expected %d records, got %d\", 5, i)\n\t}\n}\n\nfunc TestRelativeNameErrors(t *testing.T) {\n\tvar badZones = []struct {\n\t\tlabel        string\n\t\tzoneContents string\n\t\texpectedErr  string\n\t}{\n\t\t{\n\t\t\t\"relative owner name without origin\",\n\t\t\t\"example.com 3600 IN SOA ns.example.com. hostmaster.example.com. 1 86400 60 86400 3600\",\n\t\t\t\"bad owner name\",\n\t\t},\n\t\t{\n\t\t\t\"relative owner name in RDATA\",\n\t\t\t\"example.com. 3600 IN SOA ns hostmaster 1 86400 60 86400 3600\",\n\t\t\t\"bad SOA Ns\",\n\t\t},\n\t\t{\n\t\t\t\"origin reference without origin\",\n\t\t\t\"@ 3600 IN SOA ns.example.com. hostmaster.example.com. 1 86400 60 86400 3600\",\n\t\t\t\"bad owner name\",\n\t\t},\n\t\t{\n\t\t\t\"relative owner name in $INCLUDE\",\n\t\t\t\"$INCLUDE file.db example.com\",\n\t\t\t\"bad origin name\",\n\t\t},\n\t\t{\n\t\t\t\"relative owner name in $ORIGIN\",\n\t\t\t\"$ORIGIN example.com\",\n\t\t\t\"bad origin name\",\n\t\t},\n\t}\n\tfor _, errorCase := range badZones {\n\t\tz := NewZoneParser(strings.NewReader(errorCase.zoneContents), \"\", \"\")\n\t\tz.Next()\n\t\tif err := z.Err(); err == nil {\n\t\t\tt.Errorf(\"%s: expected error, got nil\", errorCase.label)\n\t\t} else if !strings.Contains(err.Error(), errorCase.expectedErr) {\n\t\t\tt.Errorf(\"%s: expected error `%s`, got `%s`\", errorCase.label, errorCase.expectedErr, err)\n\t\t}\n\t}\n}\n\nfunc TestHIP(t *testing.T) {\n\th := `www.example.com.      IN  HIP ( 2 200100107B1A74DF365639CC39F1D578\n                                AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p\n9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQ\nb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D\n                                rvs1.example.com.\n                                rvs2.example.com. )`\n\trr, err := NewRR(h)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse RR: %v\", err)\n\t}\n\tmsg := new(Msg)\n\tmsg.Answer = []RR{rr, rr}\n\tbytes, err := msg.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to pack msg: %v\", err)\n\t}\n\tif err := msg.Unpack(bytes); err != nil {\n\t\tt.Fatalf(\"failed to unpack msg: %v\", err)\n\t}\n\tif len(msg.Answer) != 2 {\n\t\tt.Fatalf(\"2 answers expected: %v\", msg)\n\t}\n\tfor i, rr := range msg.Answer {\n\t\trr := rr.(*HIP)\n\t\tif l := len(rr.RendezvousServers); l != 2 {\n\t\t\tt.Fatalf(\"2 servers expected, only %d in record %d:\\n%v\", l, i, msg)\n\t\t}\n\t\tfor j, s := range []string{\"rvs1.example.com.\", \"rvs2.example.com.\"} {\n\t\t\tif rr.RendezvousServers[j] != s {\n\t\t\t\tt.Fatalf(\"expected server %d of record %d to be %s:\\n%v\", j, i, s, msg)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Test with no known RR on the line\nfunc TestLineNumberError2(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"example.com. 1000 SO master.example.com. admin.example.com. 1 4294967294 4294967293 4294967295 100\": \"dns: expecting RR type or class, not this...: \\\"SO\\\" at line: 1:21\",\n\t\t\"example.com 1000 IN TALINK a.example.com. b..example.com.\":                                          \"dns: bad TALINK NextName: \\\"b..example.com.\\\" at line: 1:57\",\n\t\t\"example.com 1000 IN TALINK ( a.example.com. b..example.com. )\":                                      \"dns: bad TALINK NextName: \\\"b..example.com.\\\" at line: 1:60\",\n\t\t`example.com 1000 IN TALINK ( a.example.com.\n\tbb..example.com. )`: \"dns: bad TALINK NextName: \\\"bb..example.com.\\\" at line: 2:18\",\n\t\t// This is a bug, it should report an error on line 1, but the new is already processed.\n\t\t`example.com 1000 IN TALINK ( a.example.com.  b...example.com.\n\t)`: \"dns: bad TALINK NextName: \\\"b...example.com.\\\" at line: 2:1\"}\n\n\tfor in, errStr := range tests {\n\t\t_, err := NewRR(in)\n\t\tif err == nil {\n\t\t\tt.Error(\"err is nil\")\n\t\t} else {\n\t\t\tif err.Error() != errStr {\n\t\t\t\tt.Errorf(\"%s: error should be %s is %v\", in, errStr, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Test if the calculations are correct\nfunc TestRfc1982(t *testing.T) {\n\t// If the current time and the timestamp are more than 68 years apart\n\t// it means the date has wrapped. 0 is 1970\n\n\t// fall in the current 68 year span\n\tstrtests := []string{\"20120525134203\", \"19700101000000\", \"20380119031408\"}\n\tfor _, v := range strtests {\n\t\tif x, _ := StringToTime(v); v != TimeToString(x) {\n\t\t\tt.Errorf(\"1982 arithmetic string failure %s (%s:%d)\", v, TimeToString(x), x)\n\t\t}\n\t}\n\n\tinttests := map[uint32]string{0: \"19700101000000\",\n\t\t1 << 31:   \"20380119031408\",\n\t\t1<<32 - 1: \"21060207062815\",\n\t}\n\tfor i, v := range inttests {\n\t\tif TimeToString(i) != v {\n\t\t\tt.Errorf(\"1982 arithmetic int failure %d:%s (%s)\", i, v, TimeToString(i))\n\t\t}\n\t}\n\n\t// Future tests, these dates get parsed to a date within the current 136 year span\n\tfuture := map[string]string{\"22680119031408\": \"20631123173144\",\n\t\t\"19010101121212\": \"20370206184028\",\n\t\t\"19210101121212\": \"20570206184028\",\n\t\t\"19500101121212\": \"20860206184028\",\n\t\t\"19700101000000\": \"19700101000000\",\n\t\t\"19690101000000\": \"21050207062816\",\n\t\t\"29210101121212\": \"21040522212236\",\n\t}\n\tfor from, to := range future {\n\t\tx, _ := StringToTime(from)\n\t\ty := TimeToString(x)\n\t\tif y != to {\n\t\t\tt.Errorf(\"1982 arithmetic future failure %s:%s (%s)\", from, to, y)\n\t\t}\n\t}\n}\n\nfunc TestEmpty(t *testing.T) {\n\tz := NewZoneParser(strings.NewReader(\"\"), \"\", \"\")\n\tfor _, ok := z.Next(); ok; _, ok = z.Next() {\n\t\tt.Errorf(\"should be empty\")\n\t}\n\tif err := z.Err(); err != nil {\n\t\tt.Error(\"got an error when it shouldn't\")\n\t}\n}\n\nfunc TestLowercaseTokens(t *testing.T) {\n\tvar testrecords = []string{\n\t\t\"example.org. 300 IN a 1.2.3.4\",\n\t\t\"example.org. 300 in A 1.2.3.4\",\n\t\t\"example.org. 300 in a 1.2.3.4\",\n\t\t\"example.org. 300 a 1.2.3.4\",\n\t\t\"example.org. 300 A 1.2.3.4\",\n\t\t\"example.org. IN a 1.2.3.4\",\n\t\t\"example.org. in A 1.2.3.4\",\n\t\t\"example.org. in a 1.2.3.4\",\n\t\t\"example.org. a 1.2.3.4\",\n\t\t\"example.org. A 1.2.3.4\",\n\t\t\"example.org. a 1.2.3.4\",\n\t\t\"$ORIGIN example.org.\\n a 1.2.3.4\",\n\t\t\"$Origin example.org.\\n a 1.2.3.4\",\n\t\t\"$origin example.org.\\n a 1.2.3.4\",\n\t\t\"example.org. Class1 Type1 1.2.3.4\",\n\t}\n\tfor _, testrr := range testrecords {\n\t\t_, err := NewRR(testrr)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to parse %#v, got %v\", testrr, err)\n\t\t}\n\t}\n}\n\nfunc TestSRVPacking(t *testing.T) {\n\tmsg := Msg{}\n\n\tthings := []string{\"1.2.3.4:8484\",\n\t\t\"45.45.45.45:8484\",\n\t\t\"84.84.84.84:8484\",\n\t}\n\n\tfor i, n := range things {\n\t\th, p, err := net.SplitHostPort(n)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tport, _ := strconv.ParseUint(p, 10, 16)\n\n\t\trr := &SRV{\n\t\t\tHdr: RR_Header{Name: \"somename.\",\n\t\t\t\tRrtype: TypeSRV,\n\t\t\t\tClass:  ClassINET,\n\t\t\t\tTtl:    5},\n\t\t\tPriority: uint16(i),\n\t\t\tWeight:   5,\n\t\t\tPort:     uint16(port),\n\t\t\tTarget:   h + \".\",\n\t\t}\n\n\t\tmsg.Answer = append(msg.Answer, rr)\n\t}\n\n\t_, err := msg.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"couldn't pack %v: %v\", msg, err)\n\t}\n}\n\nfunc TestParseBackslash(t *testing.T) {\n\tif _, err := NewRR(\"nul\\\\000gap.test.globnix.net. 600 IN\tA 192.0.2.10\"); err != nil {\n\t\tt.Errorf(\"could not create RR with \\\\000 in it\")\n\t}\n\tif _, err := NewRR(`nul\\000gap.test.globnix.net. 600 IN TXT \"Hello\\123\"`); err != nil {\n\t\tt.Errorf(\"could not create RR with \\\\000 in it\")\n\t}\n\tif _, err := NewRR(`m\\ @\\ iek.nl. IN 3600 A 127.0.0.1`); err != nil {\n\t\tt.Errorf(\"could not create RR with \\\\ and \\\\@ in it\")\n\t}\n}\n\nfunc TestILNP(t *testing.T) {\n\ttests := []string{\n\t\t\"host1.example.com.\\t3600\\tIN\\tNID\\t10 0014:4fff:ff20:ee64\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tNID\\t20 0015:5fff:ff21:ee65\",\n\t\t\"host2.example.com.\\t3600\\tIN\\tNID\\t10 0016:6fff:ff22:ee66\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tL32\\t10 10.1.2.0\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tL32\\t20 10.1.4.0\",\n\t\t\"host2.example.com.\\t3600\\tIN\\tL32\\t10 10.1.8.0\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tL64\\t10 2001:0DB8:1140:1000\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tL64\\t20 2001:0DB8:2140:2000\",\n\t\t\"host2.example.com.\\t3600\\tIN\\tL64\\t10 2001:0DB8:4140:4000\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tLP\\t10 l64-subnet1.example.com.\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tLP\\t10 l64-subnet2.example.com.\",\n\t\t\"host1.example.com.\\t3600\\tIN\\tLP\\t20 l32-subnet1.example.com.\",\n\t}\n\tfor _, t1 := range tests {\n\t\tr, err := NewRR(t1)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"an error occurred: %v\", err)\n\t\t} else {\n\t\t\tif t1 != r.String() {\n\t\t\t\tt.Fatalf(\"strings should be equal %s %s\", t1, r.String())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestGposEidNimloc(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"444433332222111199990123000000ff. NSAP-PTR foo.bar.com.\": \"444433332222111199990123000000ff.\\t3600\\tIN\\tNSAP-PTR\\tfoo.bar.com.\",\n\t\t\"lillee. IN  GPOS -32.6882 116.8652 10.0\":                 \"lillee.\\t3600\\tIN\\tGPOS\\t-32.6882 116.8652 10.0\",\n\t\t\"hinault. IN GPOS -22.6882 116.8652 250.0\":                \"hinault.\\t3600\\tIN\\tGPOS\\t-22.6882 116.8652 250.0\",\n\t\t\"VENERA.   IN NIMLOC  75234159EAC457800920\":               \"VENERA.\\t3600\\tIN\\tNIMLOC\\t75234159EAC457800920\",\n\t\t\"VAXA.     IN EID     3141592653589793\":                   \"VAXA.\\t3600\\tIN\\tEID\\t3141592653589793\",\n\t}\n\tfor i, o := range dt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestPX(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"*.net2.it. IN PX 10 net2.it. PRMD-net2.ADMD-p400.C-it.\":      \"*.net2.it.\\t3600\\tIN\\tPX\\t10 net2.it. PRMD-net2.ADMD-p400.C-it.\",\n\t\t\"ab.net2.it. IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.\": \"ab.net2.it.\\t3600\\tIN\\tPX\\t10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.\",\n\t}\n\tfor i, o := range dt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestComment(t *testing.T) {\n\t// Comments we must see\n\tcomments := map[string]bool{\n\t\t\"; this is comment 1\": true,\n\t\t\"; this is comment 2\": true,\n\t\t\"; this is comment 4\": true,\n\t\t\"; this is comment 6\": true,\n\t\t\"; this is comment 7\": true,\n\t\t\"; this is comment 8\": true,\n\t}\n\tzone := `\nfoo. IN A 10.0.0.1 ; this is comment 1\nfoo. IN A (\n\t10.0.0.2 ; this is comment 2\n)\n; this is comment 3\nfoo. IN A 10.0.0.3\nfoo. IN A ( 10.0.0.4 ); this is comment 4\n\nfoo. IN A 10.0.0.5\n; this is comment 5\n\nfoo. IN A 10.0.0.6\n\nfoo. IN DNSKEY 256 3 5 AwEAAb+8l ; this is comment 6\nfoo. IN NSEC miek.nl. TXT RRSIG NSEC; this is comment 7\nfoo. IN TXT \"THIS IS TEXT MAN\"; this is comment 8\n`\n\tz := NewZoneParser(strings.NewReader(zone), \".\", \"\")\n\tfor _, ok := z.Next(); ok; _, ok = z.Next() {\n\t\tif z.Comment() != \"\" {\n\t\t\tif _, okC := comments[z.Comment()]; !okC {\n\t\t\t\tt.Errorf(\"wrong comment %q\", z.Comment())\n\t\t\t}\n\t\t}\n\t}\n\tif err := z.Err(); err != nil {\n\t\tt.Error(\"got an error when it shouldn't\")\n\t}\n}\n\nfunc TestZoneParserComments(t *testing.T) {\n\tfor i, test := range []struct {\n\t\tzone     string\n\t\tcomments []string\n\t}{\n\t\t{\n\t\t\t`name. IN SOA  a6.nstld.com. hostmaster.nic.name. (\n\t\t\t203362132 ; serial\n\t\t\t5m        ; refresh (5 minutes)\n\t\t\t5m        ; retry (5 minutes)\n\t\t\t2w        ; expire (2 weeks)\n\t\t\t300       ; minimum (5 minutes)\n\t\t) ; y\n. 3600000  IN  NS ONE.MY-ROOTS.NET. ; x`,\n\t\t\t[]string{\"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes) ; y\", \"; x\"},\n\t\t},\n\t\t{\n\t\t\t`name. IN SOA  a6.nstld.com. hostmaster.nic.name. (\n\t\t\t203362132 ; serial\n\t\t\t5m        ; refresh (5 minutes)\n\t\t\t5m        ; retry (5 minutes)\n\t\t\t2w        ; expire (2 weeks)\n\t\t\t300       ; minimum (5 minutes)\n\t\t) ; y\n. 3600000  IN  NS ONE.MY-ROOTS.NET.`,\n\t\t\t[]string{\"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes) ; y\", \"\"},\n\t\t},\n\t\t{\n\t\t\t`name. IN SOA  a6.nstld.com. hostmaster.nic.name. (\n\t\t\t203362132 ; serial\n\t\t\t5m        ; refresh (5 minutes)\n\t\t\t5m        ; retry (5 minutes)\n\t\t\t2w        ; expire (2 weeks)\n\t\t\t300       ; minimum (5 minutes)\n\t\t)\n. 3600000  IN  NS ONE.MY-ROOTS.NET.`,\n\t\t\t[]string{\"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes)\", \"\"},\n\t\t},\n\t\t{\n\t\t\t`name. IN SOA  a6.nstld.com. hostmaster.nic.name. (\n\t\t\t203362132 ; serial\n\t\t\t5m        ; refresh (5 minutes)\n\t\t\t5m        ; retry (5 minutes)\n\t\t\t2w        ; expire (2 weeks)\n\t\t\t300       ; minimum (5 minutes)\n\t\t)\n. 3600000  IN  NS ONE.MY-ROOTS.NET. ; x`,\n\t\t\t[]string{\"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes)\", \"; x\"},\n\t\t},\n\t\t{\n\t\t\t`name. IN SOA  a6.nstld.com. hostmaster.nic.name. (\n\t\t\t203362132 ; serial\n\t\t\t5m        ; refresh (5 minutes)\n\t\t\t5m        ; retry (5 minutes)\n\t\t\t2w        ; expire (2 weeks)\n\t\t\t300       ; minimum (5 minutes)\n\t\t)`,\n\t\t\t[]string{\"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes)\"},\n\t\t},\n\t\t{\n\t\t\t`. 3600000  IN  NS ONE.MY-ROOTS.NET. ; x`,\n\t\t\t[]string{\"; x\"},\n\t\t},\n\t\t{\n\t\t\t`. 3600000  IN  NS ONE.MY-ROOTS.NET.`,\n\t\t\t[]string{\"\"},\n\t\t},\n\t\t{\n\t\t\t`. 3600000  IN  NS ONE.MY-ROOTS.NET. ;;x`,\n\t\t\t[]string{\";;x\"},\n\t\t},\n\t} {\n\t\tr := strings.NewReader(test.zone)\n\n\t\tvar j int\n\t\tz := NewZoneParser(r, \"\", \"\")\n\t\tfor rr, ok := z.Next(); ok; rr, ok = z.Next() {\n\t\t\tif j >= len(test.comments) {\n\t\t\t\tt.Fatalf(\"too many records for zone %d at %d record, expected %d\", i, j+1, len(test.comments))\n\t\t\t}\n\n\t\t\tif z.Comment() != test.comments[j] {\n\t\t\t\tt.Errorf(\"invalid comment for record %d:%d %v\", i, j, rr)\n\t\t\t\tt.Logf(\"expected %q\", test.comments[j])\n\t\t\t\tt.Logf(\"got      %q\", z.Comment())\n\t\t\t}\n\n\t\t\tj++\n\t\t}\n\n\t\tif err := z.Err(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif j != len(test.comments) {\n\t\t\tt.Errorf(\"too few records for zone %d, got %d, expected %d\", i, j, len(test.comments))\n\t\t}\n\t}\n}\n\nfunc TestEUIxx(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"host.example. IN EUI48 00-00-5e-90-01-2a\":       \"host.example.\\t3600\\tIN\\tEUI48\\t00-00-5e-90-01-2a\",\n\t\t\"host.example. IN EUI64 00-00-5e-ef-00-00-00-2a\": \"host.example.\\t3600\\tIN\\tEUI64\\t00-00-5e-ef-00-00-00-2a\",\n\t}\n\tfor i, o := range tests {\n\t\tr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to parse %s: %v\", i, err)\n\t\t}\n\t\tif r.String() != o {\n\t\t\tt.Errorf(\"want %s, got %s\", o, r.String())\n\t\t}\n\t}\n}\n\nfunc TestUserRR(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"host.example. IN UID 1234\":              \"host.example.\\t3600\\tIN\\tUID\\t1234\",\n\t\t\"host.example. IN GID 1234556\":           \"host.example.\\t3600\\tIN\\tGID\\t1234556\",\n\t\t\"host.example. IN UINFO \\\"Miek Gieben\\\"\": \"host.example.\\t3600\\tIN\\tUINFO\\t\\\"Miek Gieben\\\"\",\n\t}\n\tfor i, o := range tests {\n\t\tr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to parse %s: %v\", i, err)\n\t\t}\n\t\tif r.String() != o {\n\t\t\tt.Errorf(\"want %s, got %s\", o, r.String())\n\t\t}\n\t}\n}\n\nfunc TestTXT(t *testing.T) {\n\t// Test single entry TXT record\n\trr, err := NewRR(`_raop._tcp.local. 60 IN TXT \"single value\"`)\n\tif err != nil {\n\t\tt.Error(\"failed to parse single value TXT record\", err)\n\t} else if rr, ok := rr.(*TXT); !ok {\n\t\tt.Error(\"wrong type, record should be of type TXT\")\n\t} else {\n\t\tif len(rr.Txt) != 1 {\n\t\t\tt.Error(\"bad size of TXT value:\", len(rr.Txt))\n\t\t} else if rr.Txt[0] != \"single value\" {\n\t\t\tt.Error(\"bad single value\")\n\t\t}\n\t\tif rr.String() != `_raop._tcp.local.\t60\tIN\tTXT\t\"single value\"` {\n\t\t\tt.Error(\"bad representation of TXT record:\", rr.String())\n\t\t}\n\t\tif Len(rr) != 28+1+12 {\n\t\t\tt.Error(\"bad size of serialized record:\", Len(rr))\n\t\t}\n\t}\n\n\t// Test multi entries TXT record\n\trr, err = NewRR(`_raop._tcp.local. 60 IN TXT \"a=1\" \"b=2\" \"c=3\" \"d=4\"`)\n\tif err != nil {\n\t\tt.Error(\"failed to parse multi-values TXT record\", err)\n\t} else if rr, ok := rr.(*TXT); !ok {\n\t\tt.Error(\"wrong type, record should be of type TXT\")\n\t} else {\n\t\tif len(rr.Txt) != 4 {\n\t\t\tt.Error(\"bad size of TXT multi-value:\", len(rr.Txt))\n\t\t} else if rr.Txt[0] != \"a=1\" || rr.Txt[1] != \"b=2\" || rr.Txt[2] != \"c=3\" || rr.Txt[3] != \"d=4\" {\n\t\t\tt.Error(\"bad values in TXT records\")\n\t\t}\n\t\tif rr.String() != `_raop._tcp.local.\t60\tIN\tTXT\t\"a=1\" \"b=2\" \"c=3\" \"d=4\"` {\n\t\t\tt.Error(\"bad representation of TXT multi value record:\", rr.String())\n\t\t}\n\t\tif Len(rr) != 28+1+3+1+3+1+3+1+3 {\n\t\t\tt.Error(\"bad size of serialized multi value record:\", Len(rr))\n\t\t}\n\t}\n\n\t// Test empty-string in TXT record\n\trr, err = NewRR(`_raop._tcp.local. 60 IN TXT \"\"`)\n\tif err != nil {\n\t\tt.Error(\"failed to parse empty-string TXT record\", err)\n\t} else if rr, ok := rr.(*TXT); !ok {\n\t\tt.Error(\"wrong type, record should be of type TXT\")\n\t} else {\n\t\tif len(rr.Txt) != 1 {\n\t\t\tt.Error(\"bad size of TXT empty-string value:\", len(rr.Txt))\n\t\t} else if rr.Txt[0] != \"\" {\n\t\t\tt.Error(\"bad value for empty-string TXT record\")\n\t\t}\n\t\tif rr.String() != `_raop._tcp.local.\t60\tIN\tTXT\t\"\"` {\n\t\t\tt.Error(\"bad representation of empty-string TXT record:\", rr.String())\n\t\t}\n\t\tif Len(rr) != 28+1 {\n\t\t\tt.Error(\"bad size of serialized record:\", Len(rr))\n\t\t}\n\t}\n\n\t// Test TXT record with string larger than 255 bytes that should be split\n\t// up by the parser. Add some escape sequences too to ensure their length\n\t// is counted correctly.\n\ts := `\"\\;\\\\\\120` + strings.Repeat(\"a\", 255) + `b\"`\n\trr, err = NewRR(`test.local. 60 IN TXT ` + s)\n\tif err != nil {\n\t\tt.Error(\"failed to parse empty-string TXT record\", err)\n\t}\n\tif rr.(*TXT).Txt[1] != \"aaab\" {\n\t\tt.Errorf(\"Txt should have two strings, last one must be 'aaab', but is %s\", rr.(*TXT).Txt[1])\n\t}\n\trrContent := strings.Replace(rr.String(), rr.Header().String(), \"\", 1)\n\texpectedRRContent := `\";\\\\x` + strings.Repeat(\"a\", 252) + `\" \"aaab\"`\n\tif expectedRRContent != rrContent {\n\t\tt.Errorf(\"Expected TXT RR content to be %#q but got %#q\", expectedRRContent, rrContent)\n\t}\n\n\t// Test TXT record that is already split up into strings of len <= 255.\n\ts = fmt.Sprintf(\n\t\t\"%q %q %q %q %q %q\",\n\t\tstrings.Repeat(`a`, 255),\n\t\tstrings.Repeat(\"b\", 255),\n\t\tstrings.Repeat(\"c\", 255),\n\t\tstrings.Repeat(\"d\", 0),\n\t\tstrings.Repeat(\"e\", 1),\n\t\tstrings.Repeat(\"f\", 123),\n\t)\n\trr, err = NewRR(`test.local. 60 IN TXT ` + s)\n\tif err != nil {\n\t\tt.Error(\"failed to parse empty-string TXT record\", err)\n\t}\n\trrContent = strings.Replace(rr.String(), rr.Header().String(), \"\", 1)\n\texpectedRRContent = s // same as input\n\tif expectedRRContent != rrContent {\n\t\tt.Errorf(\"Expected TXT RR content to be %#q but got %#q\", expectedRRContent, rrContent)\n\t}\n}\n\nfunc TestTypeXXXX(t *testing.T) {\n\t_, err := NewRR(\"example.com IN TYPE1234 \\\\# 4 aabbccdd\")\n\tif err != nil {\n\t\tt.Errorf(\"failed to parse TYPE1234 RR: %v\", err)\n\t}\n\t_, err = NewRR(\"example.com IN TYPE655341 \\\\# 8 aabbccddaabbccdd\")\n\tif err == nil {\n\t\tt.Errorf(\"this should not work, for TYPE655341\")\n\t}\n\t_, err = NewRR(\"example.com IN TYPE1 \\\\# 4 0a000001\")\n\tif err != nil {\n\t\tt.Errorf(\"failed to parse TYPE1 RR: %v\", err)\n\t}\n}\n\nfunc TestPTR(t *testing.T) {\n\t_, err := NewRR(\"144.2.0.192.in-addr.arpa. 900 IN PTR ilouse03146p0\\\\(.example.com.\")\n\tif err != nil {\n\t\tt.Error(\"failed to parse \", err)\n\t}\n}\n\nfunc TestDigit(t *testing.T) {\n\ttests := map[string]byte{\n\t\t\"miek\\\\000.nl. 100 IN TXT \\\"A\\\"\": 0,\n\t\t\"miek\\\\001.nl. 100 IN TXT \\\"A\\\"\": 1,\n\t\t\"miek\\\\254.nl. 100 IN TXT \\\"A\\\"\": 254,\n\t\t\"miek\\\\255.nl. 100 IN TXT \\\"A\\\"\": 255,\n\t\t\"miek\\\\256.nl. 100 IN TXT \\\"A\\\"\": 0,\n\t\t\"miek\\\\257.nl. 100 IN TXT \\\"A\\\"\": 1,\n\t\t\"miek\\\\004.nl. 100 IN TXT \\\"A\\\"\": 4,\n\t}\n\tfor s, i := range tests {\n\t\tr, err := NewRR(s)\n\t\tbuf := make([]byte, 40)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse %v\", err)\n\t\t}\n\t\tPackRR(r, buf, 0, nil, false)\n\t\tif buf[5] != i {\n\t\t\tt.Fatalf(\"5 pos must be %d, is %d\", i, buf[5])\n\t\t}\n\t\tr1, _, _ := UnpackRR(buf, 0)\n\t\tif r1.Header().Ttl != 100 {\n\t\t\tt.Fatalf(\"TTL should %d, is %d\", 100, r1.Header().Ttl)\n\t\t}\n\t}\n}\n\nfunc TestParseRRSIGTimestamp(t *testing.T) {\n\ttests := map[string]bool{\n\t\t`miek.nl.  IN RRSIG SOA 8 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2BvuNiUJjSYlJAgzyAE6CF875BMvvZa+Sb0 RlSCL7WODQSQHhCx/fegHhVVF+Iz8N8kOLrmXD1+jO3Bm6Prl5UhcsPx WTBsg/kmxbp8sR1kvH4oZJtVfakG3iDerrxNaf0sQwhZzyfJQAqpC7pcBoc=`: true,\n\t\t`miek.nl.  IN RRSIG SOA 8 2 43200 315565800 4102477800 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2BvuNiUJjSYlJAgzyAE6CF875BMvvZa+Sb0 RlSCL7WODQSQHhCx/fegHhVVF+Iz8N8kOLrmXD1+jO3Bm6Prl5UhcsPx WTBsg/kmxbp8sR1kvH4oZJtVfakG3iDerrxNaf0sQwhZzyfJQAqpC7pcBoc=`:          true,\n\t}\n\tfor r := range tests {\n\t\t_, err := NewRR(r)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n}\n\nfunc TestTxtEqual(t *testing.T) {\n\trr1 := new(TXT)\n\trr1.Hdr = RR_Header{Name: \".\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}\n\trr1.Txt = []string{\"a\\\"a\", \"\\\"\", \"b\"}\n\trr2, _ := NewRR(rr1.String())\n\tif rr1.String() != rr2.String() {\n\t\t// This is not an error, but keep this test.\n\t\tt.Errorf(\"these two TXT records should match:\\n%s\\n%s\", rr1.String(), rr2.String())\n\t}\n}\n\nfunc TestTxtLong(t *testing.T) {\n\trr1 := new(TXT)\n\trr1.Hdr = RR_Header{Name: \".\", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}\n\t// Make a long txt record, this breaks when sending the packet,\n\t// but not earlier.\n\trr1.Txt = []string{\"start-\"}\n\tfor i := 0; i < 200; i++ {\n\t\trr1.Txt[0] += \"start-\"\n\t}\n\tstr := rr1.String()\n\tif len(str) < len(rr1.Txt[0]) {\n\t\tt.Error(\"string conversion should work\")\n\t}\n}\n\n// Basically, don't crash.\nfunc TestMalformedPackets(t *testing.T) {\n\tvar packets = []string{\n\t\t\"0021641c0000000100000000000078787878787878787878787303636f6d0000100001\",\n\t}\n\n\t// com = 63 6f 6d\n\tfor _, packet := range packets {\n\t\tdata, _ := hex.DecodeString(packet)\n\t\tvar msg Msg\n\t\tmsg.Unpack(data)\n\t}\n}\n\ntype algorithm struct {\n\tname uint8\n\tbits int\n}\n\nfunc TestNewPrivateKey(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping test in short mode.\")\n\t}\n\talgorithms := []algorithm{\n\t\t{ECDSAP256SHA256, 256},\n\t\t{ECDSAP384SHA384, 384},\n\t\t{RSASHA1, 1024},\n\t\t{RSASHA256, 1024},\n\t\t{ED25519, 256},\n\t}\n\n\tfor _, algo := range algorithms {\n\t\tkey := new(DNSKEY)\n\t\tkey.Hdr.Rrtype = TypeDNSKEY\n\t\tkey.Hdr.Name = \"miek.nl.\"\n\t\tkey.Hdr.Class = ClassINET\n\t\tkey.Hdr.Ttl = 14400\n\t\tkey.Flags = 256\n\t\tkey.Protocol = 3\n\t\tkey.Algorithm = algo.name\n\t\tprivkey, err := key.Generate(algo.bits)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tnewPrivKey, err := key.NewPrivateKey(key.PrivateKeyString(privkey))\n\t\tif err != nil {\n\t\t\tt.Error(key.String())\n\t\t\tt.Error(key.PrivateKeyString(privkey))\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tswitch newPrivKey := newPrivKey.(type) {\n\t\tcase *rsa.PrivateKey:\n\t\t\tnewPrivKey.Precompute()\n\t\t}\n\n\t\tif !reflect.DeepEqual(privkey, newPrivKey) {\n\t\t\tt.Errorf(\"[%v] Private keys differ:\\n%#v\\n%#v\", AlgorithmToString[algo.name], privkey, newPrivKey)\n\t\t}\n\t}\n}\n\n// special input test\nfunc TestNewRRSpecial(t *testing.T) {\n\tvar (\n\t\trr     RR\n\t\terr    error\n\t\texpect string\n\t)\n\n\trr, err = NewRR(\"; comment\")\n\texpect = \"\"\n\tif err != nil {\n\t\tt.Errorf(\"unexpected err: %v\", err)\n\t}\n\tif rr != nil {\n\t\tt.Errorf(\"unexpected result: [%s] != [%s]\", rr, expect)\n\t}\n\n\trr, err = NewRR(\"\")\n\texpect = \"\"\n\tif err != nil {\n\t\tt.Errorf(\"unexpected err: %v\", err)\n\t}\n\tif rr != nil {\n\t\tt.Errorf(\"unexpected result: [%s] != [%s]\", rr, expect)\n\t}\n\n\trr, err = NewRR(\"$ORIGIN foo.\")\n\texpect = \"\"\n\tif err != nil {\n\t\tt.Errorf(\"unexpected err: %v\", err)\n\t}\n\tif rr != nil {\n\t\tt.Errorf(\"unexpected result: [%s] != [%s]\", rr, expect)\n\t}\n\n\trr, err = NewRR(\" \")\n\texpect = \"\"\n\tif err != nil {\n\t\tt.Errorf(\"unexpected err: %v\", err)\n\t}\n\tif rr != nil {\n\t\tt.Errorf(\"unexpected result: [%s] != [%s]\", rr, expect)\n\t}\n\n\trr, err = NewRR(\"\\n\")\n\texpect = \"\"\n\tif err != nil {\n\t\tt.Errorf(\"unexpected err: %v\", err)\n\t}\n\tif rr != nil {\n\t\tt.Errorf(\"unexpected result: [%s] != [%s]\", rr, expect)\n\t}\n\n\trr, err = NewRR(\"foo. A 1.1.1.1\\nbar. A 2.2.2.2\")\n\texpect = \"foo.\\t3600\\tIN\\tA\\t1.1.1.1\"\n\tif err != nil {\n\t\tt.Errorf(\"unexpected err: %v\", err)\n\t}\n\tif rr == nil || rr.String() != expect {\n\t\tt.Errorf(\"unexpected result: [%s] != [%s]\", rr, expect)\n\t}\n}\n\nfunc TestPrintfVerbsRdata(t *testing.T) {\n\tx, _ := NewRR(\"www.miek.nl. IN MX 20 mx.miek.nl.\")\n\tif Field(x, 1) != \"20\" {\n\t\tt.Errorf(\"should be 20\")\n\t}\n\tif Field(x, 2) != \"mx.miek.nl.\" {\n\t\tt.Errorf(\"should be mx.miek.nl.\")\n\t}\n\n\tx, _ = NewRR(\"www.miek.nl. IN A 127.0.0.1\")\n\tif Field(x, 1) != \"127.0.0.1\" {\n\t\tt.Errorf(\"should be 127.0.0.1\")\n\t}\n\n\tx, _ = NewRR(\"www.miek.nl. IN AAAA ::1\")\n\tif Field(x, 1) != \"::1\" {\n\t\tt.Errorf(\"should be ::1\")\n\t}\n\n\tx, _ = NewRR(\"www.miek.nl. IN NSEC a.miek.nl. A NS SOA MX AAAA\")\n\tif Field(x, 1) != \"a.miek.nl.\" {\n\t\tt.Errorf(\"should be a.miek.nl.\")\n\t}\n\tif Field(x, 2) != \"A NS SOA MX AAAA\" {\n\t\tt.Errorf(\"should be A NS SOA MX AAAA\")\n\t}\n\n\tx, _ = NewRR(\"www.miek.nl. IN TXT \\\"first\\\" \\\"second\\\"\")\n\tif Field(x, 1) != \"first second\" {\n\t\tt.Errorf(\"should be first second\")\n\t}\n\tif Field(x, 0) != \"\" {\n\t\tt.Errorf(\"should be empty\")\n\t}\n}\n\nfunc TestParseTokenOverflow(t *testing.T) {\n\t_, err := NewRR(\"_443._tcp.example.org. IN TLSA 0 0 0 308205e8308204d0a00302010202100411de8f53b462f6a5a861b712ec6b59300d06092a864886f70d01010b05003070310b300906035504061302555331153013060355040a130c446967694365727420496e6331193017060355040b13107777772e64696769636572742e636f6d312f302d06035504031326446967694365727420534841322048696768204173737572616e636520536572766572204341301e170d3134313130363030303030305a170d3135313131333132303030305a3081a5310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b4c6f7320416e67656c6573313c303a060355040a1333496e7465726e657420436f72706f726174696f6e20666f722041737369676e6564204e616d657320616e64204e756d6265727331133011060355040b130a546563686e6f6c6f6779311830160603550403130f7777772e6578616d706c652e6f726730820122300d06092a864886f70d01010105000382010f003082010a02820101009e663f52a3d18cb67cdfed547408a4e47e4036538988da2798da3b6655f7240d693ed1cb3fe6d6ad3a9e657ff6efa86b83b0cad24e5d31ff2bf70ec3b78b213f1b4bf61bdc669cbbc07d67154128ca92a9b3cbb4213a836fb823ddd4d7cc04918314d25f06086fa9970ba17e357cca9b458c27eb71760ab95e3f9bc898ae89050ae4d09ba2f7e4259d9ff1e072a6971b18355a8b9e53670c3d5dbdbd283f93a764e71b3a4140ca0746090c08510e2e21078d7d07844bf9c03865b531a0bf2ee766bc401f6451c5a1e6f6fb5d5c1d6a97a0abe91ae8b02e89241e07353909ccd5b41c46de207c06801e08f20713603827f2ae3e68cf15ef881d7e0608f70742e30203010001a382024630820242301f0603551d230418301680145168ff90af0207753cccd9656462a212b859723b301d0603551d0e04160414b000a7f422e9b1ce216117c4c46e7164c8e60c553081810603551d11047a3078820f7777772e6578616d706c652e6f7267820b6578616d706c652e636f6d820b6578616d706c652e656475820b6578616d706c652e6e6574820b6578616d706c652e6f7267820f7777772e6578616d706c652e636f6d820f7777772e6578616d706c652e656475820f7777772e6578616d706c652e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b0601050507030230750603551d1f046e306c3034a032a030862e687474703a2f2f63726c332e64696769636572742e636f6d2f736861322d68612d7365727665722d67332e63726c3034a032a030862e687474703a2f2f63726c342e64696769636572742e636f6d2f736861322d68612d7365727665722d67332e63726c30420603551d20043b3039303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f43505330818306082b0601050507010104773075302406082b060105050730018618687474703a2f2f6f6373702e64696769636572742e636f6d304d06082b060105050730028641687474703a2f2f636163657274732e64696769636572742e636f6d2f446967694365727453484132486967684173737572616e636553657276657243412e637274300c0603551d130101ff04023000300d06092a864886f70d01010b050003820101005eac2124dedb3978a86ff3608406acb542d3cb54cb83facd63aec88144d6a1bf15dbf1f215c4a73e241e582365cba9ea50dd306541653b3513af1a0756c1b2720e8d112b34fb67181efad9c4609bdc670fb025fa6e6d42188161b026cf3089a08369c2f3609fc84bcc3479140c1922ede430ca8dbac2b2a3cdacb305ba15dc7361c4c3a5e6daa99cb446cb221b28078a7a944efba70d96f31ac143d959bccd2fd50e30c325ea2624fb6b6dbe9344dbcf133bfbd5b4e892d635dbf31596451672c6b65ba5ac9b3cddea92b35dab1065cae3c8cb6bb450a62ea2f72ea7c6bdc7b65fa09b012392543734083c7687d243f8d0375304d99ccd2e148966a8637a6797\")\n\tif err != nil {\n\t\tt.Fatalf(\"long token should not return an error\")\n\t}\n}\n\nfunc TestParseTLSA(t *testing.T) {\n\tlt := []string{\n\t\t\"_443._tcp.example.org.\\t3600\\tIN\\tTLSA\\t1 1 1 c22be239f483c08957bc106219cc2d3ac1a308dfbbdd0a365f17b9351234cf00\",\n\t\t\"_443._tcp.example.org.\\t3600\\tIN\\tTLSA\\t2 1 2 4e85f45179e9cd6e0e68e2eb5be2e85ec9b92d91c609caf3ef0315213e3f92ece92c38397a607214de95c7fadc0ad0f1c604a469a0387959745032c0d51492f3\",\n\t\t\"_443._tcp.example.org.\\t3600\\tIN\\tTLSA\\t3 0 2 69ec8d2277360b215d0cd956b0e2747108dff34b27d461a41c800629e38ee6c2d1230cc9e8e36711330adc6766e6ff7c5fbb37f106f248337c1a20ad682888d2\",\n\t}\n\tfor _, o := range lt {\n\t\trr, err := NewRR(o)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", o, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseSMIMEA(t *testing.T) {\n\tlt := map[string]string{\n\t\t\"2e85e1db3e62be6ea._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t1 1 2 bd80f334566928fc18f58df7e4928c1886f48f71ca3fd41cd9b1854aca7c2180aaacad2819612ed68e7bd3701cc39be7f2529b017c0bc6a53e8fb3f0c7d48070\":   \"2e85e1db3e62be6ea._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t1 1 2 bd80f334566928fc18f58df7e4928c1886f48f71ca3fd41cd9b1854aca7c2180aaacad2819612ed68e7bd3701cc39be7f2529b017c0bc6a53e8fb3f0c7d48070\",\n\t\t\"2e85e1db3e62be6ea._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t0 0 1 cdcf0fc66b182928c5217ddd42c826983f5a4b94160ee6c1c9be62d38199f710\":                                                                   \"2e85e1db3e62be6ea._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t0 0 1 cdcf0fc66b182928c5217ddd42c826983f5a4b94160ee6c1c9be62d38199f710\",\n\t\t\"2e85e1db3e62be6ea._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8c26b251fa0c887ba4869f011a65f7e79967c2eb729f5b\":   \"2e85e1db3e62be6ea._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8c26b251fa0c887ba4869f011a65f7e79967c2eb729f5b\",\n\t\t\"2e85e1db3e62be6eb._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8 c26b251fa0c887ba4869f01 1a65f7e79967c2eb729f5b\": \"2e85e1db3e62be6eb._smimecert.example.com.\\t3600\\tIN\\tSMIMEA\\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8c26b251fa0c887ba4869f011a65f7e79967c2eb729f5b\",\n\t}\n\tfor i, o := range lt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", o, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseSSHFP(t *testing.T) {\n\tlt := []string{\n\t\t\"test.example.org.\\t300\\tSSHFP\\t1 2 (\\n\" +\n\t\t\t\"\\t\\t\\t\\t\\tBC6533CDC95A79078A39A56EA7635984ED655318ADA9\\n\" +\n\t\t\t\"\\t\\t\\t\\t\\tB6159E30723665DA95BB )\",\n\t\t\"test.example.org.\\t300\\tSSHFP\\t1 2 ( BC6533CDC  95A79078A39A56EA7635984ED655318AD  A9B6159E3072366 5DA95BB )\",\n\t}\n\tresult := \"test.example.org.\\t300\\tIN\\tSSHFP\\t1 2 BC6533CDC95A79078A39A56EA7635984ED655318ADA9B6159E30723665DA95BB\"\n\tfor _, o := range lt {\n\t\trr, err := NewRR(o)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != result {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n\\n`%s', but is     \\n`%s'\", o, result, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseHINFO(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"example.net. HINFO A B\":         \"example.net.\t3600\tIN\tHINFO\t\\\"A\\\" \\\"B\\\"\",\n\t\t\"example.net. HINFO \\\"A\\\" \\\"B\\\"\": \"example.net.\t3600\tIN\tHINFO\t\\\"A\\\" \\\"B\\\"\",\n\t\t\"example.net. HINFO A B C D E F\": \"example.net.\t3600\tIN\tHINFO\t\\\"A\\\" \\\"B C D E F\\\"\",\n\t\t\"example.net. HINFO AB\":          \"example.net.\t3600\tIN\tHINFO\t\\\"AB\\\" \\\"\\\"\",\n\t\t// \"example.net. HINFO PC-Intel-700mhz \\\"Redhat Linux 7.1\\\"\": \"example.net.\t3600\tIN\tHINFO\t\\\"PC-Intel-700mhz\\\" \\\"Redhat Linux 7.1\\\"\",\n\t\t// This one is recommended in Pro Bind book http://www.zytrax.com/books/dns/ch8/hinfo.html\n\t\t// but effectively, even Bind would replace it to correctly formed text when you AXFR\n\t\t// TODO: remove this set of comments or figure support for quoted/unquoted combinations in endingToTxtSlice function\n\t}\n\tfor i, o := range dt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseISDN(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"example.net. ISDN A B\":         \"example.net.\t3600\tIN\tISDN\t\\\"A\\\" \\\"B\\\"\",\n\t\t\"example.net. ISDN \\\"A\\\" \\\"B\\\"\": \"example.net.\t3600\tIN\tISDN\t\\\"A\\\" \\\"B\\\"\",\n\t}\n\tfor i, o := range dt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseCAA(t *testing.T) {\n\tlt := map[string]string{\n\t\t\"example.net.\tCAA\t0 issue \\\"symantec.com\\\"\":            \"example.net.\\t3600\\tIN\\tCAA\\t0 issue \\\"symantec.com\\\"\",\n\t\t\"example.net.\tCAA\t0 issuewild \\\"symantec.com; stuff\\\"\": \"example.net.\\t3600\\tIN\\tCAA\\t0 issuewild \\\"symantec.com; stuff\\\"\",\n\t\t\"example.net.\tCAA\t128 tbs \\\"critical\\\"\":                \"example.net.\\t3600\\tIN\\tCAA\\t128 tbs \\\"critical\\\"\",\n\t\t\"example.net.\tCAA\t2 auth \\\"0>09\\\\006\\\\010+\\\\006\\\\001\\\\004\\\\001\\\\214y\\\\002\\\\003\\\\001\\\\006\\\\009`\\\\134H\\\\001e\\\\003\\\\004\\\\002\\\\001\\\\004 y\\\\209\\\\012\\\\221r\\\\220\\\\156Q\\\\218\\\\150\\\\150{\\\\166\\\\245:\\\\231\\\\182%\\\\157:\\\\133\\\\179}\\\\1923r\\\\238\\\\151\\\\255\\\\128q\\\\145\\\\002\\\\001\\\\000\\\"\": \"example.net.\\t3600\\tIN\\tCAA\\t2 auth \\\"0>09\\\\006\\\\010+\\\\006\\\\001\\\\004\\\\001\\\\214y\\\\002\\\\003\\\\001\\\\006\\\\009`\\\\134H\\\\001e\\\\003\\\\004\\\\002\\\\001\\\\004 y\\\\209\\\\012\\\\221r\\\\220\\\\156Q\\\\218\\\\150\\\\150{\\\\166\\\\245:\\\\231\\\\182%\\\\157:\\\\133\\\\179}\\\\1923r\\\\238\\\\151\\\\255\\\\128q\\\\145\\\\002\\\\001\\\\000\\\"\",\n\t\t\"example.net.   TYPE257\t0 issue \\\"symantec.com\\\"\": \"example.net.\\t3600\\tIN\\tCAA\\t0 issue \\\"symantec.com\\\"\",\n\t}\n\tfor i, o := range lt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestPackCAA(t *testing.T) {\n\tm := new(Msg)\n\trecord := new(CAA)\n\trecord.Hdr = RR_Header{Name: \"example.com.\", Rrtype: TypeCAA, Class: ClassINET, Ttl: 0}\n\trecord.Tag = \"issue\"\n\trecord.Value = \"symantec.com\"\n\trecord.Flag = 1\n\n\tm.Answer = append(m.Answer, record)\n\tbytes, err := m.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to pack msg: %v\", err)\n\t}\n\tif err := m.Unpack(bytes); err != nil {\n\t\tt.Fatalf(\"failed to unpack msg: %v\", err)\n\t}\n\tif len(m.Answer) != 1 {\n\t\tt.Fatalf(\"incorrect number of answers unpacked\")\n\t}\n\trr := m.Answer[0].(*CAA)\n\tif rr.Tag != \"issue\" {\n\t\tt.Fatalf(\"invalid tag for unpacked answer\")\n\t} else if rr.Value != \"symantec.com\" {\n\t\tt.Fatalf(\"invalid value for unpacked answer\")\n\t} else if rr.Flag != 1 {\n\t\tt.Fatalf(\"invalid flag for unpacked answer\")\n\t}\n}\n\nfunc TestParseURI(t *testing.T) {\n\tlt := map[string]string{\n\t\t\"_http._tcp. IN URI   10 1 \\\"http://www.example.com/path\\\"\": \"_http._tcp.\\t3600\\tIN\\tURI\\t10 1 \\\"http://www.example.com/path\\\"\",\n\t\t\"_http._tcp. IN URI   10 1 \\\"\\\"\":                            \"_http._tcp.\\t3600\\tIN\\tURI\\t10 1 \\\"\\\"\",\n\t}\n\tfor i, o := range lt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseAVC(t *testing.T) {\n\tavcs := map[string]string{\n\t\t`example.org. IN AVC \"app-name:WOLFGANG|app-class:OAM|business=yes\"`: `example.org.\t3600\tIN\tAVC\t\"app-name:WOLFGANG|app-class:OAM|business=yes\"`,\n\t}\n\tfor avc, o := range avcs {\n\t\trr, err := NewRR(avc)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", avc, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseCSYNC(t *testing.T) {\n\tsyncs := map[string]string{\n\t\t`example.com. 3600 IN CSYNC 66 3 A NS AAAA`: `example.com.\t3600\tIN\tCSYNC\t66 3 A NS AAAA`,\n\t}\n\tfor s, o := range syncs {\n\t\trr, err := NewRR(s)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", s, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseSVCB(t *testing.T) {\n\tsvcbs := map[string]string{\n\t\t`example.com. 3600 IN SVCB 0 cloudflare.com.`:                              `example.com.\t3600\tIN\tSVCB\t0 cloudflare.com.`,\n\t\t`example.com. 3600 IN SVCB 65000 cloudflare.com. alpn=h2 ipv4hint=3.4.3.2`: `example.com.\t3600\tIN\tSVCB\t65000 cloudflare.com. alpn=\"h2\" ipv4hint=\"3.4.3.2\"`,\n\t\t`example.com. 3600 IN SVCB 65000 cloudflare.com. key65000=4\\ 3 key65001=\"\\\" \" key65002 key65003= key65004=\"\" key65005== key65006==\\\"\\\" key65007=\\254 key65008=\\032`: `example.com.\t3600\tIN\tSVCB\t65000 cloudflare.com. key65000=\"4\\ 3\" key65001=\"\\\"\\ \" key65002=\"\" key65003=\"\" key65004=\"\" key65005=\"=\" key65006=\"=\\\"\\\"\" key65007=\"\\254\" key65008=\"\\ \"`,\n\t\t// Explained in svcb.go \"In AliasMode, records SHOULD NOT include any SvcParams,\"\n\t\t`example.com. 3600 IN SVCB 0 no-default-alpn`: `example.com.\t3600\tIN\tSVCB\t0 no-default-alpn.`,\n\t\t// From the specification\n\t\t`example.com.   HTTPS   0 foo.example.com.`:                                                          `example.com.\t3600\tIN\tHTTPS\t0 foo.example.com.`,\n\t\t`example.com.   SVCB   1 .`:                                                                          `example.com.\t3600\tIN\tSVCB\t1 .`,\n\t\t`example.com.   SVCB   16 foo.example.com. port=53`:                                                  `example.com.\t3600\tIN\tSVCB\t16 foo.example.com. port=\"53\"`,\n\t\t`example.com.   SVCB   1 foo.example.com. key667=hello`:                                              `example.com.\t3600\tIN\tSVCB\t1 foo.example.com. key667=\"hello\"`,\n\t\t`example.com.   SVCB   1 foo.example.com. key667=\"hello\\210qoo\"`:                                     `example.com.\t3600\tIN\tSVCB\t1 foo.example.com. key667=\"hello\\210qoo\"`,\n\t\t`example.com.   SVCB   1 foo.example.com. ipv6hint=\"2001:db8::1,2001:db8::53:1\"`:                     `example.com.\t3600\tIN\tSVCB\t1 foo.example.com. ipv6hint=\"2001:db8::1,2001:db8::53:1\"`,\n\t\t`example.com.   SVCB   1 example.com. ipv6hint=\"2001:db8::198.51.100.100\"`:                           `example.com.\t3600\tIN\tSVCB\t1 example.com. ipv6hint=\"2001:db8::c633:6464\"`,\n\t\t`example.com.   SVCB   16 foo.example.org. alpn=h2,h3-19 mandatory=ipv4hint,alpn ipv4hint=192.0.2.1`: `example.com.\t3600\tIN\tSVCB\t16 foo.example.org. alpn=\"h2,h3-19\" mandatory=\"ipv4hint,alpn\" ipv4hint=\"192.0.2.1\"`,\n\t\t`example.com.   SVCB   16 foo.example.org. alpn=\"f\\\\\\\\oo\\\\,bar,h2\"`:                                  `example.com.\t3600\tIN\tSVCB\t16 foo.example.org. alpn=\"f\\\\\\092oo\\\\\\044bar,h2\"`,\n\t\t`example.com.   SVCB   16 foo.example.org. alpn=f\\\\\\092oo\\092,bar,h2`:                                `example.com.\t3600\tIN\tSVCB\t16 foo.example.org. alpn=\"f\\\\\\092oo\\\\\\044bar,h2\"`,\n\t\t// From draft-ietf-add-ddr-06\n\t\t`_dns.example.net. SVCB 1 example.net. alpn=h2 dohpath=/dns-query{?dns}`:     `_dns.example.net.\t3600\tIN\tSVCB\t1 example.net. alpn=\"h2\" dohpath=\"/dns-query{?dns}\"`,\n\t\t`_dns.example.net. SVCB 1 example.net. alpn=h2 dohpath=/dns\\045query{\\?dns}`: `_dns.example.net.\t3600\tIN\tSVCB\t1 example.net. alpn=\"h2\" dohpath=\"/dns-query{?dns}\"`,\n\t\t// From RFC9461 Section 7 (https://datatracker.ietf.org/doc/html/rfc9461#section-7)\n\t\t`_dns.simple.example. 7200 IN SVCB 1 simple.example. alpn=dot`:                                 `_dns.simple.example.\t7200\tIN\tSVCB\t1 simple.example. alpn=\"dot\"`,\n\t\t`_dns.doh.example. 7200 IN SVCB 1 doh.example. alpn=h2 dohpath=/dns-query{?dns}`:               `_dns.doh.example.\t7200\tIN\tSVCB\t1 doh.example. alpn=\"h2\" dohpath=\"/dns-query{?dns}\"`,\n\t\t`_dns.resolver.example.  7200 IN SVCB 1 resolver.example. alpn=dot,doq,h2,h3 dohpath=/q{?dns}`: `_dns.resolver.example.\t7200\tIN\tSVCB\t1 resolver.example. alpn=\"dot,doq,h2,h3\" dohpath=\"/q{?dns}\"`,\n\t\t`_dns.resolver.example.  7200 IN SVCB 2 resolver.example. alpn=dot port=8530`:                  `_dns.resolver.example.\t7200\tIN\tSVCB\t2 resolver.example. alpn=\"dot\" port=\"8530\"`,\n\t\t// From RFC 9540 Section 4.2.1 (https://www.rfc-editor.org/rfc/rfc9540.html#name-the-ohttp-svcparamkey)\n\t\t`_dns.resolver.arpa  7200  IN SVCB 1 doh.example.net alpn=h2 dohpath=/dns-query{?dns} ohttp`: `_dns.resolver.arpa.\t7200\tIN\tSVCB\t1 doh.example.net. alpn=\"h2\" dohpath=\"/dns-query{?dns}\" ohttp=\"\"`,\n\t\t// From RFC 9540 Section 4.1 (HTTPS RR) (https://www.rfc-editor.org/rfc/rfc9540.html#name-use-in-https-service-rrs)\n\t\t`svc.example.com. 7200  IN HTTPS 1 . alpn=h2 ohttp`:         `svc.example.com.\t7200\tIN\tHTTPS\t1 . alpn=\"h2\" ohttp=\"\"`,\n\t\t`svc.example.com. 7200  IN HTTPS 1 . mandatory=ohttp ohttp`: `svc.example.com.\t7200\tIN\tHTTPS\t1 . mandatory=\"ohttp\" ohttp=\"\"`,\n\t}\n\n\tfor s, o := range svcbs {\n\t\trr, err := NewRR(s)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", s, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseBadSVCB(t *testing.T) {\n\theader := `example.com. 3600 IN HTTPS `\n\tevils := []string{\n\t\t`65536 . no-default-alpn`, // bad priority\n\t\t`1 ..`,                    // bad domain\n\t\t`1 . no-default-alpn=1`,   // value illegal\n\t\t`1 . key`,                 // invalid key\n\t\t`1 . key=`,                // invalid key\n\t\t`1 . =`,                   // invalid key\n\t\t`1 . ==`,                  // invalid key\n\t\t`1 . =a`,                  // invalid key\n\t\t`1 . \"\"`,                  // invalid key\n\t\t`1 . \"\"=`,                 // invalid key\n\t\t`1 . \"a\"`,                 // invalid key\n\t\t`1 . \"a\"=`,                // invalid key\n\t\t`1 . key1=`,               // we know that key\n\t\t`1 . key65535`,            // key reserved\n\t\t`1 . key065534`,           // key can't be padded\n\t\t`1 . key65534=\"f`,         // unterminated value\n\t\t`1 . key65534=\"`,          // unterminated value\n\t\t`1 . key65534=\\2`,         // invalid numeric escape\n\t\t`1 . key65534=\\24`,        // invalid numeric escape\n\t\t`1 . key65534=\\256`,       // invalid numeric escape\n\t\t`1 . key65534=\\`,          // invalid numeric escape\n\t\t`1 . key65534=\"\"alpn`,     // zQuote ending needs whitespace\n\t\t`1 . key65534=\"a\"alpn`,    // zQuote ending needs whitespace\n\t\t`1 . ipv6hint=1.1.1.1`,    // not ipv6\n\t\t`1 . ipv6hint=1:1:1:1`,    // not ipv6\n\t\t`1 . ipv6hint=a`,          // not ipv6\n\t\t`1 . ipv6hint=`,           // empty ipv6\n\t\t`1 . ipv4hint=1.1.1.1.1`,  // not ipv4\n\t\t`1 . ipv4hint=::fc`,       // not ipv4\n\t\t`1 . ipv4hint=..11`,       // not ipv4\n\t\t`1 . ipv4hint=a`,          // not ipv4\n\t\t`1 . ipv4hint=`,           // empty ipv4\n\t\t`1 . port=`,               // empty port\n\t\t`1 . echconfig=YUd`,       // bad base64\n\t\t`1 . alpn=h\\`,             // unterminated escape\n\t\t`1 . alpn=h2\\\\.h3`,        // comma-separated list with bad character\n\t\t`1 . alpn=h2,,h3`,         // empty protocol identifier\n\t\t`1 . alpn=h3,`,            // final protocol identifier empty\n\t}\n\tfor _, o := range evils {\n\t\t_, err := NewRR(header + o)\n\t\tif err == nil {\n\t\t\tt.Error(\"failed to reject invalid RR: \", header+o)\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\nfunc TestParseBadNAPTR(t *testing.T) {\n\t// Should look like: mplus.ims.vodafone.com.\t3600\tIN\tNAPTR\t10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.mplus.ims.vodafone.com.\n\tnaptr := `mplus.ims.vodafone.com.\t3600\tIN\tNAPTR\t10 100 S SIP+D2U  _sip._udp.mplus.ims.vodafone.com.`\n\t_, err := NewRR(naptr) // parse fails, we should not have leaked a goroutine.\n\tif err == nil {\n\t\tt.Fatalf(\"parsing NAPTR should have failed: %s\", naptr)\n\t}\n\tif err := goroutineLeaked(); err != nil {\n\t\tt.Errorf(\"leaked goroutines: %s\", err)\n\t}\n}\n\nfunc TestUnbalancedParens(t *testing.T) {\n\tsig := `example.com. 3600 IN RRSIG MX 15 2 3600 (\n              1440021600 1438207200 3613 example.com. (\n              oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f\n              x8A4M3e23mRZ9VrbpMngwcrqNAg== )`\n\t_, err := NewRR(sig)\n\tif err == nil {\n\t\tt.Fatalf(\"failed to detect extra opening brace\")\n\t}\n}\n\nfunc TestBad(t *testing.T) {\n\ttests := []string{\n\t\t`\" TYPE257 9 1E12\\x00\\x105\"`,\n\t\t`\" TYPE256  9 5\"`,\n\t\t`\" TYPE257 0\\\"00000000000000400000000000000000000\\x00\\x10000000000000000000000000000000000 9 l\\x16\\x01\\x005266\"`,\n\t}\n\tfor i := range tests {\n\t\ts, err := strconv.Unquote(tests[i])\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to unquote: %q: %s\", tests[i], err)\n\t\t}\n\t\tif _, err = NewRR(s); err == nil {\n\t\t\tt.Errorf(\"correctly parsed %q\", s)\n\t\t}\n\t}\n}\n\nfunc TestNULLRecord(t *testing.T) {\n\t// packet captured from iodine\n\tpacket := `8116840000010001000000000569627a6c700474657374046d69656b026e6c00000a0001c00c000a0001000000000005497f000001`\n\tdata, _ := hex.DecodeString(packet)\n\tmsg := new(Msg)\n\terr := msg.Unpack(data)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to unpack NULL record\")\n\t}\n\tif _, ok := msg.Answer[0].(*NULL); !ok {\n\t\tt.Fatalf(\"Expected packet to contain NULL record\")\n\t}\n}\nfunc TestAAAAParsing(t *testing.T) {\n\ttests := []string{\n\t\t\"2001:db8::1\",\n\t\t\"1::1\",\n\t\t\"2001:db81:d2b4:b6ba:50db:49cc:a8d1:5bb1\",\n\t\t\"::ffff:192.0.2.0\",\n\t}\n\n\trrPrefix := \".\\t1\\tIN\\tAAAA\\t\"\n\n\tfor num, tc := range tests {\n\t\tt.Run(fmt.Sprintf(\"Test %d\", num), func(t *testing.T) {\n\t\t\trr, err := NewRR(rrPrefix + tc)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"failed to parse RR: %s\", err)\n\t\t\t}\n\t\t\t// Output presentation format and try to parse again\n\t\t\treparseRR, err := NewRR(rr.String())\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"failed to reparse RR: %s\", err)\n\t\t\t}\n\t\t\tif reparseRR.String() != rrPrefix+tc {\n\t\t\t\tt.Errorf(\"expected %s,got %s\", rrPrefix+tc, reparseRR.String())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseAPL(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tin     string\n\t\texpect string\n\t}{\n\t\t{\n\t\t\t\"v4\",\n\t\t\t\". APL 1:192.0.2.0/24\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t1:192.0.2.0/24\",\n\t\t},\n\t\t{\n\t\t\t\"v6\",\n\t\t\t\". APL 2:2001:db8::/32\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t2:2001:db8::/32\",\n\t\t},\n\t\t{\n\t\t\t\"null v6\",\n\t\t\t\". APL 2:::/0\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t2:::/0\",\n\t\t},\n\t\t{\n\t\t\t\"null v4\",\n\t\t\t\". APL 1:0.0.0.0/0\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t1:0.0.0.0/0\",\n\t\t},\n\t\t{\n\t\t\t\"full v6\",\n\t\t\t\". APL 2:::/0\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t2:::/0\",\n\t\t},\n\t\t{\n\t\t\t\"full v4\",\n\t\t\t\". APL 1:192.0.2.1/32\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t1:192.0.2.1/32\",\n\t\t},\n\t\t{\n\t\t\t\"full v6\",\n\t\t\t\". APL 2:2001:0db8:d2b4:b6ba:50db:49cc:a8d1:5bb1/128\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t2:2001:db8:d2b4:b6ba:50db:49cc:a8d1:5bb1/128\",\n\t\t},\n\t\t{\n\t\t\t\"v4in6\",\n\t\t\t\". APL 2:::ffff:192.0.2.0/120\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t2:::ffff:192.0.2.0/120\",\n\t\t},\n\t\t{\n\t\t\t\"v4in6 v6 syntax\",\n\t\t\t\". APL 2:::ffff:c000:0200/120\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t2:::ffff:192.0.2.0/120\",\n\t\t},\n\t\t{\n\t\t\t\"negate\",\n\t\t\t\". APL !1:192.0.2.0/24\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t!1:192.0.2.0/24\",\n\t\t},\n\t\t{\n\t\t\t\"multiple\",\n\t\t\t\". APL 1:192.0.2.0/24 !1:192.0.2.1/32 2:2001:db8::/32 !2:2001:db8:1::0/48\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t1:192.0.2.0/24 !1:192.0.2.1/32 2:2001:db8::/32 !2:2001:db8:1::/48\",\n\t\t},\n\t\t{\n\t\t\t\"no address\",\n\t\t\t\". APL\",\n\t\t\t\".\\t3600\\tIN\\tAPL\\t\",\n\t\t},\n\t}\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\trr, err := NewRR(tc.in)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"failed to parse RR: %s\", err)\n\t\t\t}\n\n\t\t\tgot := rr.String()\n\t\t\tif got != tc.expect {\n\t\t\t\tt.Errorf(\"expected %q, got %q\", tc.expect, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseAPLErrors(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   string\n\t}{\n\t\t{\n\t\t\t\"unexpected\",\n\t\t\t`. APL \"\"`,\n\t\t},\n\t\t{\n\t\t\t\"unrecognized family\",\n\t\t\t\". APL 3:0.0.0.0/0\",\n\t\t},\n\t\t{\n\t\t\t\"malformed family\",\n\t\t\t\". APL foo:0.0.0.0/0\",\n\t\t},\n\t\t{\n\t\t\t\"malformed address\",\n\t\t\t\". APL 1:192.0.2/16\",\n\t\t},\n\t\t{\n\t\t\t\"extra bits\",\n\t\t\t\". APL 2:2001:db8::/0\",\n\t\t},\n\t\t{\n\t\t\t\"address mismatch v2\",\n\t\t\t\". APL 1:2001:db8::/64\",\n\t\t},\n\t\t{\n\t\t\t\"address mismatch v6\",\n\t\t\t\". APL 2:192.0.2.1/32\",\n\t\t},\n\t\t{\n\t\t\t\"no prefix\",\n\t\t\t\". APL 1:192.0.2.1\",\n\t\t},\n\t\t{\n\t\t\t\"no family\",\n\t\t\t\". APL 0.0.0.0/0\",\n\t\t},\n\t}\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t_, err := NewRR(tc.in)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"expected error, got none\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnpackRRWithHeaderInvalidLengths(t *testing.T) {\n\trr, err := NewRR(\"test.example.org. 300 IN SSHFP 1 2 BC6533CDC95A79078A39A56EA7635984ED655318ADA9B6159E30723665DA95BB\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse SSHFP record: %v\", err)\n\t}\n\n\tbuf := make([]byte, Len(rr))\n\theaderEnd, end, err := packRR(rr, buf, 0, compressionMap{}, false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to pack A record: %v\", err)\n\t}\n\n\trr.Header().Rdlength = uint16(end - headerEnd)\n\tfor _, off := range []int{\n\t\t-1,\n\t\tend + 1,\n\t\t1<<16 - 1,\n\t} {\n\t\t_, _, err := UnpackRRWithHeader(*rr.Header(), buf, off)\n\t\tif de, ok := err.(*Error); !ok || de.err != \"bad off\" {\n\t\t\tt.Errorf(\"UnpackRRWithHeader with bad offset (%d) returned wrong or no error: %v\", off, err)\n\t\t}\n\t}\n\n\tfor _, rdlength := range []uint16{\n\t\tuint16(end - headerEnd + 1),\n\t\tuint16(end),\n\t\t1<<16 - 1,\n\t} {\n\t\trr.Header().Rdlength = rdlength\n\n\t\t_, _, err := UnpackRRWithHeader(*rr.Header(), buf, headerEnd)\n\t\tif de, ok := err.(*Error); !ok || de.err != \"bad rdlength\" {\n\t\t\tt.Errorf(\"UnpackRRWithHeader with bad rdlength (%d) returned wrong or no error: %v\", rdlength, err)\n\t\t}\n\t}\n}\n\nfunc TestParseZONEMD(t *testing.T) {\n\t// Uses examples from https://tools.ietf.org/html/rfc8976\n\tdt := map[string]string{\n\t\t// Simple Zone\n\t\t`example.\t86400\tIN\tZONEMD\t2018031900 1 1 (\n\t\t\t\t\t\t\t\t\t\tc68090d90a7aed71\n\t\t\t\t\t\t\t\t\t\t6bc459f9340e3d7c\n\t\t\t\t\t\t\t\t\t\t1370d4d24b7e2fc3\n\t\t\t\t\t\t\t\t\t\ta1ddc0b9a87153b9\n\t\t\t\t\t\t\t\t\t\ta9713b3c9ae5cc27\n\t\t\t\t\t\t\t\t\t\t777f98b8e730044c )\n\t\t`: \"example.\\t86400\\tIN\\tZONEMD\\t2018031900 1 1 c68090d90a7aed716bc459f9340e3d7c1370d4d24b7e2fc3a1ddc0b9a87153b9a9713b3c9ae5cc27777f98b8e730044c\",\n\t\t// Complex Zone\n\t\t`example.\t86400\tIN\tZONEMD\t2018031900 1 1 (\n\t\t\t\t\t\t\t\t\t\ta3b69bad980a3504\n\t\t\t\t\t\t\t\t\t\te1cffcb0fd6397f9\n\t\t\t\t\t\t\t\t\t\t3848071c93151f55\n\t\t\t\t\t\t\t\t\t\t2ae2f6b1711d4bd2\n\t\t\t\t\t\t\t\t\t\td8b39808226d7b9d\n\t\t\t\t\t\t\t\t\t\tb71e34b72077f8fe )\n\t\t`: \"example.\\t86400\\tIN\\tZONEMD\\t2018031900 1 1 a3b69bad980a3504e1cffcb0fd6397f93848071c93151f552ae2f6b1711d4bd2d8b39808226d7b9db71e34b72077f8fe\",\n\t\t// Multiple Digests Zone\n\t\t`example.\t86400\tIN\tZONEMD\t2018031900 1 1 (\n\t\t\t\t\t\t\t\t\t\t62e6cf51b02e54b9\n\t\t\t\t\t\t\t\t\t\tb5f967d547ce4313\n\t\t\t\t\t\t\t\t\t\t6792901f9f88e637\n\t\t\t\t\t\t\t\t\t\t493daaf401c92c27\n\t\t\t\t\t\t\t\t\t\t9dd10f0edb1c56f8\n\t\t\t\t\t\t\t\t\t\t080211f8480ee306 )\n\t\t`: \"example.\\t86400\\tIN\\tZONEMD\\t2018031900 1 1 62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306\",\n\t\t`example.\t86400\tIN\tZONEMD\t2018031900 1 2 (\n\t\t\t\t\t\t\t\t\t\t08cfa1115c7b948c\n\t\t\t\t\t\t\t\t\t\t4163a901270395ea\n\t\t\t\t\t\t\t\t\t\t226a930cd2cbcf2f\n\t\t\t\t\t\t\t\t\t\ta9a5e6eb85f37c8a\n\t\t\t\t\t\t\t\t\t\t4e114d884e66f176\n\t\t\t\t\t\t\t\t\t\teab121cb02db7d65\n\t\t\t\t\t\t\t\t\t\t2e0cc4827e7a3204\n\t\t\t\t\t\t\t\t\t\tf166b47e5613fd27 )\n\t\t`: \"example.\\t86400\\tIN\\tZONEMD\\t2018031900 1 2 08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27\",\n\t\t`example.\t86400\tIN\tZONEMD\t2018031900 1 240 (\n\t\t\t\t\t\t\t\t\t\te2d523f654b9422a\n\t\t\t\t\t\t\t\t\t\t96c5a8f44607bbee )\n\t\t`: \"example.\t86400\tIN\tZONEMD\t2018031900 1 240 e2d523f654b9422a96c5a8f44607bbee\",\n\t\t`example.\t86400\tIN\tZONEMD\t2018031900 241 1 (\n\t\t\t\t\t\t\t\t\t\te1846540e33a9e41\n\t\t\t\t\t\t\t\t\t\t89792d18d5d131f6\n\t\t\t\t\t\t\t\t\t\t05fc283e )\n\t\t`: \"example.\t86400\tIN\tZONEMD\t2018031900 241 1 e1846540e33a9e4189792d18d5d131f605fc283e\",\n\t\t// URI.ARPA zone\n\t\t`uri.arpa.\t\t3600\tIN\t\tZONEMD\t2018100702 1 1 (\n\t\t\t0dbc3c4dbfd75777c12ca19c337854b1577799901307c482e9d91d5d15\n\t\t\tcd934d16319d98e30c4201cf25a1d5a0254960 )`: \"uri.arpa.\\t3600\\tIN\\tZONEMD\\t2018100702 1 1 0dbc3c4dbfd75777c12ca19c337854b1577799901307c482e9d91d5d15cd934d16319d98e30c4201cf25a1d5a0254960\",\n\t\t// ROOT-SERVERS.NET Zone\n\t\t`root-servers.net.     3600000 IN  ZONEMD  2018091100 1 1 (\n\t\t\tf1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a97\n\t\t\t8a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79 )\n\t\t`: \"root-servers.net.\\t3600000\\tIN\\tZONEMD\\t2018091100 1 1 f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a978a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79\",\n\t}\n\tfor i, o := range dt {\n\t\trr, err := NewRR(i)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\tif rr.String() != o {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", i, o, rr.String())\n\t\t}\n\t}\n}\n\nfunc TestParseIPSECKEY(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"ipseckey. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\":                                  \"ipseckey.\\t3600\\tIN\\tIPSECKEY\\t10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\",\n\t\t\"ipseckey. 3600 IN IPSECKEY 10 1 2 1.2.3.4 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\":                            \"ipseckey.\\t3600\\tIN\\tIPSECKEY\\t10 1 2 1.2.3.4 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\",\n\t\t\"ipseckey. 3600 IN IPSECKEY 10 2 2 2001:470:30:84:e276:63ff:fe72:3900 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\": \"ipseckey.\\t3600\\tIN\\tIPSECKEY\\t10 2 2 2001:470:30:84:e276:63ff:fe72:3900 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\",\n\t\t\"ipseckey. 3600 IN IPSECKEY 10 3 2 ipseckey2. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\":                         \"ipseckey.\\t3600\\tIN\\tIPSECKEY\\t10 3 2 ipseckey2. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==\",\n\t}\n\n\tfor i, o := range dt {\n\t\trr := testRR(i).(*IPSECKEY)\n\t\tif s := rr.String(); s != o {\n\t\t\tt.Errorf(\"input %#v does not match expected output %#v\", s, o)\n\t\t}\n\t}\n}\n\nfunc TestParseAMTRELAY(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"amtrelay. 3600 IN AMTRELAY 10 0 0 .\":                                  \"amtrelay.\\t3600\\tIN\\tAMTRELAY\\t10 0 0 .\",\n\t\t\"amtrelay. 3600 IN AMTRELAY 10 0 1 1.2.3.4\":                            \"amtrelay.\\t3600\\tIN\\tAMTRELAY\\t10 0 1 1.2.3.4\",\n\t\t\"amtrelay. 3600 IN AMTRELAY 10 1 2 2001:470:30:84:e276:63ff:fe72:3900\": \"amtrelay.\\t3600\\tIN\\tAMTRELAY\\t10 1 2 2001:470:30:84:e276:63ff:fe72:3900\",\n\t\t\"amtrelay. 3600 IN AMTRELAY 10 1 3 amtrelay2.\":                         \"amtrelay.\\t3600\\tIN\\tAMTRELAY\\t10 1 3 amtrelay2.\",\n\t}\n\n\tfor i, o := range dt {\n\t\trr := testRR(i).(*AMTRELAY)\n\t\tif s := rr.String(); s != o {\n\t\t\tt.Errorf(\"input %#v does not match expected output %#v\", s, o)\n\t\t}\n\t}\n}\n\nfunc TestParseOPENPGPKEY(t *testing.T) {\n\tdt := map[string]string{\n\t\t\"2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db._openpgpkey 3600 IN OPENPGPKEY mDMEZCMu8xYJKwYBBAHaRw8BAQdAH4FTbN/H5SoMBl9Ez2cFQ1NuzymK894fq2ffsYDvRkG0EWFsaWNlQGV4YW1wbGUuY29tiJYEExYKAD4CGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AWIQRjw8oAQytQxDz5Q/Io7xpohfeBngUCZCMv5gUJAAk7ZgAKCRAo7xpohfeBnlmVAP9k0slIpLwddCD1bZ9qVjqzNcS743OIDny7XuH6x02L2wEAwxqAotO7/oUm0L4wyYR6hvGlhuGMSZXc9xMwZ1wVcA8=\":     \"2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db._openpgpkey.\\t3600\\tIN\\tOPENPGPKEY\\tmDMEZCMu8xYJKwYBBAHaRw8BAQdAH4FTbN/H5SoMBl9Ez2cFQ1NuzymK894fq2ffsYDvRkG0EWFsaWNlQGV4YW1wbGUuY29tiJYEExYKAD4CGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AWIQRjw8oAQytQxDz5Q/Io7xpohfeBngUCZCMv5gUJAAk7ZgAKCRAo7xpohfeBnlmVAP9k0slIpLwddCD1bZ9qVjqzNcS743OIDny7XuH6x02L2wEAwxqAotO7/oUm0L4wyYR6hvGlhuGMSZXc9xMwZ1wVcA8=\",\n\t\t\"2bb5bc4202aaecd48dcb54967c8e7f1b7574a436f04e0d15534b20e5._openpgpkey 3600 IN OPENPGPKEY mDMEZCMxgRYJKwYBBAHaRw8BAQdA/fgtlQjGflt2MUMWhRZRnH5Hg+BY9sQTeePmqqUs+lK0Fem6u+iho+WtkEBleGFtcGxlLmNvbYiWBBMWCgA+AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAFiEEIWsEkWx5wygGCb61+tJ3q3m88E0FAmQjMbMFCQAJOqwACgkQ+tJ3q3m88E0z4gEAtowKJMPefyV5YCW8VubgXK7Fa+hjwXOPSsHnEnJw9pUBAL+VZvNZv/VZvyGGMd31Yivqerzl6q+VIkZ6XffVb2AB\": \"2bb5bc4202aaecd48dcb54967c8e7f1b7574a436f04e0d15534b20e5._openpgpkey.\\t3600\\tIN\\tOPENPGPKEY\\tmDMEZCMxgRYJKwYBBAHaRw8BAQdA/fgtlQjGflt2MUMWhRZRnH5Hg+BY9sQTeePmqqUs+lK0Fem6u+iho+WtkEBleGFtcGxlLmNvbYiWBBMWCgA+AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAFiEEIWsEkWx5wygGCb61+tJ3q3m88E0FAmQjMbMFCQAJOqwACgkQ+tJ3q3m88E0z4gEAtowKJMPefyV5YCW8VubgXK7Fa+hjwXOPSsHnEnJw9pUBAL+VZvNZv/VZvyGGMd31Yivqerzl6q+VIkZ6XffVb2AB\",\n\t\t\"2bb5bc4202aaecd48dcb54967c8e7f1b7574a436f04e0d15534b20e5._openpgpkey 3600 IN OPENPGPKEY mQINBGQjcLcBEADfQ2Ob7oiBqBuZOxW1ikn3Agp8HdOm1C1QNlz8Sdic6kAwzRIHmVrpLYJOVVCPOxF82XZJCHi/s31xQupfKCbaWcIgrJTHHkHXlF6ER8S/0DQcCJV5ZAe5z3Fnc1we4uTgazlsiuj/YOr9yozScO7yCDU7l6vAnUk835rpWdOhFy7G+9v3VORmLL4d6F1ONyIE4Koity3y0qNGE7Ei0D8HarSAr2hsbx1XGuxW5weo1nxrS8iQQkhJP5yjWkfIrsyYaBvwoX8fqh7CSKHpP13zxQ93BtcWqPM5Cxt34wFWIrHTtAfIE+Fl2H+Q5jZos/fN7dUxgHT3FJOtjXIL2f5prsjFq5xBOQ90CNW0yvWdhGI5uFUFX5/yFO+sMSTiEbQGOiQ//Z7829HGG+A3kGWJYohWlTW2yhwL/MXnVn0ZmiGR2VcspqYd+sEQk/G3Iqs+4jxdx78YsZOdZNYIdtjrTEhS4MXbnavSAdx0riniKEZjQMo36hxh4lpohPEisj7h8NoZoUKSe33k3WeF13dzad/kb7Qj0JtQL98dy343aRznQsIYP6yXEjB+/pkKmTC83rorOd3bqiptEbRPqA4II+K3YZUQh7hB7ixI5bH35vs5W5aaE61w4eC39Ftc2Bv/BIRAxU4xYhwRiME6j5zmkwyt/Wt8YJeV6d76Uofn3wARAQABtBXpurvooaPlrZBAZXhhbXBsZS5jb22JAlQEEwEKAD4WIQRm20sNRuRfkhCOidV7PHUEvXBR2wUCZCNwtwIbAwUJAAk6UgULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB7PHUEvXBR22pND/9C3kW3ysKOkgM2Z/tw1mNY/4xy2Zap8LM7DC9niFBKj6f+Yz0vTuu6EvfLh3YQBB7zd2xLEs1M8nneNYI9CZfW9zPuwPG+BoIIouZaXzqnyQZz1hVLWW7YcFIv9hWuc5ZyYh0qs58HO47cfl3wi6TqVZKHyYznkw4/n7NHkFuMex1qJGx/LDbXiXMJB3shrWa2WTn3ONjJ5VicjLqPUye7ASXvACtgddLoOPlK72GN8/bNvEaUT53kYuy659ESTiIvngzUDr215cH/upljaGMrKCDrHAVoyar6ePgmCopN2QzcFM2rlaLzbnBwMkBfVCFe+K/kI+v6ByiwSK8hInVqaS2tj0fUKGk/d4MQFIMI86YBhS3O+PUJNF+VG45tZe5QzicEPXJz+olzd3BFrH1I1xWwDhYsWn2zlJb8qWYDzGnZYcTlJk1/136AGSnYd8TAbD013A+OVwNy2/VDjS4M8krjGfJ1uVHH7bKYINvdQxmohnOWoddj0u2TcSpqTZA7VGjOad2oNwxoTMnEmF8Iw5ASWHbjuzFZR9LfDfsuqPB6DBgiDmLMG36OCfwNm0E1mE8F8XaSqeiRrfRM1OVjKvJmMln2Pul9HMmx5MYhhUwhtn3VHG5UPwaSX9sdOC3vt3HYr9odNJt3MZnG0btI6+z1RrSK5GDSkboDXYBrsrkCDQRkI3C3ARAAsow2zqcOrCu9kuyz+lq2Ke5rBz9E0HH0xOZ7ZYDk/w4AjzXQqmGV1yPqELa9PQgz3I6ka5bmQ8XW+/oiEKpK4ZLMvEIneKB4UzyDg8qIdJwXmZxA5YVyeExjuc+5sKX4VCmFX9JGjcNT0tDe1gDLapJNKzkvxSVaX6Bb1A8NSkeHMK/ynwptoSlsopkL6LreL8VO4LfAdN8N19hoOpOVzCbNDFjj3YDH3Af+Z/lkMlcUKwP3g6iQl2p42uObyedcvOqTWFHrBLH2w+HEyv3uLioimOx0WMd0uWkK98UnGfhQ8i60wRfT+7E3HmPuCQ+V8eNGd3xS1J/OkK11M2/999X7WnCwQm/qDDdWcS9tycNiEHhAarYm6moSBbCW2jLKbkJEc/6IS3r04RYp5ZLhsPZVVgKyFT2QpGJVdGs/oS4VtyAE+yh4dJxL3VvjbQpLBNOnFSfm67UqnbbpmFfqEU8fnTWuNKPSSBa5hR8vz27XzuAyc2zhNgCmyvNgK2pLo2dDPRVsnTv1pR0n9K/b3BbH7I1mZSk6m1pnM63imcCP3McWRbL0iPT3bPYNye0n9YZIJZ1HAzc/AUAJ1oMJN/CF5hXDPggU3jjr+79rm3qjLTOkjEHmTauKtDHh+Jw77KvevwqX1rymjHNgl2FM7hRxkm10+huPQksdONIApfUAEQEAAYkCPAQYAQoAJhYhBGbbSw1G5F+SEI6J1Xs8dQS9cFHbBQJkI3C3AhsMBQkACTpSAAoJEHs8dQS9cFHbycgQAKq6DjwaZZP1XA2yhoMM8yVUpGTtPaBx5/fDiT7pzTy8GU3MCfYXT9kExPvBqTr2faI3gBJ+bMNkPYpmSUHq+kW1i8Q8Ibr7d3PFc83q0ZyEwPr57nlaF08Hiw7ZkTr1py55fwKF4eEZUoF0SX9AFP75FdXpAVT8/w6/gYsGwyPz4Hn08bi/7UUI0xnxtEUu8K0fheL0fLyu6Qhm7NNOnzXOwZAYV6AWrXvitsspglQE9di17sI5tu3plR/ZvnQ3tVllJQubH1x6P2+/MeXaSILOJ7LcJEAj5hYAVH6YPb0GuRx+bm5d4lNKEeII+HYhsaqGCkdwDVTiM25soe9hN7z8f+pxxhmPlCh1DlDLdr/zp9etshne4mgY9KrJD9Yjm53VCi0zhlUpEigeIiXhsh1wlG1+63C594hihXRWpA+KMjecHZzMfS4LQRs3lthN5QTdOHkKeX4ClulZV1FS+eq5kSpt/p4r9KaR1qLiZyaV43Z1ZgNfD6gbD5iC1oxYjy2tj0/hV1OWPcW0Fj+xSwmMVvGCI0dqrjO9tLnF4w4+ddaHtryBbtlAyV4HOtKoNxiBVf/Up6EOOPS6J7LOH6EYkOZwoPwBXaEdkASoAo6vTDgqBA2lIcwPg7jKX+o07McITk9BACAfxUV3oPR2nFmTGbxgY4MStUPo55P6VCt3\": \"2bb5bc4202aaecd48dcb54967c8e7f1b7574a436f04e0d15534b20e5._openpgpkey.\\t3600\\tIN\\tOPENPGPKEY\\tmQINBGQjcLcBEADfQ2Ob7oiBqBuZOxW1ikn3Agp8HdOm1C1QNlz8Sdic6kAwzRIHmVrpLYJOVVCPOxF82XZJCHi/s31xQupfKCbaWcIgrJTHHkHXlF6ER8S/0DQcCJV5ZAe5z3Fnc1we4uTgazlsiuj/YOr9yozScO7yCDU7l6vAnUk835rpWdOhFy7G+9v3VORmLL4d6F1ONyIE4Koity3y0qNGE7Ei0D8HarSAr2hsbx1XGuxW5weo1nxrS8iQQkhJP5yjWkfIrsyYaBvwoX8fqh7CSKHpP13zxQ93BtcWqPM5Cxt34wFWIrHTtAfIE+Fl2H+Q5jZos/fN7dUxgHT3FJOtjXIL2f5prsjFq5xBOQ90CNW0yvWdhGI5uFUFX5/yFO+sMSTiEbQGOiQ//Z7829HGG+A3kGWJYohWlTW2yhwL/MXnVn0ZmiGR2VcspqYd+sEQk/G3Iqs+4jxdx78YsZOdZNYIdtjrTEhS4MXbnavSAdx0riniKEZjQMo36hxh4lpohPEisj7h8NoZoUKSe33k3WeF13dzad/kb7Qj0JtQL98dy343aRznQsIYP6yXEjB+/pkKmTC83rorOd3bqiptEbRPqA4II+K3YZUQh7hB7ixI5bH35vs5W5aaE61w4eC39Ftc2Bv/BIRAxU4xYhwRiME6j5zmkwyt/Wt8YJeV6d76Uofn3wARAQABtBXpurvooaPlrZBAZXhhbXBsZS5jb22JAlQEEwEKAD4WIQRm20sNRuRfkhCOidV7PHUEvXBR2wUCZCNwtwIbAwUJAAk6UgULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB7PHUEvXBR22pND/9C3kW3ysKOkgM2Z/tw1mNY/4xy2Zap8LM7DC9niFBKj6f+Yz0vTuu6EvfLh3YQBB7zd2xLEs1M8nneNYI9CZfW9zPuwPG+BoIIouZaXzqnyQZz1hVLWW7YcFIv9hWuc5ZyYh0qs58HO47cfl3wi6TqVZKHyYznkw4/n7NHkFuMex1qJGx/LDbXiXMJB3shrWa2WTn3ONjJ5VicjLqPUye7ASXvACtgddLoOPlK72GN8/bNvEaUT53kYuy659ESTiIvngzUDr215cH/upljaGMrKCDrHAVoyar6ePgmCopN2QzcFM2rlaLzbnBwMkBfVCFe+K/kI+v6ByiwSK8hInVqaS2tj0fUKGk/d4MQFIMI86YBhS3O+PUJNF+VG45tZe5QzicEPXJz+olzd3BFrH1I1xWwDhYsWn2zlJb8qWYDzGnZYcTlJk1/136AGSnYd8TAbD013A+OVwNy2/VDjS4M8krjGfJ1uVHH7bKYINvdQxmohnOWoddj0u2TcSpqTZA7VGjOad2oNwxoTMnEmF8Iw5ASWHbjuzFZR9LfDfsuqPB6DBgiDmLMG36OCfwNm0E1mE8F8XaSqeiRrfRM1OVjKvJmMln2Pul9HMmx5MYhhUwhtn3VHG5UPwaSX9sdOC3vt3HYr9odNJt3MZnG0btI6+z1RrSK5GDSkboDXYBrsrkCDQRkI3C3ARAAsow2zqcOrCu9kuyz+lq2Ke5rBz9E0HH0xOZ7ZYDk/w4AjzXQqmGV1yPqELa9PQgz3I6ka5bmQ8XW+/oiEKpK4ZLMvEIneKB4UzyDg8qIdJwXmZxA5YVyeExjuc+5sKX4VCmFX9JGjcNT0tDe1gDLapJNKzkvxSVaX6Bb1A8NSkeHMK/ynwptoSlsopkL6LreL8VO4LfAdN8N19hoOpOVzCbNDFjj3YDH3Af+Z/lkMlcUKwP3g6iQl2p42uObyedcvOqTWFHrBLH2w+HEyv3uLioimOx0WMd0uWkK98UnGfhQ8i60wRfT+7E3HmPuCQ+V8eNGd3xS1J/OkK11M2/999X7WnCwQm/qDDdWcS9tycNiEHhAarYm6moSBbCW2jLKbkJEc/6IS3r04RYp5ZLhsPZVVgKyFT2QpGJVdGs/oS4VtyAE+yh4dJxL3VvjbQpLBNOnFSfm67UqnbbpmFfqEU8fnTWuNKPSSBa5hR8vz27XzuAyc2zhNgCmyvNgK2pLo2dDPRVsnTv1pR0n9K/b3BbH7I1mZSk6m1pnM63imcCP3McWRbL0iPT3bPYNye0n9YZIJZ1HAzc/AUAJ1oMJN/CF5hXDPggU3jjr+79rm3qjLTOkjEHmTauKtDHh+Jw77KvevwqX1rymjHNgl2FM7hRxkm10+huPQksdONIApfUAEQEAAYkCPAQYAQoAJhYhBGbbSw1G5F+SEI6J1Xs8dQS9cFHbBQJkI3C3AhsMBQkACTpSAAoJEHs8dQS9cFHbycgQAKq6DjwaZZP1XA2yhoMM8yVUpGTtPaBx5/fDiT7pzTy8GU3MCfYXT9kExPvBqTr2faI3gBJ+bMNkPYpmSUHq+kW1i8Q8Ibr7d3PFc83q0ZyEwPr57nlaF08Hiw7ZkTr1py55fwKF4eEZUoF0SX9AFP75FdXpAVT8/w6/gYsGwyPz4Hn08bi/7UUI0xnxtEUu8K0fheL0fLyu6Qhm7NNOnzXOwZAYV6AWrXvitsspglQE9di17sI5tu3plR/ZvnQ3tVllJQubH1x6P2+/MeXaSILOJ7LcJEAj5hYAVH6YPb0GuRx+bm5d4lNKEeII+HYhsaqGCkdwDVTiM25soe9hN7z8f+pxxhmPlCh1DlDLdr/zp9etshne4mgY9KrJD9Yjm53VCi0zhlUpEigeIiXhsh1wlG1+63C594hihXRWpA+KMjecHZzMfS4LQRs3lthN5QTdOHkKeX4ClulZV1FS+eq5kSpt/p4r9KaR1qLiZyaV43Z1ZgNfD6gbD5iC1oxYjy2tj0/hV1OWPcW0Fj+xSwmMVvGCI0dqrjO9tLnF4w4+ddaHtryBbtlAyV4HOtKoNxiBVf/Up6EOOPS6J7LOH6EYkOZwoPwBXaEdkASoAo6vTDgqBA2lIcwPg7jKX+o07McITk9BACAfxUV3oPR2nFmTGbxgY4MStUPo55P6VCt3\",\n\t}\n\tfor i, o := range dt {\n\t\trr := testRR(i).(*OPENPGPKEY)\n\t\tif s := rr.String(); s != o {\n\t\t\tt.Errorf(\"input %#v does not match expected output %#v\", s, o)\n\t\t}\n\t}\n}\n\nfunc TestParseRRSIGAlgNames(t *testing.T) {\n\ttests := map[string]uint8{\n\t\t`miek.nl.  IN RRSIG SOA RSASHA1 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:         RSASHA1,\n\t\t`miek.nl.  IN RRSIG SOA RSAMD5 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:          RSAMD5,\n\t\t`miek.nl.  IN RRSIG SOA ECC-GOST 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:        ECCGOST,\n\t\t`miek.nl.  IN RRSIG SOA ED448 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:           ED448,\n\t\t`miek.nl.  IN RRSIG SOA ECDSAP256SHA256 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: ECDSAP256SHA256,\n\t\t`miek.nl.  IN RRSIG SOA INDIRECT 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:        INDIRECT,\n\t\t`miek.nl.  IN RRSIG SOA BLA 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:             0,\n\t\t`miek.nl.  IN RRSIG SOA - 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`:               0,\n\t}\n\tfor r, alg := range tests {\n\t\trr, err := NewRR(r)\n\t\tif alg != 0 && err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\t\tif alg != 0 && rr.(*RRSIG).Algorithm != alg {\n\t\t\tt.Errorf(\"expecting alg %d, got %d\", alg, rr.(*RRSIG).Algorithm)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "privaterr.go",
    "content": "package dns\n\nimport \"strings\"\n\n// PrivateRdata is an interface used for implementing \"Private Use\" RR types, see\n// RFC 6895. This allows one to experiment with new RR types, without requesting an\n// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.\ntype PrivateRdata interface {\n\t// String returns the text presentation of the Rdata of the Private RR.\n\tString() string\n\t// Parse parses the Rdata of the private RR.\n\tParse([]string) error\n\t// Pack is used when packing a private RR into a buffer.\n\tPack([]byte) (int, error)\n\t// Unpack is used when unpacking a private RR from a buffer.\n\tUnpack([]byte) (int, error)\n\t// Copy copies the Rdata into the PrivateRdata argument.\n\tCopy(PrivateRdata) error\n\t// Len returns the length in octets of the Rdata.\n\tLen() int\n}\n\n// PrivateRR represents an RR that uses a PrivateRdata user-defined type.\n// It mocks normal RRs and implements dns.RR interface.\ntype PrivateRR struct {\n\tHdr  RR_Header\n\tData PrivateRdata\n\n\tgenerator func() PrivateRdata // for copy\n}\n\n// Header return the RR header of r.\nfunc (r *PrivateRR) Header() *RR_Header { return &r.Hdr }\n\nfunc (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }\n\n// Private len and copy parts to satisfy RR interface.\nfunc (r *PrivateRR) len(off int, compression map[string]struct{}) int {\n\tl := r.Hdr.len(off, compression)\n\tl += r.Data.Len()\n\treturn l\n}\n\nfunc (r *PrivateRR) copy() RR {\n\t// make new RR like this:\n\trr := &PrivateRR{r.Hdr, r.generator(), r.generator}\n\n\tif err := r.Data.Copy(rr.Data); err != nil {\n\t\tpanic(\"dns: got value that could not be used to copy Private rdata: \" + err.Error())\n\t}\n\n\treturn rr\n}\n\nfunc (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {\n\tn, err := r.Data.Pack(msg[off:])\n\tif err != nil {\n\t\treturn len(msg), err\n\t}\n\toff += n\n\treturn off, nil\n}\n\nfunc (r *PrivateRR) unpack(msg []byte, off int) (int, error) {\n\toff1, err := r.Data.Unpack(msg[off:])\n\toff += off1\n\treturn off, err\n}\n\nfunc (r *PrivateRR) parse(c *zlexer, origin string) *ParseError {\n\tvar l lex\n\ttext := make([]string, 0, 2) // could be 0..N elements, median is probably 1\nFetch:\n\tfor {\n\t\t// TODO(miek): we could also be returning _QUOTE, this might or might not\n\t\t// be an issue (basically parsing TXT becomes hard)\n\t\tswitch l, _ = c.Next(); l.value {\n\t\tcase zNewline, zEOF:\n\t\t\tbreak Fetch\n\t\tcase zString:\n\t\t\ttext = append(text, l.token)\n\t\t}\n\t}\n\n\terr := r.Data.Parse(text)\n\tif err != nil {\n\t\treturn &ParseError{wrappedErr: err, lex: l}\n\t}\n\n\treturn nil\n}\n\nfunc (r *PrivateRR) isDuplicate(r2 RR) bool { return false }\n\n// PrivateHandle registers a private resource record type. It requires\n// string and numeric representation of private RR type and generator function as argument.\nfunc PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {\n\trtypestr = strings.ToUpper(rtypestr)\n\n\tTypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator(), generator} }\n\tTypeToString[rtype] = rtypestr\n\tStringToType[rtypestr] = rtype\n}\n\n// PrivateHandleRemove removes definitions required to support private RR type.\nfunc PrivateHandleRemove(rtype uint16) {\n\trtypestr, ok := TypeToString[rtype]\n\tif ok {\n\t\tdelete(TypeToRR, rtype)\n\t\tdelete(TypeToString, rtype)\n\t\tdelete(StringToType, rtypestr)\n\t}\n}\n"
  },
  {
    "path": "privaterr_test.go",
    "content": "package dns_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/miekg/dns\"\n)\n\nconst TypeISBN uint16 = 0xFF00\n\n// A crazy new RR type :)\ntype ISBN struct {\n\tx string // rdata with 10 or 13 numbers, dashes or spaces allowed\n}\n\nfunc NewISBN() dns.PrivateRdata { return &ISBN{\"\"} }\n\nfunc (rd *ISBN) Len() int       { return len([]byte(rd.x)) }\nfunc (rd *ISBN) String() string { return rd.x }\n\nfunc (rd *ISBN) Parse(txt []string) error {\n\trd.x = strings.TrimSpace(strings.Join(txt, \" \"))\n\treturn nil\n}\n\nfunc (rd *ISBN) Pack(buf []byte) (int, error) {\n\tb := []byte(rd.x)\n\tn := copy(buf, b)\n\tif n != len(b) {\n\t\treturn n, dns.ErrBuf\n\t}\n\treturn n, nil\n}\n\nfunc (rd *ISBN) Unpack(buf []byte) (int, error) {\n\trd.x = string(buf)\n\treturn len(buf), nil\n}\n\nfunc (rd *ISBN) Copy(dest dns.PrivateRdata) error {\n\tisbn, ok := dest.(*ISBN)\n\tif !ok {\n\t\treturn dns.ErrRdata\n\t}\n\tisbn.x = rd.x\n\treturn nil\n}\n\nvar testrecord = strings.Join([]string{\"example.org.\", \"3600\", \"IN\", \"ISBN\", \"12-3 456789-0-123\"}, \"\\t\")\n\nfunc TestPrivateText(t *testing.T) {\n\tdns.PrivateHandle(\"ISBN\", TypeISBN, NewISBN)\n\tdefer dns.PrivateHandleRemove(TypeISBN)\n\n\trr, err := dns.NewRR(testrecord)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif rr.String() != testrecord {\n\t\tt.Errorf(\"record string representation did not match original %#v != %#v\", rr.String(), testrecord)\n\t}\n}\n\nfunc TestPrivateByteSlice(t *testing.T) {\n\tdns.PrivateHandle(\"ISBN\", TypeISBN, NewISBN)\n\tdefer dns.PrivateHandleRemove(TypeISBN)\n\n\trr, err := dns.NewRR(testrecord)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbuf := make([]byte, 100)\n\toff, err := dns.PackRR(rr, buf, 0, nil, false)\n\tif err != nil {\n\t\tt.Errorf(\"got error packing ISBN: %v\", err)\n\t}\n\n\tcustrr := rr.(*dns.PrivateRR)\n\tif ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off {\n\t\tt.Errorf(\"offset is not matching to length of Private RR: %d!=%d\", off, ln)\n\t}\n\n\trr1, off1, err := dns.UnpackRR(buf[:off], 0)\n\tif err != nil {\n\t\tt.Errorf(\"got error unpacking ISBN: %v\", err)\n\t\treturn\n\t}\n\n\tif off1 != off {\n\t\tt.Errorf(\"offset after unpacking differs: %d != %d\", off1, off)\n\t}\n\n\tif rr1.String() != testrecord {\n\t\tt.Errorf(\"record string representation did not match original %#v != %#v\", rr1.String(), testrecord)\n\t}\n}\n\nconst TypeVERSION uint16 = 0xFF01\n\ntype VERSION struct {\n\tx string\n}\n\nfunc NewVersion() dns.PrivateRdata { return &VERSION{\"\"} }\n\nfunc (rd *VERSION) String() string { return rd.x }\nfunc (rd *VERSION) Parse(txt []string) error {\n\trd.x = strings.TrimSpace(strings.Join(txt, \" \"))\n\treturn nil\n}\n\nfunc (rd *VERSION) Pack(buf []byte) (int, error) {\n\tb := []byte(rd.x)\n\tn := copy(buf, b)\n\tif n != len(b) {\n\t\treturn n, dns.ErrBuf\n\t}\n\treturn n, nil\n}\n\nfunc (rd *VERSION) Unpack(buf []byte) (int, error) {\n\trd.x = string(buf)\n\treturn len(buf), nil\n}\n\nfunc (rd *VERSION) Copy(dest dns.PrivateRdata) error {\n\tisbn, ok := dest.(*VERSION)\n\tif !ok {\n\t\treturn dns.ErrRdata\n\t}\n\tisbn.x = rd.x\n\treturn nil\n}\n\nfunc (rd *VERSION) Len() int {\n\treturn len([]byte(rd.x))\n}\n\nvar smallzone = `$ORIGIN example.org.\n@ 3600 IN SOA\tsns.dns.icann.org. noc.dns.icann.org. (\n\t\t2014091518 7200 3600 1209600 3600\n)\n    A   1.2.3.4\nok ISBN 1231-92110-12\ngo VERSION (\n\t1.3.1 ; comment\n)\nwww ISBN 1231-92110-16\n*  CNAME @\n`\n\nfunc TestPrivateZoneParser(t *testing.T) {\n\tdns.PrivateHandle(\"ISBN\", TypeISBN, NewISBN)\n\tdns.PrivateHandle(\"VERSION\", TypeVERSION, NewVersion)\n\tdefer dns.PrivateHandleRemove(TypeISBN)\n\tdefer dns.PrivateHandleRemove(TypeVERSION)\n\n\tr := strings.NewReader(smallzone)\n\tz := dns.NewZoneParser(r, \".\", \"\")\n\n\tfor _, ok := z.Next(); ok; _, ok = z.Next() {\n\t}\n\tif err := z.Err(); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "reverse.go",
    "content": "package dns\n\n// StringToType is the reverse of TypeToString, needed for string parsing.\nvar StringToType = reverseInt16(TypeToString)\n\n// StringToClass is the reverse of ClassToString, needed for string parsing.\nvar StringToClass = reverseInt16(ClassToString)\n\n// StringToOpcode is a map of opcodes to strings.\nvar StringToOpcode = reverseInt(OpcodeToString)\n\n// StringToRcode is a map of rcodes to strings.\nvar StringToRcode = reverseInt(RcodeToString)\n\nfunc init() {\n\t// Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733.\n\tStringToRcode[\"NOTIMPL\"] = RcodeNotImplemented\n}\n\n// StringToAlgorithm is the reverse of AlgorithmToString.\nvar StringToAlgorithm = reverseInt8(AlgorithmToString)\n\n// StringToHash is a map of names to hash IDs.\nvar StringToHash = reverseInt8(HashToString)\n\n// StringToCertType is the reverse of CertTypeToString.\nvar StringToCertType = reverseInt16(CertTypeToString)\n\n// StringToStatefulType is the reverse of StatefulTypeToString.\nvar StringToStatefulType = reverseInt16(StatefulTypeToString)\n\n// Reverse a map\nfunc reverseInt8(m map[uint8]string) map[string]uint8 {\n\tn := make(map[string]uint8, len(m))\n\tfor u, s := range m {\n\t\tn[s] = u\n\t}\n\treturn n\n}\n\nfunc reverseInt16(m map[uint16]string) map[string]uint16 {\n\tn := make(map[string]uint16, len(m))\n\tfor u, s := range m {\n\t\tn[s] = u\n\t}\n\treturn n\n}\n\nfunc reverseInt(m map[int]string) map[string]int {\n\tn := make(map[string]int, len(m))\n\tfor u, s := range m {\n\t\tn[s] = u\n\t}\n\treturn n\n}\n"
  },
  {
    "path": "rr_test.go",
    "content": "package dns\n\n// testRR is a helper that wraps a call to NewRR and panics if the error is non-nil.\nfunc testRR(s string) RR {\n\tr, err := NewRR(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn r\n}\n"
  },
  {
    "path": "sanitize.go",
    "content": "package dns\n\n// Dedup removes identical RRs from rrs. It preserves the original ordering.\n// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies\n// rrs.\n// m is used to store the RRs temporary. If it is nil a new map will be allocated.\nfunc Dedup(rrs []RR, m map[string]RR) []RR {\n\n\tif m == nil {\n\t\tm = make(map[string]RR)\n\t}\n\t// Save the keys, so we don't have to call normalizedString twice.\n\tkeys := make([]*string, 0, len(rrs))\n\n\tfor _, r := range rrs {\n\t\tkey := normalizedString(r)\n\t\tkeys = append(keys, &key)\n\t\tif mr, ok := m[key]; ok {\n\t\t\t// Shortest TTL wins.\n\t\t\trh, mrh := r.Header(), mr.Header()\n\t\t\tif mrh.Ttl > rh.Ttl {\n\t\t\t\tmrh.Ttl = rh.Ttl\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tm[key] = r\n\t}\n\t// If the length of the result map equals the amount of RRs we got,\n\t// it means they were all different. We can then just return the original rrset.\n\tif len(m) == len(rrs) {\n\t\treturn rrs\n\t}\n\n\tj := 0\n\tfor i, r := range rrs {\n\t\t// If keys[i] lives in the map, we should copy and remove it.\n\t\tif _, ok := m[*keys[i]]; ok {\n\t\t\tdelete(m, *keys[i])\n\t\t\trrs[j] = r\n\t\t\tj++\n\t\t}\n\n\t\tif len(m) == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn rrs[:j]\n}\n\n// normalizedString returns a normalized string from r. The TTL\n// is removed and the domain name is lowercased. We go from this:\n// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:\n// lowercasename<TAB>CLASS<TAB>TYPE...\nfunc normalizedString(r RR) string {\n\t// A string Go DNS makes has: domainname<TAB>TTL<TAB>...\n\tb := []byte(r.String())\n\n\t// find the first non-escaped tab, then another, so we capture where the TTL lives.\n\tesc := false\n\tttlStart, ttlEnd := 0, 0\n\tfor i := 0; i < len(b) && ttlEnd == 0; i++ {\n\t\tswitch {\n\t\tcase b[i] == '\\\\':\n\t\t\tesc = !esc\n\t\tcase b[i] == '\\t' && !esc:\n\t\t\tif ttlStart == 0 {\n\t\t\t\tttlStart = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ttlEnd == 0 {\n\t\t\t\tttlEnd = i\n\t\t\t}\n\t\tcase b[i] >= 'A' && b[i] <= 'Z' && !esc:\n\t\t\tb[i] += 32\n\t\tdefault:\n\t\t\tesc = false\n\t\t}\n\t}\n\n\t// remove TTL.\n\tcopy(b[ttlStart:], b[ttlEnd:])\n\tcut := ttlEnd - ttlStart\n\treturn string(b[:len(b)-cut])\n}\n"
  },
  {
    "path": "sanitize_test.go",
    "content": "package dns\n\nimport \"testing\"\n\nfunc TestDedup(t *testing.T) {\n\ttestcases := map[[3]RR][]string{\n\t\t[...]RR{\n\t\t\ttestRR(\"mIek.nl. IN A 127.0.0.1\"),\n\t\t\ttestRR(\"mieK.nl. IN A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.Nl. IN A 127.0.0.1\"),\n\t\t}: {\"mIek.nl.\\t3600\\tIN\\tA\\t127.0.0.1\"},\n\t\t[...]RR{\n\t\t\ttestRR(\"miEk.nl. 2000 IN A 127.0.0.1\"),\n\t\t\ttestRR(\"mieK.Nl. 1000 IN A 127.0.0.1\"),\n\t\t\ttestRR(\"Miek.nL. 500 IN A 127.0.0.1\"),\n\t\t}: {\"miEk.nl.\\t500\\tIN\\tA\\t127.0.0.1\"},\n\t\t[...]RR{\n\t\t\ttestRR(\"miek.nl. IN A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.nl. CH A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.nl. IN A 127.0.0.1\"),\n\t\t}: {\"miek.nl.\\t3600\\tIN\\tA\\t127.0.0.1\",\n\t\t\t\"miek.nl.\\t3600\\tCH\\tA\\t127.0.0.1\",\n\t\t},\n\t\t[...]RR{\n\t\t\ttestRR(\"miek.nl. CH A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.nl. IN A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.de. IN A 127.0.0.1\"),\n\t\t}: {\"miek.nl.\\t3600\\tCH\\tA\\t127.0.0.1\",\n\t\t\t\"miek.nl.\\t3600\\tIN\\tA\\t127.0.0.1\",\n\t\t\t\"miek.de.\\t3600\\tIN\\tA\\t127.0.0.1\",\n\t\t},\n\t\t[...]RR{\n\t\t\ttestRR(\"miek.de. IN A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.nl. 200 IN A 127.0.0.1\"),\n\t\t\ttestRR(\"miek.nl. 300 IN A 127.0.0.1\"),\n\t\t}: {\"miek.de.\\t3600\\tIN\\tA\\t127.0.0.1\",\n\t\t\t\"miek.nl.\\t200\\tIN\\tA\\t127.0.0.1\",\n\t\t},\n\t}\n\n\tfor rr, expected := range testcases {\n\t\tout := Dedup([]RR{rr[0], rr[1], rr[2]}, nil)\n\t\tfor i, o := range out {\n\t\t\tif o.String() != expected[i] {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", expected[i], o.String())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkDedup(b *testing.B) {\n\trrs := []RR{\n\t\ttestRR(\"miEk.nl. 2000 IN A 127.0.0.1\"),\n\t\ttestRR(\"mieK.Nl. 1000 IN A 127.0.0.1\"),\n\t\ttestRR(\"Miek.nL. 500 IN A 127.0.0.1\"),\n\t}\n\tm := make(map[string]RR)\n\tfor i := 0; i < b.N; i++ {\n\t\tDedup(rrs, m)\n\t}\n}\n\nfunc TestNormalizedString(t *testing.T) {\n\ttests := map[RR]string{\n\t\ttestRR(\"mIEk.Nl. 3600 IN A 127.0.0.1\"):     \"miek.nl.\\tIN\\tA\\t127.0.0.1\",\n\t\ttestRR(\"m\\\\ iek.nL. 3600 IN A 127.0.0.1\"):  \"m\\\\ iek.nl.\\tIN\\tA\\t127.0.0.1\",\n\t\ttestRR(\"m\\\\\\tIeK.nl. 3600 in A 127.0.0.1\"): \"m\\\\009iek.nl.\\tIN\\tA\\t127.0.0.1\",\n\t}\n\tfor tc, expected := range tests {\n\t\tn := normalizedString(tc)\n\t\tif n != expected {\n\t\t\tt.Errorf(\"expected %s, got %s\", expected, n)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "scan.go",
    "content": "package dns\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"math\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nconst maxTok = 512 // Token buffer start size, and growth size amount.\n\n// The maximum depth of $INCLUDE directives supported by the\n// ZoneParser API.\nconst maxIncludeDepth = 7\n\n// Tokenize a RFC 1035 zone file. The tokenizer will normalize it:\n// * Add ownernames if they are left blank;\n// * Suppress sequences of spaces;\n// * Make each RR fit on one line (_NEWLINE is send as last)\n// * Handle comments: ;\n// * Handle braces - anywhere.\nconst (\n\t// Zonefile\n\tzEOF = iota\n\tzString\n\tzBlank\n\tzQuote\n\tzNewline\n\tzRrtpe\n\tzOwner\n\tzClass\n\tzDirOrigin   // $ORIGIN\n\tzDirTTL      // $TTL\n\tzDirInclude  // $INCLUDE\n\tzDirGenerate // $GENERATE\n\n\t// Privatekey file\n\tzValue\n\tzKey\n\n\tzExpectOwnerDir      // Ownername\n\tzExpectOwnerBl       // Whitespace after the ownername\n\tzExpectAny           // Expect rrtype, ttl or class\n\tzExpectAnyNoClass    // Expect rrtype or ttl\n\tzExpectAnyNoClassBl  // The whitespace after _EXPECT_ANY_NOCLASS\n\tzExpectAnyNoTTL      // Expect rrtype or class\n\tzExpectAnyNoTTLBl    // Whitespace after _EXPECT_ANY_NOTTL\n\tzExpectRrtype        // Expect rrtype\n\tzExpectRrtypeBl      // Whitespace BEFORE rrtype\n\tzExpectRdata         // The first element of the rdata\n\tzExpectDirTTLBl      // Space after directive $TTL\n\tzExpectDirTTL        // Directive $TTL\n\tzExpectDirOriginBl   // Space after directive $ORIGIN\n\tzExpectDirOrigin     // Directive $ORIGIN\n\tzExpectDirIncludeBl  // Space after directive $INCLUDE\n\tzExpectDirInclude    // Directive $INCLUDE\n\tzExpectDirGenerate   // Directive $GENERATE\n\tzExpectDirGenerateBl // Space after directive $GENERATE\n)\n\n// ParseError is a parsing error. It contains the parse error and the location in the io.Reader\n// where the error occurred.\ntype ParseError struct {\n\tfile       string\n\terr        string\n\twrappedErr error\n\tlex        lex\n}\n\nfunc (e *ParseError) Error() (s string) {\n\tif e.file != \"\" {\n\t\ts = e.file + \": \"\n\t}\n\tif e.err == \"\" && e.wrappedErr != nil {\n\t\te.err = e.wrappedErr.Error()\n\t}\n\ts += \"dns: \" + e.err + \": \" + strconv.QuoteToASCII(e.lex.token) + \" at line: \" +\n\t\tstrconv.Itoa(e.lex.line) + \":\" + strconv.Itoa(e.lex.column)\n\treturn\n}\n\nfunc (e *ParseError) Unwrap() error { return e.wrappedErr }\n\ntype lex struct {\n\ttoken  string // text of the token\n\terr    bool   // when true, token text has lexer error\n\tvalue  uint8  // value: zString, _BLANK, etc.\n\ttorc   uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar\n\tline   int    // line in the file\n\tcolumn int    // column in the file\n}\n\n// ttlState describes the state necessary to fill in an omitted RR TTL\ntype ttlState struct {\n\tttl           uint32 // ttl is the current default TTL\n\tisByDirective bool   // isByDirective indicates whether ttl was set by a $TTL directive\n}\n\n// NewRR reads a string s and returns the first RR.\n// If s contains no records, NewRR will return nil with no error.\n//\n// The class defaults to IN, TTL defaults to 3600, and\n// origin for resolving relative domain names defaults to the DNS root (.).\n// Full zone file syntax is supported, including directives like $TTL and $ORIGIN.\n// All fields of the returned RR are set from the read data, except RR.Header().Rdlength which is set to 0.\n// Is you need a partial resource record with no rdata - for instance - for dynamic updates, see the [ANY]\n// documentation.\nfunc NewRR(s string) (RR, error) {\n\tif len(s) > 0 && s[len(s)-1] != '\\n' { // We need a closing newline\n\t\treturn ReadRR(strings.NewReader(s+\"\\n\"), \"\")\n\t}\n\treturn ReadRR(strings.NewReader(s), \"\")\n}\n\n// ReadRR reads the RR contained in r.\n//\n// The string file is used in error reporting and to resolve relative\n// $INCLUDE directives.\n//\n// See NewRR for more documentation.\nfunc ReadRR(r io.Reader, file string) (RR, error) {\n\tzp := NewZoneParser(r, \".\", file)\n\tzp.SetDefaultTTL(defaultTtl)\n\tzp.SetIncludeAllowed(true)\n\trr, _ := zp.Next()\n\treturn rr, zp.Err()\n}\n\n// ZoneParser is a parser for an RFC 1035 style zonefile.\n//\n// Each parsed RR in the zone is returned sequentially from Next. An\n// optional comment can be retrieved with Comment.\n//\n// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all\n// supported. Although $INCLUDE is disabled by default.\n// Note that $GENERATE's range support up to a maximum of 65535 steps.\n//\n// Basic usage pattern when reading from a string (z) containing the\n// zone data:\n//\n//\tzp := NewZoneParser(strings.NewReader(z), \"\", \"\")\n//\n//\tfor rr, ok := zp.Next(); ok; rr, ok = zp.Next() {\n//\t\t// Do something with rr\n//\t}\n//\n//\tif err := zp.Err(); err != nil {\n//\t\t// log.Println(err)\n//\t}\n//\n// Comments specified after an RR (and on the same line!) are\n// returned too:\n//\n//\tfoo. IN A 10.0.0.1 ; this is a comment\n//\n// The text \"; this is comment\" is returned from Comment. Comments inside\n// the RR are returned concatenated along with the RR. Comments on a line\n// by themselves are discarded.\n//\n// Callers should not assume all returned data in an Resource Record is\n// syntactically correct, e.g. illegal base64 in RRSIGs will be returned as-is.\ntype ZoneParser struct {\n\tc *zlexer\n\n\tparseErr *ParseError\n\n\torigin string\n\tfile   string\n\n\tdefttl *ttlState\n\n\th RR_Header\n\n\t// sub is used to parse $INCLUDE files and $GENERATE directives.\n\t// Next, by calling subNext, forwards the resulting RRs from this\n\t// sub parser to the calling code.\n\tsub  *ZoneParser\n\tr    io.Reader\n\tfsys fs.FS\n\n\tincludeDepth uint8\n\n\tincludeAllowed     bool\n\tgenerateDisallowed bool\n}\n\n// NewZoneParser returns an RFC 1035 style zonefile parser that reads\n// from r.\n//\n// The string file is used in error reporting and to resolve relative\n// $INCLUDE directives. The string origin is used as the initial\n// origin, as if the file would start with an $ORIGIN directive.\nfunc NewZoneParser(r io.Reader, origin, file string) *ZoneParser {\n\tvar pe *ParseError\n\tif origin != \"\" {\n\t\torigin = Fqdn(origin)\n\t\tif _, ok := IsDomainName(origin); !ok {\n\t\t\tpe = &ParseError{file: file, err: \"bad initial origin name\"}\n\t\t}\n\t}\n\n\treturn &ZoneParser{\n\t\tc: newZLexer(r),\n\n\t\tparseErr: pe,\n\n\t\torigin: origin,\n\t\tfile:   file,\n\t}\n}\n\n// SetDefaultTTL sets the parsers default TTL to ttl.\nfunc (zp *ZoneParser) SetDefaultTTL(ttl uint32) {\n\tzp.defttl = &ttlState{ttl, false}\n}\n\n// SetIncludeAllowed controls whether $INCLUDE directives are\n// allowed. $INCLUDE directives are not supported by default.\n//\n// The $INCLUDE directive will open and read from a user controlled\n// file on the system. Even if the file is not a valid zonefile, the\n// contents of the file may be revealed in error messages, such as:\n//\n//\t/etc/passwd: dns: not a TTL: \"root:x:0:0:root:/root:/bin/bash\" at line: 1:31\n//\t/etc/shadow: dns: not a TTL: \"root:$6$<redacted>::0:99999:7:::\" at line: 1:125\nfunc (zp *ZoneParser) SetIncludeAllowed(v bool) {\n\tzp.includeAllowed = v\n}\n\n// SetIncludeFS provides an [fs.FS] to use when looking for the target of\n// $INCLUDE directives.  ($INCLUDE must still be enabled separately by calling\n// [ZoneParser.SetIncludeAllowed].)  If fsys is nil, [os.Open] will be used.\n//\n// When fsys is an on-disk FS, the ability of $INCLUDE to reach files from\n// outside its root directory depends upon the FS implementation.  For\n// instance, [os.DirFS] will refuse to open paths like \"../../etc/passwd\",\n// however it will still follow links which may point anywhere on the system.\n//\n// FS paths are slash-separated on all systems, even Windows.  $INCLUDE paths\n// containing other characters such as backslash and colon may be accepted as\n// valid, but those characters will never be interpreted by an FS\n// implementation as path element separators.  See [fs.ValidPath] for more\n// details.\nfunc (zp *ZoneParser) SetIncludeFS(fsys fs.FS) {\n\tzp.fsys = fsys\n}\n\n// Err returns the first non-EOF error that was encountered by the\n// ZoneParser.\nfunc (zp *ZoneParser) Err() error {\n\tif zp.parseErr != nil {\n\t\treturn zp.parseErr\n\t}\n\n\tif zp.sub != nil {\n\t\tif err := zp.sub.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn zp.c.Err()\n}\n\nfunc (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) {\n\tzp.parseErr = &ParseError{file: zp.file, err: err, lex: l}\n\treturn nil, false\n}\n\n// Comment returns an optional text comment that occurred alongside\n// the RR.\nfunc (zp *ZoneParser) Comment() string {\n\tif zp.parseErr != nil {\n\t\treturn \"\"\n\t}\n\n\tif zp.sub != nil {\n\t\treturn zp.sub.Comment()\n\t}\n\n\treturn zp.c.Comment()\n}\n\nfunc (zp *ZoneParser) subNext() (RR, bool) {\n\tif rr, ok := zp.sub.Next(); ok {\n\t\treturn rr, true\n\t}\n\n\tif zp.sub.r != nil {\n\t\tif c, ok := zp.sub.r.(io.Closer); ok {\n\t\t\tc.Close()\n\t\t}\n\t\tzp.sub.r = nil\n\t}\n\n\tif zp.sub.Err() != nil {\n\t\t// We have errors to surface.\n\t\treturn nil, false\n\t}\n\n\tzp.sub = nil\n\treturn zp.Next()\n}\n\n// Next advances the parser to the next RR in the zonefile and\n// returns the (RR, true). It will return (nil, false) when the\n// parsing stops, either by reaching the end of the input or an\n// error. After Next returns (nil, false), the Err method will return\n// any error that occurred during parsing.\nfunc (zp *ZoneParser) Next() (RR, bool) {\n\tif zp.parseErr != nil {\n\t\treturn nil, false\n\t}\n\tif zp.sub != nil {\n\t\treturn zp.subNext()\n\t}\n\n\t// 6 possible beginnings of a line (_ is a space):\n\t//\n\t//   0. zRRTYPE                              -> all omitted until the rrtype\n\t//   1. zOwner _ zRrtype                     -> class/ttl omitted\n\t//   2. zOwner _ zString _ zRrtype           -> class omitted\n\t//   3. zOwner _ zString _ zClass  _ zRrtype -> ttl/class\n\t//   4. zOwner _ zClass  _ zRrtype           -> ttl omitted\n\t//   5. zOwner _ zClass  _ zString _ zRrtype -> class/ttl (reversed)\n\t//\n\t// After detecting these, we know the zRrtype so we can jump to functions\n\t// handling the rdata for each of these types.\n\n\tst := zExpectOwnerDir // initial state\n\th := &zp.h\n\n\tfor l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {\n\t\t// zlexer spotted an error already\n\t\tif l.err {\n\t\t\treturn zp.setParseError(l.token, l)\n\t\t}\n\n\t\tswitch st {\n\t\tcase zExpectOwnerDir:\n\t\t\t// We can also expect a directive, like $TTL or $ORIGIN\n\t\t\tif zp.defttl != nil {\n\t\t\t\th.Ttl = zp.defttl.ttl\n\t\t\t}\n\n\t\t\th.Class = ClassINET\n\n\t\t\tswitch l.value {\n\t\t\tcase zNewline:\n\t\t\t\tst = zExpectOwnerDir\n\t\t\tcase zOwner:\n\t\t\t\tname, ok := toAbsoluteName(l.token, zp.origin)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn zp.setParseError(\"bad owner name\", l)\n\t\t\t\t}\n\n\t\t\t\th.Name = name\n\n\t\t\t\tst = zExpectOwnerBl\n\t\t\tcase zDirTTL:\n\t\t\t\tst = zExpectDirTTLBl\n\t\t\tcase zDirOrigin:\n\t\t\t\tst = zExpectDirOriginBl\n\t\t\tcase zDirInclude:\n\t\t\t\tst = zExpectDirIncludeBl\n\t\t\tcase zDirGenerate:\n\t\t\t\tst = zExpectDirGenerateBl\n\t\t\tcase zRrtpe:\n\t\t\t\th.Rrtype = l.torc\n\n\t\t\t\tst = zExpectRdata\n\t\t\tcase zClass:\n\t\t\t\th.Class = l.torc\n\n\t\t\t\tst = zExpectAnyNoClassBl\n\t\t\tcase zBlank:\n\t\t\t\t// Discard, can happen when there is nothing on the\n\t\t\t\t// line except the RR type\n\t\t\tcase zString:\n\t\t\t\tttl, ok := stringToTTL(l.token)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn zp.setParseError(\"not a TTL\", l)\n\t\t\t\t}\n\n\t\t\t\th.Ttl = ttl\n\n\t\t\t\tif zp.defttl == nil || !zp.defttl.isByDirective {\n\t\t\t\t\tzp.defttl = &ttlState{ttl, false}\n\t\t\t\t}\n\n\t\t\t\tst = zExpectAnyNoTTLBl\n\t\t\tdefault:\n\t\t\t\treturn zp.setParseError(\"syntax error at beginning\", l)\n\t\t\t}\n\t\tcase zExpectDirIncludeBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank after $INCLUDE-directive\", l)\n\t\t\t}\n\n\t\t\tst = zExpectDirInclude\n\t\tcase zExpectDirInclude:\n\t\t\tif l.value != zString {\n\t\t\t\treturn zp.setParseError(\"expecting $INCLUDE value, not this...\", l)\n\t\t\t}\n\n\t\t\tneworigin := zp.origin // There may be optionally a new origin set after the filename, if not use current one\n\t\t\tswitch l, _ := zp.c.Next(); l.value {\n\t\t\tcase zBlank:\n\t\t\t\tl, _ := zp.c.Next()\n\t\t\t\tif l.value == zString {\n\t\t\t\t\tname, ok := toAbsoluteName(l.token, zp.origin)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn zp.setParseError(\"bad origin name\", l)\n\t\t\t\t\t}\n\n\t\t\t\t\tneworigin = name\n\t\t\t\t}\n\t\t\tcase zNewline, zEOF:\n\t\t\t\t// Ok\n\t\t\tdefault:\n\t\t\t\treturn zp.setParseError(\"garbage after $INCLUDE\", l)\n\t\t\t}\n\n\t\t\tif !zp.includeAllowed {\n\t\t\t\treturn zp.setParseError(\"$INCLUDE directive not allowed\", l)\n\t\t\t}\n\t\t\tif zp.includeDepth >= maxIncludeDepth {\n\t\t\t\treturn zp.setParseError(\"too deeply nested $INCLUDE\", l)\n\t\t\t}\n\n\t\t\t// Start with the new file\n\t\t\tincludePath := l.token\n\t\t\tvar r1 io.Reader\n\t\t\tvar e1 error\n\t\t\tif zp.fsys != nil {\n\t\t\t\t// fs.FS always uses / as separator, even on Windows, so use\n\t\t\t\t// path instead of filepath here:\n\t\t\t\tif !path.IsAbs(includePath) {\n\t\t\t\t\tincludePath = path.Join(path.Dir(zp.file), includePath)\n\t\t\t\t}\n\n\t\t\t\t// os.DirFS, and probably others, expect all paths to be\n\t\t\t\t// relative, so clean the path and remove leading / if\n\t\t\t\t// present:\n\t\t\t\tincludePath = strings.TrimLeft(path.Clean(includePath), \"/\")\n\n\t\t\t\tr1, e1 = zp.fsys.Open(includePath)\n\t\t\t} else {\n\t\t\t\tif !filepath.IsAbs(includePath) {\n\t\t\t\t\tincludePath = filepath.Join(filepath.Dir(zp.file), includePath)\n\t\t\t\t}\n\t\t\t\tr1, e1 = os.Open(includePath)\n\t\t\t}\n\t\t\tif e1 != nil {\n\t\t\t\tvar as string\n\t\t\t\tif includePath != l.token {\n\t\t\t\t\tas = fmt.Sprintf(\" as `%s'\", includePath)\n\t\t\t\t}\n\t\t\t\tzp.parseErr = &ParseError{\n\t\t\t\t\tfile:       zp.file,\n\t\t\t\t\twrappedErr: fmt.Errorf(\"failed to open `%s'%s: %w\", l.token, as, e1),\n\t\t\t\t\tlex:        l,\n\t\t\t\t}\n\t\t\t\treturn nil, false\n\t\t\t}\n\n\t\t\tzp.sub = NewZoneParser(r1, neworigin, includePath)\n\t\t\tzp.sub.defttl, zp.sub.includeDepth, zp.sub.r = zp.defttl, zp.includeDepth+1, r1\n\t\t\tzp.sub.SetIncludeAllowed(true)\n\t\t\tzp.sub.SetIncludeFS(zp.fsys)\n\t\t\treturn zp.subNext()\n\t\tcase zExpectDirTTLBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank after $TTL-directive\", l)\n\t\t\t}\n\n\t\t\tst = zExpectDirTTL\n\t\tcase zExpectDirTTL:\n\t\t\tif l.value != zString {\n\t\t\t\treturn zp.setParseError(\"expecting $TTL value, not this...\", l)\n\t\t\t}\n\n\t\t\tif err := slurpRemainder(zp.c); err != nil {\n\t\t\t\treturn zp.setParseError(err.err, err.lex)\n\t\t\t}\n\n\t\t\tttl, ok := stringToTTL(l.token)\n\t\t\tif !ok {\n\t\t\t\treturn zp.setParseError(\"expecting $TTL value, not this...\", l)\n\t\t\t}\n\n\t\t\tzp.defttl = &ttlState{ttl, true}\n\n\t\t\tst = zExpectOwnerDir\n\t\tcase zExpectDirOriginBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank after $ORIGIN-directive\", l)\n\t\t\t}\n\n\t\t\tst = zExpectDirOrigin\n\t\tcase zExpectDirOrigin:\n\t\t\tif l.value != zString {\n\t\t\t\treturn zp.setParseError(\"expecting $ORIGIN value, not this...\", l)\n\t\t\t}\n\n\t\t\tif err := slurpRemainder(zp.c); err != nil {\n\t\t\t\treturn zp.setParseError(err.err, err.lex)\n\t\t\t}\n\n\t\t\tname, ok := toAbsoluteName(l.token, zp.origin)\n\t\t\tif !ok {\n\t\t\t\treturn zp.setParseError(\"bad origin name\", l)\n\t\t\t}\n\n\t\t\tzp.origin = name\n\n\t\t\tst = zExpectOwnerDir\n\t\tcase zExpectDirGenerateBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank after $GENERATE-directive\", l)\n\t\t\t}\n\n\t\t\tst = zExpectDirGenerate\n\t\tcase zExpectDirGenerate:\n\t\t\tif zp.generateDisallowed {\n\t\t\t\treturn zp.setParseError(\"nested $GENERATE directive not allowed\", l)\n\t\t\t}\n\t\t\tif l.value != zString {\n\t\t\t\treturn zp.setParseError(\"expecting $GENERATE value, not this...\", l)\n\t\t\t}\n\n\t\t\treturn zp.generate(l)\n\t\tcase zExpectOwnerBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank after owner\", l)\n\t\t\t}\n\n\t\t\tst = zExpectAny\n\t\tcase zExpectAny:\n\t\t\tswitch l.value {\n\t\t\tcase zRrtpe:\n\t\t\t\tif zp.defttl == nil {\n\t\t\t\t\treturn zp.setParseError(\"missing TTL with no previous value\", l)\n\t\t\t\t}\n\n\t\t\t\th.Rrtype = l.torc\n\n\t\t\t\tst = zExpectRdata\n\t\t\tcase zClass:\n\t\t\t\th.Class = l.torc\n\n\t\t\t\tst = zExpectAnyNoClassBl\n\t\t\tcase zString:\n\t\t\t\tttl, ok := stringToTTL(l.token)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn zp.setParseError(\"not a TTL\", l)\n\t\t\t\t}\n\n\t\t\t\th.Ttl = ttl\n\n\t\t\t\tif zp.defttl == nil || !zp.defttl.isByDirective {\n\t\t\t\t\tzp.defttl = &ttlState{ttl, false}\n\t\t\t\t}\n\n\t\t\t\tst = zExpectAnyNoTTLBl\n\t\t\tdefault:\n\t\t\t\treturn zp.setParseError(\"expecting RR type, TTL or class, not this...\", l)\n\t\t\t}\n\t\tcase zExpectAnyNoClassBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank before class\", l)\n\t\t\t}\n\n\t\t\tst = zExpectAnyNoClass\n\t\tcase zExpectAnyNoTTLBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank before TTL\", l)\n\t\t\t}\n\n\t\t\tst = zExpectAnyNoTTL\n\t\tcase zExpectAnyNoTTL:\n\t\t\tswitch l.value {\n\t\t\tcase zClass:\n\t\t\t\th.Class = l.torc\n\n\t\t\t\tst = zExpectRrtypeBl\n\t\t\tcase zRrtpe:\n\t\t\t\th.Rrtype = l.torc\n\n\t\t\t\tst = zExpectRdata\n\t\t\tdefault:\n\t\t\t\treturn zp.setParseError(\"expecting RR type or class, not this...\", l)\n\t\t\t}\n\t\tcase zExpectAnyNoClass:\n\t\t\tswitch l.value {\n\t\t\tcase zString:\n\t\t\t\tttl, ok := stringToTTL(l.token)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn zp.setParseError(\"not a TTL\", l)\n\t\t\t\t}\n\n\t\t\t\th.Ttl = ttl\n\n\t\t\t\tif zp.defttl == nil || !zp.defttl.isByDirective {\n\t\t\t\t\tzp.defttl = &ttlState{ttl, false}\n\t\t\t\t}\n\n\t\t\t\tst = zExpectRrtypeBl\n\t\t\tcase zRrtpe:\n\t\t\t\th.Rrtype = l.torc\n\n\t\t\t\tst = zExpectRdata\n\t\t\tdefault:\n\t\t\t\treturn zp.setParseError(\"expecting RR type or TTL, not this...\", l)\n\t\t\t}\n\t\tcase zExpectRrtypeBl:\n\t\t\tif l.value != zBlank {\n\t\t\t\treturn zp.setParseError(\"no blank before RR type\", l)\n\t\t\t}\n\n\t\t\tst = zExpectRrtype\n\t\tcase zExpectRrtype:\n\t\t\tif l.value != zRrtpe {\n\t\t\t\treturn zp.setParseError(\"unknown RR type\", l)\n\t\t\t}\n\n\t\t\th.Rrtype = l.torc\n\n\t\t\tst = zExpectRdata\n\t\tcase zExpectRdata:\n\t\t\tvar (\n\t\t\t\trr             RR\n\t\t\t\tparseAsRFC3597 bool\n\t\t\t)\n\t\t\tif newFn, ok := TypeToRR[h.Rrtype]; ok {\n\t\t\t\trr = newFn()\n\t\t\t\t*rr.Header() = *h\n\n\t\t\t\t// We may be parsing a known RR type using the RFC3597 format.\n\t\t\t\t// If so, we handle that here in a generic way.\n\t\t\t\t//\n\t\t\t\t// This is also true for PrivateRR types which will have the\n\t\t\t\t// RFC3597 parsing done for them and the Unpack method called\n\t\t\t\t// to populate the RR instead of simply deferring to Parse.\n\t\t\t\tif zp.c.Peek().token == \"\\\\#\" {\n\t\t\t\t\tparseAsRFC3597 = true\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trr = &RFC3597{Hdr: *h}\n\t\t\t}\n\n\t\t\t_, isPrivate := rr.(*PrivateRR)\n\t\t\tif !isPrivate && zp.c.Peek().token == \"\" {\n\t\t\t\t// This is a dynamic update rr.\n\n\t\t\t\tif err := slurpRemainder(zp.c); err != nil {\n\t\t\t\t\treturn zp.setParseError(err.err, err.lex)\n\t\t\t\t}\n\n\t\t\t\treturn rr, true\n\t\t\t} else if l.value == zNewline {\n\t\t\t\treturn zp.setParseError(\"unexpected newline\", l)\n\t\t\t}\n\n\t\t\tparseAsRR := rr\n\t\t\tif parseAsRFC3597 {\n\t\t\t\tparseAsRR = &RFC3597{Hdr: *h}\n\t\t\t}\n\n\t\t\tif err := parseAsRR.parse(zp.c, zp.origin); err != nil {\n\t\t\t\t// err is a concrete *ParseError without the file field set.\n\t\t\t\t// The setParseError call below will construct a new\n\t\t\t\t// *ParseError with file set to zp.file.\n\n\t\t\t\t// err.lex may be nil in which case we substitute our current\n\t\t\t\t// lex token.\n\t\t\t\tif err.lex == (lex{}) {\n\t\t\t\t\treturn zp.setParseError(err.err, l)\n\t\t\t\t}\n\n\t\t\t\treturn zp.setParseError(err.err, err.lex)\n\t\t\t}\n\n\t\t\tif parseAsRFC3597 {\n\t\t\t\terr := parseAsRR.(*RFC3597).fromRFC3597(rr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn zp.setParseError(err.Error(), l)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn rr, true\n\t\t}\n\t}\n\n\t// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this\n\t// is not an error, because an empty zone file is still a zone file.\n\treturn nil, false\n}\n\ntype zlexer struct {\n\tbr io.ByteReader\n\n\treadErr error\n\n\tline   int\n\tcolumn int\n\n\tcomBuf  string\n\tcomment string\n\n\tl       lex\n\tcachedL *lex\n\n\tbrace  int\n\tquote  bool\n\tspace  bool\n\tcommt  bool\n\trrtype bool\n\towner  bool\n\n\tnextL bool\n\n\teol bool // end-of-line\n}\n\nfunc newZLexer(r io.Reader) *zlexer {\n\tbr, ok := r.(io.ByteReader)\n\tif !ok {\n\t\tbr = bufio.NewReaderSize(r, 1024)\n\t}\n\n\treturn &zlexer{\n\t\tbr: br,\n\n\t\tline: 1,\n\n\t\towner: true,\n\t}\n}\n\nfunc (zl *zlexer) Err() error {\n\tif zl.readErr == io.EOF {\n\t\treturn nil\n\t}\n\n\treturn zl.readErr\n}\n\n// readByte returns the next byte from the input\nfunc (zl *zlexer) readByte() (byte, bool) {\n\tif zl.readErr != nil {\n\t\treturn 0, false\n\t}\n\n\tc, err := zl.br.ReadByte()\n\tif err != nil {\n\t\tzl.readErr = err\n\t\treturn 0, false\n\t}\n\n\t// delay the newline handling until the next token is delivered,\n\t// fixes off-by-one errors when reporting a parse error.\n\tif zl.eol {\n\t\tzl.line++\n\t\tzl.column = 0\n\t\tzl.eol = false\n\t}\n\n\tif c == '\\n' {\n\t\tzl.eol = true\n\t} else {\n\t\tzl.column++\n\t}\n\n\treturn c, true\n}\n\nfunc (zl *zlexer) Peek() lex {\n\tif zl.nextL {\n\t\treturn zl.l\n\t}\n\n\tl, ok := zl.Next()\n\tif !ok {\n\t\treturn l\n\t}\n\n\tif zl.nextL {\n\t\t// Cache l. Next returns zl.cachedL then zl.l.\n\t\tzl.cachedL = &l\n\t} else {\n\t\t// In this case l == zl.l, so we just tell Next to return zl.l.\n\t\tzl.nextL = true\n\t}\n\n\treturn l\n}\n\nfunc (zl *zlexer) Next() (lex, bool) {\n\tl := &zl.l\n\tswitch {\n\tcase zl.cachedL != nil:\n\t\tl, zl.cachedL = zl.cachedL, nil\n\t\treturn *l, true\n\tcase zl.nextL:\n\t\tzl.nextL = false\n\t\treturn *l, true\n\tcase l.err:\n\t\t// Parsing errors should be sticky.\n\t\treturn lex{value: zEOF}, false\n\t}\n\n\tvar (\n\t\tstr = make([]byte, maxTok) // Hold string text\n\t\tcom = make([]byte, maxTok) // Hold comment text\n\n\t\tstri int // Offset in str (0 means empty)\n\t\tcomi int // Offset in com (0 means empty)\n\n\t\tescape bool\n\t)\n\n\tif zl.comBuf != \"\" {\n\t\tcomi = copy(com[:], zl.comBuf)\n\t\tzl.comBuf = \"\"\n\t}\n\n\tzl.comment = \"\"\n\n\tfor x, ok := zl.readByte(); ok; x, ok = zl.readByte() {\n\t\tl.line, l.column = zl.line, zl.column\n\n\t\tif stri >= len(str) {\n\t\t\t// if buffer length is insufficient, increase it.\n\t\t\tstr = append(str[:], make([]byte, maxTok)...)\n\t\t}\n\t\tif comi >= len(com) {\n\t\t\t// if buffer length is insufficient, increase it.\n\t\t\tcom = append(com[:], make([]byte, maxTok)...)\n\t\t}\n\n\t\tswitch x {\n\t\tcase ' ', '\\t':\n\t\t\tif escape || zl.quote {\n\t\t\t\t// Inside quotes or escaped this is legal.\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\n\t\t\t\tescape = false\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif zl.commt {\n\t\t\t\tcom[comi] = x\n\t\t\t\tcomi++\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tvar retL lex\n\t\t\tif stri == 0 {\n\t\t\t\t// Space directly in the beginning, handled in the grammar\n\t\t\t} else if zl.owner {\n\t\t\t\t// If we have a string and it's the first, make it an owner\n\t\t\t\tl.value = zOwner\n\t\t\t\tl.token = string(str[:stri])\n\n\t\t\t\t// escape $... start with a \\ not a $, so this will work\n\t\t\t\tswitch strings.ToUpper(l.token) {\n\t\t\t\tcase \"$TTL\":\n\t\t\t\t\tl.value = zDirTTL\n\t\t\t\tcase \"$ORIGIN\":\n\t\t\t\t\tl.value = zDirOrigin\n\t\t\t\tcase \"$INCLUDE\":\n\t\t\t\t\tl.value = zDirInclude\n\t\t\t\tcase \"$GENERATE\":\n\t\t\t\t\tl.value = zDirGenerate\n\t\t\t\t}\n\n\t\t\t\tretL = *l\n\t\t\t} else {\n\t\t\t\tl.value = zString\n\t\t\t\tl.token = string(str[:stri])\n\n\t\t\t\tif !zl.rrtype {\n\t\t\t\t\ttokenUpper := strings.ToUpper(l.token)\n\t\t\t\t\tif t, ok := StringToType[tokenUpper]; ok {\n\t\t\t\t\t\tl.value = zRrtpe\n\t\t\t\t\t\tl.torc = t\n\n\t\t\t\t\t\tzl.rrtype = true\n\t\t\t\t\t} else if strings.HasPrefix(tokenUpper, \"TYPE\") {\n\t\t\t\t\t\tt, ok := typeToInt(l.token)\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\tl.token = \"unknown RR type\"\n\t\t\t\t\t\t\tl.err = true\n\t\t\t\t\t\t\treturn *l, true\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tl.value = zRrtpe\n\t\t\t\t\t\tl.torc = t\n\n\t\t\t\t\t\tzl.rrtype = true\n\t\t\t\t\t}\n\n\t\t\t\t\tif t, ok := StringToClass[tokenUpper]; ok {\n\t\t\t\t\t\tl.value = zClass\n\t\t\t\t\t\tl.torc = t\n\t\t\t\t\t} else if strings.HasPrefix(tokenUpper, \"CLASS\") {\n\t\t\t\t\t\tt, ok := classToInt(l.token)\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\tl.token = \"unknown class\"\n\t\t\t\t\t\t\tl.err = true\n\t\t\t\t\t\t\treturn *l, true\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tl.value = zClass\n\t\t\t\t\t\tl.torc = t\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tretL = *l\n\t\t\t}\n\n\t\t\tzl.owner = false\n\n\t\t\tif !zl.space {\n\t\t\t\tzl.space = true\n\n\t\t\t\tl.value = zBlank\n\t\t\t\tl.token = \" \"\n\n\t\t\t\tif retL == (lex{}) {\n\t\t\t\t\treturn *l, true\n\t\t\t\t}\n\n\t\t\t\tzl.nextL = true\n\t\t\t}\n\n\t\t\tif retL != (lex{}) {\n\t\t\t\treturn retL, true\n\t\t\t}\n\t\tcase ';':\n\t\t\tif escape || zl.quote {\n\t\t\t\t// Inside quotes or escaped this is legal.\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\n\t\t\t\tescape = false\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tzl.commt = true\n\t\t\tzl.comBuf = \"\"\n\n\t\t\tif comi > 1 {\n\t\t\t\t// A newline was previously seen inside a comment that\n\t\t\t\t// was inside braces and we delayed adding it until now.\n\t\t\t\tcom[comi] = ' ' // convert newline to space\n\t\t\t\tcomi++\n\t\t\t\tif comi >= len(com) {\n\t\t\t\t\tl.token = \"comment length insufficient for parsing\"\n\t\t\t\t\tl.err = true\n\t\t\t\t\treturn *l, true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcom[comi] = ';'\n\t\t\tcomi++\n\n\t\t\tif stri > 0 {\n\t\t\t\tzl.comBuf = string(com[:comi])\n\n\t\t\t\tl.value = zString\n\t\t\t\tl.token = string(str[:stri])\n\t\t\t\treturn *l, true\n\t\t\t}\n\t\tcase '\\r':\n\t\t\tescape = false\n\n\t\t\tif zl.quote {\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\t\t\t}\n\n\t\t\t// discard if outside of quotes\n\t\tcase '\\n':\n\t\t\tescape = false\n\n\t\t\t// Escaped newline\n\t\t\tif zl.quote {\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif zl.commt {\n\t\t\t\t// Reset a comment\n\t\t\t\tzl.commt = false\n\t\t\t\tzl.rrtype = false\n\n\t\t\t\t// If not in a brace this ends the comment AND the RR\n\t\t\t\tif zl.brace == 0 {\n\t\t\t\t\tzl.owner = true\n\n\t\t\t\t\tl.value = zNewline\n\t\t\t\t\tl.token = \"\\n\"\n\t\t\t\t\tzl.comment = string(com[:comi])\n\t\t\t\t\treturn *l, true\n\t\t\t\t}\n\n\t\t\t\tzl.comBuf = string(com[:comi])\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif zl.brace == 0 {\n\t\t\t\t// If there is previous text, we should output it here\n\t\t\t\tvar retL lex\n\t\t\t\tif stri != 0 {\n\t\t\t\t\tl.value = zString\n\t\t\t\t\tl.token = string(str[:stri])\n\n\t\t\t\t\tif !zl.rrtype {\n\t\t\t\t\t\ttokenUpper := strings.ToUpper(l.token)\n\t\t\t\t\t\tif t, ok := StringToType[tokenUpper]; ok {\n\t\t\t\t\t\t\tzl.rrtype = true\n\n\t\t\t\t\t\t\tl.value = zRrtpe\n\t\t\t\t\t\t\tl.torc = t\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tretL = *l\n\t\t\t\t}\n\n\t\t\t\tl.value = zNewline\n\t\t\t\tl.token = \"\\n\"\n\n\t\t\t\tzl.comment = zl.comBuf\n\t\t\t\tzl.comBuf = \"\"\n\t\t\t\tzl.rrtype = false\n\t\t\t\tzl.owner = true\n\n\t\t\t\tif retL != (lex{}) {\n\t\t\t\t\tzl.nextL = true\n\t\t\t\t\treturn retL, true\n\t\t\t\t}\n\n\t\t\t\treturn *l, true\n\t\t\t}\n\t\tcase '\\\\':\n\t\t\t// comments do not get escaped chars, everything is copied\n\t\t\tif zl.commt {\n\t\t\t\tcom[comi] = x\n\t\t\t\tcomi++\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// something already escaped must be in string\n\t\t\tif escape {\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\n\t\t\t\tescape = false\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// something escaped outside of string gets added to string\n\t\t\tstr[stri] = x\n\t\t\tstri++\n\n\t\t\tescape = true\n\t\tcase '\"':\n\t\t\tif zl.commt {\n\t\t\t\tcom[comi] = x\n\t\t\t\tcomi++\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif escape {\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\n\t\t\t\tescape = false\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tzl.space = false\n\n\t\t\t// send previous gathered text and the quote\n\t\t\tvar retL lex\n\t\t\tif stri != 0 {\n\t\t\t\tl.value = zString\n\t\t\t\tl.token = string(str[:stri])\n\n\t\t\t\tretL = *l\n\t\t\t}\n\n\t\t\t// send quote itself as separate token\n\t\t\tl.value = zQuote\n\t\t\tl.token = \"\\\"\"\n\n\t\t\tzl.quote = !zl.quote\n\n\t\t\tif retL != (lex{}) {\n\t\t\t\tzl.nextL = true\n\t\t\t\treturn retL, true\n\t\t\t}\n\n\t\t\treturn *l, true\n\t\tcase '(', ')':\n\t\t\tif zl.commt {\n\t\t\t\tcom[comi] = x\n\t\t\t\tcomi++\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif escape || zl.quote {\n\t\t\t\t// Inside quotes or escaped this is legal.\n\t\t\t\tstr[stri] = x\n\t\t\t\tstri++\n\n\t\t\t\tescape = false\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tswitch x {\n\t\t\tcase ')':\n\t\t\t\tzl.brace--\n\n\t\t\t\tif zl.brace < 0 {\n\t\t\t\t\tl.token = \"extra closing brace\"\n\t\t\t\t\tl.err = true\n\t\t\t\t\treturn *l, true\n\t\t\t\t}\n\t\t\tcase '(':\n\t\t\t\tzl.brace++\n\t\t\t}\n\t\tdefault:\n\t\t\tescape = false\n\n\t\t\tif zl.commt {\n\t\t\t\tcom[comi] = x\n\t\t\t\tcomi++\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tstr[stri] = x\n\t\t\tstri++\n\n\t\t\tzl.space = false\n\t\t}\n\t}\n\n\tif zl.readErr != nil && zl.readErr != io.EOF {\n\t\t// Don't return any tokens after a read error occurs.\n\t\treturn lex{value: zEOF}, false\n\t}\n\n\tvar retL lex\n\tif stri > 0 {\n\t\t// Send remainder of str\n\t\tl.value = zString\n\t\tl.token = string(str[:stri])\n\t\tretL = *l\n\n\t\tif comi <= 0 {\n\t\t\treturn retL, true\n\t\t}\n\t}\n\n\tif comi > 0 {\n\t\t// Send remainder of com\n\t\tl.value = zNewline\n\t\tl.token = \"\\n\"\n\t\tzl.comment = string(com[:comi])\n\n\t\tif retL != (lex{}) {\n\t\t\tzl.nextL = true\n\t\t\treturn retL, true\n\t\t}\n\n\t\treturn *l, true\n\t}\n\n\tif zl.brace != 0 {\n\t\tl.token = \"unbalanced brace\"\n\t\tl.err = true\n\t\treturn *l, true\n\t}\n\n\treturn lex{value: zEOF}, false\n}\n\nfunc (zl *zlexer) Comment() string {\n\tif zl.l.err {\n\t\treturn \"\"\n\t}\n\n\treturn zl.comment\n}\n\n// Extract the class number from CLASSxx\nfunc classToInt(token string) (uint16, bool) {\n\toffset := 5\n\tif len(token) < offset+1 {\n\t\treturn 0, false\n\t}\n\tclass, err := strconv.ParseUint(token[offset:], 10, 16)\n\tif err != nil {\n\t\treturn 0, false\n\t}\n\treturn uint16(class), true\n}\n\n// Extract the rr number from TYPExxx\nfunc typeToInt(token string) (uint16, bool) {\n\toffset := 4\n\tif len(token) < offset+1 {\n\t\treturn 0, false\n\t}\n\ttyp, err := strconv.ParseUint(token[offset:], 10, 16)\n\tif err != nil {\n\t\treturn 0, false\n\t}\n\treturn uint16(typ), true\n}\n\n// stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds.\nfunc stringToTTL(token string) (uint32, bool) {\n\tvar s, i uint\n\tfor _, c := range token {\n\t\tswitch c {\n\t\tcase 's', 'S':\n\t\t\ts += i\n\t\t\ti = 0\n\t\tcase 'm', 'M':\n\t\t\ts += i * 60\n\t\t\ti = 0\n\t\tcase 'h', 'H':\n\t\t\ts += i * 60 * 60\n\t\t\ti = 0\n\t\tcase 'd', 'D':\n\t\t\ts += i * 60 * 60 * 24\n\t\t\ti = 0\n\t\tcase 'w', 'W':\n\t\t\ts += i * 60 * 60 * 24 * 7\n\t\t\ti = 0\n\t\tcase '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':\n\t\t\ti *= 10\n\t\t\ti += uint(c) - '0'\n\t\tdefault:\n\t\t\treturn 0, false\n\t\t}\n\t}\n\tif s+i > math.MaxUint32 {\n\t\treturn 0, false\n\t}\n\treturn uint32(s + i), true\n}\n\n// Parse LOC records' <digits>[.<digits>][mM] into a\n// mantissa exponent format. Token should contain the entire\n// string (i.e. no spaces allowed)\nfunc stringToCm(token string) (e, m uint8, ok bool) {\n\tif token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {\n\t\ttoken = token[0 : len(token)-1]\n\t}\n\n\tvar (\n\t\tmeters, cmeters, val int\n\t\terr                  error\n\t)\n\tmStr, cmStr, hasCM := strings.Cut(token, \".\")\n\tif hasCM {\n\t\t// There's no point in having more than 2 digits in this part, and would rather make the implementation complicated ('123' should be treated as '12').\n\t\t// So we simply reject it.\n\t\t// We also make sure the first character is a digit to reject '+-' signs.\n\t\tcmeters, err = strconv.Atoi(cmStr)\n\t\tif err != nil || len(cmStr) > 2 || cmStr[0] < '0' || cmStr[0] > '9' {\n\t\t\treturn\n\t\t}\n\t\tif len(cmStr) == 1 {\n\t\t\t// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm.\n\t\t\tcmeters *= 10\n\t\t}\n\t}\n\t// This slightly ugly condition will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).\n\tif !hasCM || mStr != \"\" {\n\t\tmeters, err = strconv.Atoi(mStr)\n\t\t// RFC1876 states the max value is 90000000.00.  The latter two conditions enforce it.\n\t\tif err != nil || mStr[0] < '0' || mStr[0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif meters > 0 {\n\t\te = 2\n\t\tval = meters\n\t} else {\n\t\te = 0\n\t\tval = cmeters\n\t}\n\tfor val >= 10 {\n\t\te++\n\t\tval /= 10\n\t}\n\treturn e, uint8(val), true\n}\n\nfunc toAbsoluteName(name, origin string) (absolute string, ok bool) {\n\t// check for an explicit origin reference\n\tif name == \"@\" {\n\t\t// require a nonempty origin\n\t\tif origin == \"\" {\n\t\t\treturn \"\", false\n\t\t}\n\t\treturn origin, true\n\t}\n\n\t// this can happen when we have a comment after a RR that has a domain, '...   MX 20 ; this is wrong'.\n\t// technically a newline can be in a domain name, but this is clearly an error and the newline only shows\n\t// because of the scanning and the comment.\n\tif name == \"\\n\" {\n\t\treturn \"\", false\n\t}\n\n\t// require a valid domain name\n\t_, ok = IsDomainName(name)\n\tif !ok || name == \"\" {\n\t\treturn \"\", false\n\t}\n\n\t// check if name is already absolute\n\tif IsFqdn(name) {\n\t\treturn name, true\n\t}\n\n\t// require a nonempty origin\n\tif origin == \"\" {\n\t\treturn \"\", false\n\t}\n\treturn appendOrigin(name, origin), true\n}\n\nfunc appendOrigin(name, origin string) string {\n\tif origin == \".\" {\n\t\treturn name + origin\n\t}\n\treturn name + \".\" + origin\n}\n\n// LOC record helper function\nfunc locCheckNorth(token string, latitude uint32) (uint32, bool) {\n\tif latitude > 90*1000*60*60 {\n\t\treturn latitude, false\n\t}\n\tswitch token {\n\tcase \"n\", \"N\":\n\t\treturn LOC_EQUATOR + latitude, true\n\tcase \"s\", \"S\":\n\t\treturn LOC_EQUATOR - latitude, true\n\t}\n\treturn latitude, false\n}\n\n// LOC record helper function\nfunc locCheckEast(token string, longitude uint32) (uint32, bool) {\n\tif longitude > 180*1000*60*60 {\n\t\treturn longitude, false\n\t}\n\tswitch token {\n\tcase \"e\", \"E\":\n\t\treturn LOC_EQUATOR + longitude, true\n\tcase \"w\", \"W\":\n\t\treturn LOC_EQUATOR - longitude, true\n\t}\n\treturn longitude, false\n}\n\n// \"Eat\" the rest of the \"line\"\nfunc slurpRemainder(c *zlexer) *ParseError {\n\tl, _ := c.Next()\n\tswitch l.value {\n\tcase zBlank:\n\t\tl, _ = c.Next()\n\t\tif l.value != zNewline && l.value != zEOF {\n\t\t\treturn &ParseError{err: \"garbage after rdata\", lex: l}\n\t\t}\n\tcase zNewline:\n\tcase zEOF:\n\tdefault:\n\t\treturn &ParseError{err: \"garbage after rdata\", lex: l}\n\t}\n\treturn nil\n}\n\n// Parse a 64 bit-like ipv6 address: \"0014:4fff:ff20:ee64\"\n// Used for NID and L64 record.\nfunc stringToNodeID(l lex) (uint64, *ParseError) {\n\tif len(l.token) < 19 {\n\t\treturn 0, &ParseError{file: l.token, err: \"bad NID/L64 NodeID/Locator64\", lex: l}\n\t}\n\t// There must be three colons at fixes positions, if not its a parse error\n\tif l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {\n\t\treturn 0, &ParseError{file: l.token, err: \"bad NID/L64 NodeID/Locator64\", lex: l}\n\t}\n\ts := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]\n\tu, err := strconv.ParseUint(s, 16, 64)\n\tif err != nil {\n\t\treturn 0, &ParseError{file: l.token, err: \"bad NID/L64 NodeID/Locator64\", lex: l}\n\t}\n\treturn u, nil\n}\n"
  },
  {
    "path": "scan_rr.go",
    "content": "package dns\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces)\n// or an error\nfunc endingToString(c *zlexer, errstr string) (string, *ParseError) {\n\tvar s strings.Builder\n\tl, _ := c.Next() // zString\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tif l.err {\n\t\t\treturn s.String(), &ParseError{err: errstr, lex: l}\n\t\t}\n\t\tswitch l.value {\n\t\tcase zString:\n\t\t\ts.WriteString(l.token)\n\t\tcase zBlank: // Ok\n\t\tdefault:\n\t\t\treturn \"\", &ParseError{err: errstr, lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\n\treturn s.String(), nil\n}\n\n// A remainder of the rdata with embedded spaces, split on unquoted whitespace\n// and return the parsed string slice or an error\nfunc endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) {\n\t// Get the remaining data until we see a zNewline\n\tl, _ := c.Next()\n\tif l.err {\n\t\treturn nil, &ParseError{err: errstr, lex: l}\n\t}\n\n\t// Build the slice\n\ts := make([]string, 0)\n\tquote := false\n\tempty := false\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tif l.err {\n\t\t\treturn nil, &ParseError{err: errstr, lex: l}\n\t\t}\n\t\tswitch l.value {\n\t\tcase zString:\n\t\t\tempty = false\n\t\t\t// split up tokens that are larger than 255 into 255-chunks\n\t\t\tsx := []string{}\n\t\t\tp := 0\n\t\t\tfor {\n\t\t\t\ti, ok := escapedStringOffset(l.token[p:], 255)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, &ParseError{err: errstr, lex: l}\n\t\t\t\t}\n\t\t\t\tif i != -1 && p+i != len(l.token) {\n\t\t\t\t\tsx = append(sx, l.token[p:p+i])\n\t\t\t\t} else {\n\t\t\t\t\tsx = append(sx, l.token[p:])\n\t\t\t\t\tbreak\n\n\t\t\t\t}\n\t\t\t\tp += i\n\t\t\t}\n\t\t\ts = append(s, sx...)\n\t\tcase zBlank:\n\t\t\tif quote {\n\t\t\t\t// zBlank can only be seen in between txt parts.\n\t\t\t\treturn nil, &ParseError{err: errstr, lex: l}\n\t\t\t}\n\t\tcase zQuote:\n\t\t\tif empty && quote {\n\t\t\t\ts = append(s, \"\")\n\t\t\t}\n\t\t\tquote = !quote\n\t\t\tempty = true\n\t\tdefault:\n\t\t\treturn nil, &ParseError{err: errstr, lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\n\tif quote {\n\t\treturn nil, &ParseError{err: errstr, lex: l}\n\t}\n\n\treturn s, nil\n}\n\nfunc (rr *A) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\trr.A = net.ParseIP(l.token)\n\t// IPv4 addresses cannot include \":\".\n\t// We do this rather than use net.IP's To4() because\n\t// To4() treats IPv4-mapped IPv6 addresses as being\n\t// IPv4.\n\tisIPv4 := !strings.Contains(l.token, \":\")\n\tif rr.A == nil || !isIPv4 || l.err {\n\t\treturn &ParseError{err: \"bad A A\", lex: l}\n\t}\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *AAAA) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\trr.AAAA = net.ParseIP(l.token)\n\t// IPv6 addresses must include \":\", and IPv4\n\t// addresses cannot include \":\".\n\tisIPv6 := strings.Contains(l.token, \":\")\n\tif rr.AAAA == nil || !isIPv6 || l.err {\n\t\treturn &ParseError{err: \"bad AAAA AAAA\", lex: l}\n\t}\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *NS) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad NS Ns\", lex: l}\n\t}\n\trr.Ns = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *PTR) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad PTR Ptr\", lex: l}\n\t}\n\trr.Ptr = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *NSAPPTR) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad NSAP-PTR Ptr\", lex: l}\n\t}\n\trr.Ptr = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *RP) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tmbox, mboxOk := toAbsoluteName(l.token, o)\n\tif l.err || !mboxOk {\n\t\treturn &ParseError{err: \"bad RP Mbox\", lex: l}\n\t}\n\trr.Mbox = mbox\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\trr.Txt = l.token\n\n\ttxt, txtOk := toAbsoluteName(l.token, o)\n\tif l.err || !txtOk {\n\t\treturn &ParseError{err: \"bad RP Txt\", lex: l}\n\t}\n\trr.Txt = txt\n\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *MR) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad MR Mr\", lex: l}\n\t}\n\trr.Mr = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *MB) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad MB Mb\", lex: l}\n\t}\n\trr.Mb = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *MG) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad MG Mg\", lex: l}\n\t}\n\trr.Mg = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *HINFO) parse(c *zlexer, o string) *ParseError {\n\tchunks, e := endingToTxtSlice(c, \"bad HINFO Fields\")\n\tif e != nil {\n\t\treturn e\n\t}\n\n\tif ln := len(chunks); ln == 0 {\n\t\treturn nil\n\t} else if ln == 1 {\n\t\t// Can we split it?\n\t\tif out := strings.Fields(chunks[0]); len(out) > 1 {\n\t\t\tchunks = out\n\t\t} else {\n\t\t\tchunks = append(chunks, \"\")\n\t\t}\n\t}\n\n\trr.Cpu = chunks[0]\n\trr.Os = strings.Join(chunks[1:], \" \")\n\treturn nil\n}\n\n// according to RFC 1183 the parsing is identical to HINFO, so just use that code.\nfunc (rr *ISDN) parse(c *zlexer, o string) *ParseError {\n\tchunks, e := endingToTxtSlice(c, \"bad ISDN Fields\")\n\tif e != nil {\n\t\treturn e\n\t}\n\n\tif ln := len(chunks); ln == 0 {\n\t\treturn nil\n\t} else if ln == 1 {\n\t\t// Can we split it?\n\t\tif out := strings.Fields(chunks[0]); len(out) > 1 {\n\t\t\tchunks = out\n\t\t} else {\n\t\t\tchunks = append(chunks, \"\")\n\t\t}\n\t}\n\n\trr.Address = chunks[0]\n\trr.SubAddress = strings.Join(chunks[1:], \" \")\n\n\treturn nil\n}\n\nfunc (rr *MINFO) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\trmail, rmailOk := toAbsoluteName(l.token, o)\n\tif l.err || !rmailOk {\n\t\treturn &ParseError{err: \"bad MINFO Rmail\", lex: l}\n\t}\n\trr.Rmail = rmail\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\trr.Email = l.token\n\n\temail, emailOk := toAbsoluteName(l.token, o)\n\tif l.err || !emailOk {\n\t\treturn &ParseError{err: \"bad MINFO Email\", lex: l}\n\t}\n\trr.Email = email\n\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *MF) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad MF Mf\", lex: l}\n\t}\n\trr.Mf = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *MD) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad MD Md\", lex: l}\n\t}\n\trr.Md = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *MX) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad MX Pref\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Mx = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad MX Mx\", lex: l}\n\t}\n\trr.Mx = name\n\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *RT) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil {\n\t\treturn &ParseError{err: \"bad RT Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Host = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad RT Host\", lex: l}\n\t}\n\trr.Host = name\n\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *AFSDB) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad AFSDB Subtype\", lex: l}\n\t}\n\trr.Subtype = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Hostname = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad AFSDB Hostname\", lex: l}\n\t}\n\trr.Hostname = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *X25) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tif l.err {\n\t\treturn &ParseError{err: \"bad X25 PSDNAddress\", lex: l}\n\t}\n\trr.PSDNAddress = l.token\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *KX) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad KX Pref\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Exchanger = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad KX Exchanger\", lex: l}\n\t}\n\trr.Exchanger = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *CNAME) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad CNAME Target\", lex: l}\n\t}\n\trr.Target = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *DNAME) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad DNAME Target\", lex: l}\n\t}\n\trr.Target = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *SOA) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tns, nsOk := toAbsoluteName(l.token, o)\n\tif l.err || !nsOk {\n\t\treturn &ParseError{err: \"bad SOA Ns\", lex: l}\n\t}\n\trr.Ns = ns\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\trr.Mbox = l.token\n\n\tmbox, mboxOk := toAbsoluteName(l.token, o)\n\tif l.err || !mboxOk {\n\t\treturn &ParseError{err: \"bad SOA Mbox\", lex: l}\n\t}\n\trr.Mbox = mbox\n\n\tc.Next() // zBlank\n\n\tvar (\n\t\tv  uint32\n\t\tok bool\n\t)\n\tfor i := 0; i < 5; i++ {\n\t\tl, _ = c.Next()\n\t\tif l.err {\n\t\t\treturn &ParseError{err: \"bad SOA zone parameter\", lex: l}\n\t\t}\n\t\tif j, err := strconv.ParseUint(l.token, 10, 32); err != nil {\n\t\t\tif i == 0 {\n\t\t\t\t// Serial must be a number\n\t\t\t\treturn &ParseError{err: \"bad SOA zone parameter\", lex: l}\n\t\t\t}\n\t\t\t// We allow other fields to be unitful duration strings\n\t\t\tif v, ok = stringToTTL(l.token); !ok {\n\t\t\t\treturn &ParseError{err: \"bad SOA zone parameter\", lex: l}\n\n\t\t\t}\n\t\t} else {\n\t\t\tv = uint32(j)\n\t\t}\n\t\tswitch i {\n\t\tcase 0:\n\t\t\trr.Serial = v\n\t\t\tc.Next() // zBlank\n\t\tcase 1:\n\t\t\trr.Refresh = v\n\t\t\tc.Next() // zBlank\n\t\tcase 2:\n\t\t\trr.Retry = v\n\t\t\tc.Next() // zBlank\n\t\tcase 3:\n\t\t\trr.Expire = v\n\t\t\tc.Next() // zBlank\n\t\tcase 4:\n\t\t\trr.Minttl = v\n\t\t}\n\t}\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *SRV) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad SRV Priority\", lex: l}\n\t}\n\trr.Priority = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e1 := strconv.ParseUint(l.token, 10, 16)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad SRV Weight\", lex: l}\n\t}\n\trr.Weight = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e2 := strconv.ParseUint(l.token, 10, 16)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad SRV Port\", lex: l}\n\t}\n\trr.Port = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Target = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad SRV Target\", lex: l}\n\t}\n\trr.Target = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *NAPTR) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad NAPTR Order\", lex: l}\n\t}\n\trr.Order = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e1 := strconv.ParseUint(l.token, 10, 16)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad NAPTR Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\n\t// Flags\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // _QUOTE\n\tif l.value != zQuote {\n\t\treturn &ParseError{err: \"bad NAPTR Flags\", lex: l}\n\t}\n\tl, _ = c.Next() // Either String or Quote\n\tif l.value == zString {\n\t\trr.Flags = l.token\n\t\tl, _ = c.Next() // _QUOTE\n\t\tif l.value != zQuote {\n\t\t\treturn &ParseError{err: \"bad NAPTR Flags\", lex: l}\n\t\t}\n\t} else if l.value == zQuote {\n\t\trr.Flags = \"\"\n\t} else {\n\t\treturn &ParseError{err: \"bad NAPTR Flags\", lex: l}\n\t}\n\n\t// Service\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // _QUOTE\n\tif l.value != zQuote {\n\t\treturn &ParseError{err: \"bad NAPTR Service\", lex: l}\n\t}\n\tl, _ = c.Next() // Either String or Quote\n\tif l.value == zString {\n\t\trr.Service = l.token\n\t\tl, _ = c.Next() // _QUOTE\n\t\tif l.value != zQuote {\n\t\t\treturn &ParseError{err: \"bad NAPTR Service\", lex: l}\n\t\t}\n\t} else if l.value == zQuote {\n\t\trr.Service = \"\"\n\t} else {\n\t\treturn &ParseError{err: \"bad NAPTR Service\", lex: l}\n\t}\n\n\t// Regexp\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // _QUOTE\n\tif l.value != zQuote {\n\t\treturn &ParseError{err: \"bad NAPTR Regexp\", lex: l}\n\t}\n\tl, _ = c.Next() // Either String or Quote\n\tif l.value == zString {\n\t\trr.Regexp = l.token\n\t\tl, _ = c.Next() // _QUOTE\n\t\tif l.value != zQuote {\n\t\t\treturn &ParseError{err: \"bad NAPTR Regexp\", lex: l}\n\t\t}\n\t} else if l.value == zQuote {\n\t\trr.Regexp = \"\"\n\t} else {\n\t\treturn &ParseError{err: \"bad NAPTR Regexp\", lex: l}\n\t}\n\n\t// After quote no space??\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Replacement = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad NAPTR Replacement\", lex: l}\n\t}\n\trr.Replacement = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *TALINK) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tpreviousName, previousNameOk := toAbsoluteName(l.token, o)\n\tif l.err || !previousNameOk {\n\t\treturn &ParseError{err: \"bad TALINK PreviousName\", lex: l}\n\t}\n\trr.PreviousName = previousName\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\trr.NextName = l.token\n\n\tnextName, nextNameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nextNameOk {\n\t\treturn &ParseError{err: \"bad TALINK NextName\", lex: l}\n\t}\n\trr.NextName = nextName\n\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *LOC) parse(c *zlexer, o string) *ParseError {\n\t// Non zero defaults for LOC record, see RFC 1876, Section 3.\n\trr.Size = 0x12     // 1e2 cm (1m)\n\trr.HorizPre = 0x16 // 1e6 cm (10000m)\n\trr.VertPre = 0x13  // 1e3 cm (10m)\n\tok := false\n\n\t// North\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 32)\n\tif e != nil || l.err || i > 90 {\n\t\treturn &ParseError{err: \"bad LOC Latitude\", lex: l}\n\t}\n\trr.Latitude = 1000 * 60 * 60 * uint32(i)\n\n\tc.Next() // zBlank\n\t// Either number, 'N' or 'S'\n\tl, _ = c.Next()\n\tif rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {\n\t\tgoto East\n\t}\n\tif i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 {\n\t\treturn &ParseError{err: \"bad LOC Latitude minutes\", lex: l}\n\t} else {\n\t\trr.Latitude += 1000 * 60 * uint32(i)\n\t}\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {\n\t\treturn &ParseError{err: \"bad LOC Latitude seconds\", lex: l}\n\t} else {\n\t\trr.Latitude += uint32(1000 * i)\n\t}\n\tc.Next() // zBlank\n\t// Either number, 'N' or 'S'\n\tl, _ = c.Next()\n\tif rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {\n\t\tgoto East\n\t}\n\t// If still alive, flag an error\n\treturn &ParseError{err: \"bad LOC Latitude North/South\", lex: l}\n\nEast:\n\t// East\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 180 {\n\t\treturn &ParseError{err: \"bad LOC Longitude\", lex: l}\n\t} else {\n\t\trr.Longitude = 1000 * 60 * 60 * uint32(i)\n\t}\n\tc.Next() // zBlank\n\t// Either number, 'E' or 'W'\n\tl, _ = c.Next()\n\tif rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {\n\t\tgoto Altitude\n\t}\n\tif i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 {\n\t\treturn &ParseError{err: \"bad LOC Longitude minutes\", lex: l}\n\t} else {\n\t\trr.Longitude += 1000 * 60 * uint32(i)\n\t}\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {\n\t\treturn &ParseError{err: \"bad LOC Longitude seconds\", lex: l}\n\t} else {\n\t\trr.Longitude += uint32(1000 * i)\n\t}\n\tc.Next() // zBlank\n\t// Either number, 'E' or 'W'\n\tl, _ = c.Next()\n\tif rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {\n\t\tgoto Altitude\n\t}\n\t// If still alive, flag an error\n\treturn &ParseError{err: \"bad LOC Longitude East/West\", lex: l}\n\nAltitude:\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif l.token == \"\" || l.err {\n\t\treturn &ParseError{err: \"bad LOC Altitude\", lex: l}\n\t}\n\tif l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {\n\t\tl.token = l.token[0 : len(l.token)-1]\n\t}\n\tif i, err := strconv.ParseFloat(l.token, 64); err != nil {\n\t\treturn &ParseError{err: \"bad LOC Altitude\", lex: l}\n\t} else {\n\t\trr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5)\n\t}\n\n\t// And now optionally the other values\n\tl, _ = c.Next()\n\tcount := 0\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tswitch l.value {\n\t\tcase zString:\n\t\t\tswitch count {\n\t\t\tcase 0: // Size\n\t\t\t\texp, m, ok := stringToCm(l.token)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn &ParseError{err: \"bad LOC Size\", lex: l}\n\t\t\t\t}\n\t\t\t\trr.Size = exp&0x0f | m<<4&0xf0\n\t\t\tcase 1: // HorizPre\n\t\t\t\texp, m, ok := stringToCm(l.token)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn &ParseError{err: \"bad LOC HorizPre\", lex: l}\n\t\t\t\t}\n\t\t\t\trr.HorizPre = exp&0x0f | m<<4&0xf0\n\t\t\tcase 2: // VertPre\n\t\t\t\texp, m, ok := stringToCm(l.token)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn &ParseError{err: \"bad LOC VertPre\", lex: l}\n\t\t\t\t}\n\t\t\t\trr.VertPre = exp&0x0f | m<<4&0xf0\n\t\t\t}\n\t\t\tcount++\n\t\tcase zBlank:\n\t\t\t// Ok\n\t\tdefault:\n\t\t\treturn &ParseError{err: \"bad LOC Size, HorizPre or VertPre\", lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\treturn nil\n}\n\nfunc (rr *HIP) parse(c *zlexer, o string) *ParseError {\n\t// HitLength is not represented\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad HIP PublicKeyAlgorithm\", lex: l}\n\t}\n\trr.PublicKeyAlgorithm = uint8(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\tif l.token == \"\" || l.err {\n\t\treturn &ParseError{err: \"bad HIP Hit\", lex: l}\n\t}\n\trr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.\n\trr.HitLength = uint8(len(rr.Hit)) / 2\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\tif l.token == \"\" || l.err {\n\t\treturn &ParseError{err: \"bad HIP PublicKey\", lex: l}\n\t}\n\trr.PublicKey = l.token // This cannot contain spaces\n\tdecodedPK, decodedPKerr := base64.StdEncoding.DecodeString(rr.PublicKey)\n\tif decodedPKerr != nil {\n\t\treturn &ParseError{err: \"bad HIP PublicKey\", lex: l}\n\t}\n\trr.PublicKeyLength = uint16(len(decodedPK))\n\n\t// RendezvousServers (if any)\n\tl, _ = c.Next()\n\tvar xs []string\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tswitch l.value {\n\t\tcase zString:\n\t\t\tname, nameOk := toAbsoluteName(l.token, o)\n\t\t\tif l.err || !nameOk {\n\t\t\t\treturn &ParseError{err: \"bad HIP RendezvousServers\", lex: l}\n\t\t\t}\n\t\t\txs = append(xs, name)\n\t\tcase zBlank:\n\t\t\t// Ok\n\t\tdefault:\n\t\t\treturn &ParseError{err: \"bad HIP RendezvousServers\", lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\n\trr.RendezvousServers = xs\n\treturn nil\n}\n\nfunc (rr *CERT) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tif v, ok := StringToCertType[l.token]; ok {\n\t\trr.Type = v\n\t} else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil {\n\t\treturn &ParseError{err: \"bad CERT Type\", lex: l}\n\t} else {\n\t\trr.Type = uint16(i)\n\t}\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad CERT KeyTag\", lex: l}\n\t}\n\trr.KeyTag = uint16(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\tif v, ok := StringToAlgorithm[l.token]; ok {\n\t\trr.Algorithm = v\n\t} else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {\n\t\treturn &ParseError{err: \"bad CERT Algorithm\", lex: l}\n\t} else {\n\t\trr.Algorithm = uint8(i)\n\t}\n\ts, e1 := endingToString(c, \"bad CERT Certificate\")\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\trr.Certificate = s\n\treturn nil\n}\n\nfunc (rr *OPENPGPKEY) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToString(c, \"bad OPENPGPKEY PublicKey\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.PublicKey = s\n\treturn nil\n}\n\nfunc (rr *CSYNC) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tj, e := strconv.ParseUint(l.token, 10, 32)\n\tif e != nil {\n\t\t// Serial must be a number\n\t\treturn &ParseError{err: \"bad CSYNC serial\", lex: l}\n\t}\n\trr.Serial = uint32(j)\n\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tj, e1 := strconv.ParseUint(l.token, 10, 16)\n\tif e1 != nil {\n\t\t// Serial must be a number\n\t\treturn &ParseError{err: \"bad CSYNC flags\", lex: l}\n\t}\n\trr.Flags = uint16(j)\n\n\trr.TypeBitMap = make([]uint16, 0)\n\tvar (\n\t\tk  uint16\n\t\tok bool\n\t)\n\tl, _ = c.Next()\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tswitch l.value {\n\t\tcase zBlank:\n\t\t\t// Ok\n\t\tcase zString:\n\t\t\ttokenUpper := strings.ToUpper(l.token)\n\t\t\tif k, ok = StringToType[tokenUpper]; !ok {\n\t\t\t\tif k, ok = typeToInt(l.token); !ok {\n\t\t\t\t\treturn &ParseError{err: \"bad CSYNC TypeBitMap\", lex: l}\n\t\t\t\t}\n\t\t\t}\n\t\t\trr.TypeBitMap = append(rr.TypeBitMap, k)\n\t\tdefault:\n\t\t\treturn &ParseError{err: \"bad CSYNC TypeBitMap\", lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\treturn nil\n}\n\nfunc (rr *ZONEMD) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 32)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad ZONEMD Serial\", lex: l}\n\t}\n\trr.Serial = uint32(i)\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad ZONEMD Scheme\", lex: l}\n\t}\n\trr.Scheme = uint8(i)\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, err := strconv.ParseUint(l.token, 10, 8)\n\tif err != nil || l.err {\n\t\treturn &ParseError{err: \"bad ZONEMD Hash Algorithm\", lex: l}\n\t}\n\trr.Hash = uint8(i)\n\n\ts, e2 := endingToString(c, \"bad ZONEMD Digest\")\n\tif e2 != nil {\n\t\treturn e2\n\t}\n\trr.Digest = s\n\treturn nil\n}\n\nfunc (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) }\n\nfunc (rr *RRSIG) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ttokenUpper := strings.ToUpper(l.token)\n\tif t, ok := StringToType[tokenUpper]; !ok {\n\t\tif strings.HasPrefix(tokenUpper, \"TYPE\") {\n\t\t\tt, ok = typeToInt(l.token)\n\t\t\tif !ok {\n\t\t\t\treturn &ParseError{err: \"bad RRSIG Typecovered\", lex: l}\n\t\t\t}\n\t\t\trr.TypeCovered = t\n\t\t} else {\n\t\t\treturn &ParseError{err: \"bad RRSIG Typecovered\", lex: l}\n\t\t}\n\t} else {\n\t\trr.TypeCovered = t\n\t}\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif l.err {\n\t\treturn &ParseError{err: \"bad RRSIG Algorithm\", lex: l}\n\t}\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\trr.Algorithm = uint8(i) // if 0 we'll check the mnemonic in the if\n\tif e != nil {\n\t\tv, ok := StringToAlgorithm[l.token]\n\t\tif !ok {\n\t\t\treturn &ParseError{err: \"bad RRSIG Algorithm\", lex: l}\n\t\t}\n\t\trr.Algorithm = v\n\t}\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad RRSIG Labels\", lex: l}\n\t}\n\trr.Labels = uint8(i)\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e2 := strconv.ParseUint(l.token, 10, 32)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad RRSIG OrigTtl\", lex: l}\n\t}\n\trr.OrigTtl = uint32(i)\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := StringToTime(l.token); err != nil {\n\t\t// Try to see if all numeric and use it as epoch\n\t\tif i, err := strconv.ParseUint(l.token, 10, 32); err == nil {\n\t\t\trr.Expiration = uint32(i)\n\t\t} else {\n\t\t\treturn &ParseError{err: \"bad RRSIG Expiration\", lex: l}\n\t\t}\n\t} else {\n\t\trr.Expiration = i\n\t}\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := StringToTime(l.token); err != nil {\n\t\tif i, err := strconv.ParseUint(l.token, 10, 32); err == nil {\n\t\t\trr.Inception = uint32(i)\n\t\t} else {\n\t\t\treturn &ParseError{err: \"bad RRSIG Inception\", lex: l}\n\t\t}\n\t} else {\n\t\trr.Inception = i\n\t}\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e3 := strconv.ParseUint(l.token, 10, 16)\n\tif e3 != nil || l.err {\n\t\treturn &ParseError{err: \"bad RRSIG KeyTag\", lex: l}\n\t}\n\trr.KeyTag = uint16(i)\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\trr.SignerName = l.token\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad RRSIG SignerName\", lex: l}\n\t}\n\trr.SignerName = name\n\n\ts, e4 := endingToString(c, \"bad RRSIG Signature\")\n\tif e4 != nil {\n\t\treturn e4\n\t}\n\trr.Signature = s\n\n\treturn nil\n}\n\nfunc (rr *NXT) parse(c *zlexer, o string) *ParseError { return rr.NSEC.parse(c, o) }\n\nfunc (rr *NSEC) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad NSEC NextDomain\", lex: l}\n\t}\n\trr.NextDomain = name\n\n\trr.TypeBitMap = make([]uint16, 0)\n\tvar (\n\t\tk  uint16\n\t\tok bool\n\t)\n\tl, _ = c.Next()\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tswitch l.value {\n\t\tcase zBlank:\n\t\t\t// Ok\n\t\tcase zString:\n\t\t\ttokenUpper := strings.ToUpper(l.token)\n\t\t\tif k, ok = StringToType[tokenUpper]; !ok {\n\t\t\t\tif k, ok = typeToInt(l.token); !ok {\n\t\t\t\t\treturn &ParseError{err: \"bad NSEC TypeBitMap\", lex: l}\n\t\t\t\t}\n\t\t\t}\n\t\t\trr.TypeBitMap = append(rr.TypeBitMap, k)\n\t\tdefault:\n\t\t\treturn &ParseError{err: \"bad NSEC TypeBitMap\", lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\treturn nil\n}\n\nfunc (rr *NSEC3) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3 Hash\", lex: l}\n\t}\n\trr.Hash = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3 Flags\", lex: l}\n\t}\n\trr.Flags = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e2 := strconv.ParseUint(l.token, 10, 16)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3 Iterations\", lex: l}\n\t}\n\trr.Iterations = uint16(i)\n\tc.Next()\n\tl, _ = c.Next()\n\tif l.token == \"\" || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3 Salt\", lex: l}\n\t}\n\tif l.token != \"-\" {\n\t\trr.SaltLength = uint8(len(l.token)) / 2\n\t\trr.Salt = l.token\n\t}\n\n\tc.Next()\n\tl, _ = c.Next()\n\tif l.token == \"\" || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3 NextDomain\", lex: l}\n\t}\n\trr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)\n\trr.NextDomain = l.token\n\n\trr.TypeBitMap = make([]uint16, 0)\n\tvar (\n\t\tk  uint16\n\t\tok bool\n\t)\n\tl, _ = c.Next()\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tswitch l.value {\n\t\tcase zBlank:\n\t\t\t// Ok\n\t\tcase zString:\n\t\t\ttokenUpper := strings.ToUpper(l.token)\n\t\t\tif k, ok = StringToType[tokenUpper]; !ok {\n\t\t\t\tif k, ok = typeToInt(l.token); !ok {\n\t\t\t\t\treturn &ParseError{err: \"bad NSEC3 TypeBitMap\", lex: l}\n\t\t\t\t}\n\t\t\t}\n\t\t\trr.TypeBitMap = append(rr.TypeBitMap, k)\n\t\tdefault:\n\t\t\treturn &ParseError{err: \"bad NSEC3 TypeBitMap\", lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\treturn nil\n}\n\nfunc (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3PARAM Hash\", lex: l}\n\t}\n\trr.Hash = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3PARAM Flags\", lex: l}\n\t}\n\trr.Flags = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e2 := strconv.ParseUint(l.token, 10, 16)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad NSEC3PARAM Iterations\", lex: l}\n\t}\n\trr.Iterations = uint16(i)\n\tc.Next()\n\tl, _ = c.Next()\n\tif l.token != \"-\" {\n\t\trr.SaltLength = uint8(len(l.token) / 2)\n\t\trr.Salt = l.token\n\t}\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *EUI48) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tif len(l.token) != 17 || l.err {\n\t\treturn &ParseError{err: \"bad EUI48 Address\", lex: l}\n\t}\n\taddr := make([]byte, 12)\n\tdash := 0\n\tfor i := 0; i < 10; i += 2 {\n\t\taddr[i] = l.token[i+dash]\n\t\taddr[i+1] = l.token[i+1+dash]\n\t\tdash++\n\t\tif l.token[i+1+dash] != '-' {\n\t\t\treturn &ParseError{err: \"bad EUI48 Address\", lex: l}\n\t\t}\n\t}\n\taddr[10] = l.token[15]\n\taddr[11] = l.token[16]\n\n\ti, e := strconv.ParseUint(string(addr), 16, 48)\n\tif e != nil {\n\t\treturn &ParseError{err: \"bad EUI48 Address\", lex: l}\n\t}\n\trr.Address = i\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *EUI64) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tif len(l.token) != 23 || l.err {\n\t\treturn &ParseError{err: \"bad EUI64 Address\", lex: l}\n\t}\n\taddr := make([]byte, 16)\n\tdash := 0\n\tfor i := 0; i < 14; i += 2 {\n\t\taddr[i] = l.token[i+dash]\n\t\taddr[i+1] = l.token[i+1+dash]\n\t\tdash++\n\t\tif l.token[i+1+dash] != '-' {\n\t\t\treturn &ParseError{err: \"bad EUI64 Address\", lex: l}\n\t\t}\n\t}\n\taddr[14] = l.token[21]\n\taddr[15] = l.token[22]\n\n\ti, e := strconv.ParseUint(string(addr), 16, 64)\n\tif e != nil {\n\t\treturn &ParseError{err: \"bad EUI68 Address\", lex: l}\n\t}\n\trr.Address = i\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *SSHFP) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad SSHFP Algorithm\", lex: l}\n\t}\n\trr.Algorithm = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad SSHFP Type\", lex: l}\n\t}\n\trr.Type = uint8(i)\n\tc.Next() // zBlank\n\ts, e2 := endingToString(c, \"bad SSHFP Fingerprint\")\n\tif e2 != nil {\n\t\treturn e2\n\t}\n\trr.FingerPrint = s\n\treturn nil\n}\n\nfunc (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad \" + typ + \" Flags\", lex: l}\n\t}\n\trr.Flags = uint16(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad \" + typ + \" Protocol\", lex: l}\n\t}\n\trr.Protocol = uint8(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e2 := strconv.ParseUint(l.token, 10, 8)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad \" + typ + \" Algorithm\", lex: l}\n\t}\n\trr.Algorithm = uint8(i)\n\ts, e3 := endingToString(c, \"bad \"+typ+\" PublicKey\")\n\tif e3 != nil {\n\t\treturn e3\n\t}\n\trr.PublicKey = s\n\treturn nil\n}\n\nfunc (rr *DNSKEY) parse(c *zlexer, o string) *ParseError  { return rr.parseDNSKEY(c, o, \"DNSKEY\") }\nfunc (rr *KEY) parse(c *zlexer, o string) *ParseError     { return rr.parseDNSKEY(c, o, \"KEY\") }\nfunc (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, \"CDNSKEY\") }\nfunc (rr *DS) parse(c *zlexer, o string) *ParseError      { return rr.parseDS(c, o, \"DS\") }\nfunc (rr *DLV) parse(c *zlexer, o string) *ParseError     { return rr.parseDS(c, o, \"DLV\") }\nfunc (rr *CDS) parse(c *zlexer, o string) *ParseError     { return rr.parseDS(c, o, \"CDS\") }\n\nfunc (rr *IPSECKEY) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tnum, err := strconv.ParseUint(l.token, 10, 8)\n\tif err != nil || l.err {\n\t\treturn &ParseError{err: \"bad IPSECKEY value\", lex: l}\n\t}\n\trr.Precedence = uint8(num)\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tnum, err = strconv.ParseUint(l.token, 10, 8)\n\tif err != nil || l.err {\n\t\treturn &ParseError{err: \"bad IPSECKEY value\", lex: l}\n\t}\n\trr.GatewayType = uint8(num)\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tnum, err = strconv.ParseUint(l.token, 10, 8)\n\tif err != nil || l.err {\n\t\treturn &ParseError{err: \"bad IPSECKEY value\", lex: l}\n\t}\n\trr.Algorithm = uint8(num)\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tif l.err {\n\t\treturn &ParseError{err: \"bad IPSECKEY gateway\", lex: l}\n\t}\n\n\trr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType)\n\tif err != nil {\n\t\treturn &ParseError{wrappedErr: fmt.Errorf(\"IPSECKEY %w\", err), lex: l}\n\t}\n\n\tc.Next() // zBlank\n\n\ts, pErr := endingToString(c, \"bad IPSECKEY PublicKey\")\n\tif pErr != nil {\n\t\treturn pErr\n\t}\n\trr.PublicKey = s\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *AMTRELAY) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tnum, err := strconv.ParseUint(l.token, 10, 8)\n\tif err != nil || l.err {\n\t\treturn &ParseError{err: \"bad AMTRELAY value\", lex: l}\n\t}\n\trr.Precedence = uint8(num)\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tif l.err || !(l.token == \"0\" || l.token == \"1\") {\n\t\treturn &ParseError{err: \"bad discovery value\", lex: l}\n\t}\n\tif l.token == \"1\" {\n\t\trr.GatewayType = 0x80\n\t}\n\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tnum, err = strconv.ParseUint(l.token, 10, 8)\n\tif err != nil || l.err {\n\t\treturn &ParseError{err: \"bad AMTRELAY value\", lex: l}\n\t}\n\trr.GatewayType |= uint8(num)\n\tc.Next() // zBlank\n\n\tl, _ = c.Next()\n\tif l.err {\n\t\treturn &ParseError{err: \"bad AMTRELAY gateway\", lex: l}\n\t}\n\n\trr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType&0x7f)\n\tif err != nil {\n\t\treturn &ParseError{wrappedErr: fmt.Errorf(\"AMTRELAY %w\", err), lex: l}\n\t}\n\n\treturn slurpRemainder(c)\n}\n\n// same constants and parsing between IPSECKEY and AMTRELAY\nfunc parseAddrHostUnion(token, o string, gatewayType uint8) (addr net.IP, host string, err error) {\n\tswitch gatewayType {\n\tcase IPSECGatewayNone:\n\t\tif token != \".\" {\n\t\t\treturn addr, host, errors.New(\"gateway type none with gateway set\")\n\t\t}\n\tcase IPSECGatewayIPv4, IPSECGatewayIPv6:\n\t\taddr = net.ParseIP(token)\n\t\tif addr == nil {\n\t\t\treturn addr, host, errors.New(\"gateway IP invalid\")\n\t\t}\n\t\tif (addr.To4() == nil) == (gatewayType == IPSECGatewayIPv4) {\n\t\t\treturn addr, host, errors.New(\"gateway IP family mismatch\")\n\t\t}\n\tcase IPSECGatewayHost:\n\t\tvar ok bool\n\t\thost, ok = toAbsoluteName(token, o)\n\t\tif !ok {\n\t\t\treturn addr, host, errors.New(\"invalid gateway host\")\n\t\t}\n\t}\n\n\treturn addr, host, nil\n}\n\nfunc (rr *RKEY) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad RKEY Flags\", lex: l}\n\t}\n\trr.Flags = uint16(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad RKEY Protocol\", lex: l}\n\t}\n\trr.Protocol = uint8(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\ti, e2 := strconv.ParseUint(l.token, 10, 8)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad RKEY Algorithm\", lex: l}\n\t}\n\trr.Algorithm = uint8(i)\n\ts, e3 := endingToString(c, \"bad RKEY PublicKey\")\n\tif e3 != nil {\n\t\treturn e3\n\t}\n\trr.PublicKey = s\n\treturn nil\n}\n\nfunc (rr *EID) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToString(c, \"bad EID Endpoint\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Endpoint = s\n\treturn nil\n}\n\nfunc (rr *NIMLOC) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToString(c, \"bad NIMLOC Locator\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Locator = s\n\treturn nil\n}\n\nfunc (rr *GPOS) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\t_, e := strconv.ParseFloat(l.token, 64)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad GPOS Longitude\", lex: l}\n\t}\n\trr.Longitude = l.token\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\t_, e1 := strconv.ParseFloat(l.token, 64)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad GPOS Latitude\", lex: l}\n\t}\n\trr.Latitude = l.token\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\t_, e2 := strconv.ParseFloat(l.token, 64)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad GPOS Altitude\", lex: l}\n\t}\n\trr.Altitude = l.token\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad \" + typ + \" KeyTag\", lex: l}\n\t}\n\trr.KeyTag = uint16(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := strconv.ParseUint(l.token, 10, 8); err != nil {\n\t\ttokenUpper := strings.ToUpper(l.token)\n\t\ti, ok := StringToAlgorithm[tokenUpper]\n\t\tif !ok || l.err {\n\t\t\treturn &ParseError{err: \"bad \" + typ + \" Algorithm\", lex: l}\n\t\t}\n\t\trr.Algorithm = i\n\t} else {\n\t\trr.Algorithm = uint8(i)\n\t}\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad \" + typ + \" DigestType\", lex: l}\n\t}\n\trr.DigestType = uint8(i)\n\ts, e2 := endingToString(c, \"bad \"+typ+\" Digest\")\n\tif e2 != nil {\n\t\treturn e2\n\t}\n\trr.Digest = s\n\treturn nil\n}\n\nfunc (rr *TA) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad TA KeyTag\", lex: l}\n\t}\n\trr.KeyTag = uint16(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif i, err := strconv.ParseUint(l.token, 10, 8); err != nil {\n\t\ttokenUpper := strings.ToUpper(l.token)\n\t\ti, ok := StringToAlgorithm[tokenUpper]\n\t\tif !ok || l.err {\n\t\t\treturn &ParseError{err: \"bad TA Algorithm\", lex: l}\n\t\t}\n\t\trr.Algorithm = i\n\t} else {\n\t\trr.Algorithm = uint8(i)\n\t}\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad TA DigestType\", lex: l}\n\t}\n\trr.DigestType = uint8(i)\n\ts, e2 := endingToString(c, \"bad TA Digest\")\n\tif e2 != nil {\n\t\treturn e2\n\t}\n\trr.Digest = s\n\treturn nil\n}\n\nfunc (rr *TLSA) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad TLSA Usage\", lex: l}\n\t}\n\trr.Usage = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad TLSA Selector\", lex: l}\n\t}\n\trr.Selector = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e2 := strconv.ParseUint(l.token, 10, 8)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad TLSA MatchingType\", lex: l}\n\t}\n\trr.MatchingType = uint8(i)\n\t// So this needs be e2 (i.e. different than e), because...??t\n\ts, e3 := endingToString(c, \"bad TLSA Certificate\")\n\tif e3 != nil {\n\t\treturn e3\n\t}\n\trr.Certificate = s\n\treturn nil\n}\n\nfunc (rr *SMIMEA) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad SMIMEA Usage\", lex: l}\n\t}\n\trr.Usage = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad SMIMEA Selector\", lex: l}\n\t}\n\trr.Selector = uint8(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e2 := strconv.ParseUint(l.token, 10, 8)\n\tif e2 != nil || l.err {\n\t\treturn &ParseError{err: \"bad SMIMEA MatchingType\", lex: l}\n\t}\n\trr.MatchingType = uint8(i)\n\t// So this needs be e2 (i.e. different than e), because...??t\n\ts, e3 := endingToString(c, \"bad SMIMEA Certificate\")\n\tif e3 != nil {\n\t\treturn e3\n\t}\n\trr.Certificate = s\n\treturn nil\n}\n\nfunc (rr *RFC3597) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\tif l.token != \"\\\\#\" {\n\t\treturn &ParseError{err: \"bad RFC3597 Rdata\", lex: l}\n\t}\n\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\trdlength, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad RFC3597 Rdata \", lex: l}\n\t}\n\n\ts, e1 := endingToString(c, \"bad RFC3597 Rdata\")\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif int(rdlength)*2 != len(s) {\n\t\treturn &ParseError{err: \"bad RFC3597 Rdata\", lex: l}\n\t}\n\trr.Rdata = s\n\treturn nil\n}\n\nfunc (rr *SPF) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToTxtSlice(c, \"bad SPF Txt\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Txt = s\n\treturn nil\n}\n\nfunc (rr *AVC) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToTxtSlice(c, \"bad AVC Txt\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Txt = s\n\treturn nil\n}\n\nfunc (rr *TXT) parse(c *zlexer, o string) *ParseError {\n\t// no zBlank reading here, because all this rdata is TXT\n\ts, e := endingToTxtSlice(c, \"bad TXT Txt\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Txt = s\n\treturn nil\n}\n\n// identical to setTXT\nfunc (rr *NINFO) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToTxtSlice(c, \"bad NINFO ZSData\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.ZSData = s\n\treturn nil\n}\n\n// Uses the same format as TXT\nfunc (rr *RESINFO) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToTxtSlice(c, \"bad RESINFO Resinfo\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Txt = s\n\treturn nil\n}\n\nfunc (rr *URI) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad URI Priority\", lex: l}\n\t}\n\trr.Priority = uint16(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 16)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad URI Weight\", lex: l}\n\t}\n\trr.Weight = uint16(i)\n\n\tc.Next() // zBlank\n\ts, e2 := endingToTxtSlice(c, \"bad URI Target\")\n\tif e2 != nil {\n\t\treturn e2\n\t}\n\tif len(s) != 1 {\n\t\treturn &ParseError{err: \"bad URI Target\", lex: l}\n\t}\n\trr.Target = s[0]\n\treturn nil\n}\n\nfunc (rr *DHCID) parse(c *zlexer, o string) *ParseError {\n\t// awesome record to parse!\n\ts, e := endingToString(c, \"bad DHCID Digest\")\n\tif e != nil {\n\t\treturn e\n\t}\n\trr.Digest = s\n\treturn nil\n}\n\nfunc (rr *NID) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad NID Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\tu, e1 := stringToNodeID(l)\n\tif e1 != nil || l.err {\n\t\treturn e1\n\t}\n\trr.NodeID = u\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *L32) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad L32 Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Locator32 = net.ParseIP(l.token)\n\tif rr.Locator32 == nil || l.err {\n\t\treturn &ParseError{err: \"bad L32 Locator\", lex: l}\n\t}\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *LP) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad LP Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Fqdn = l.token\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{err: \"bad LP Fqdn\", lex: l}\n\t}\n\trr.Fqdn = name\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *L64) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad L64 Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\tu, e1 := stringToNodeID(l)\n\tif e1 != nil || l.err {\n\t\treturn e1\n\t}\n\trr.Locator64 = u\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *UID) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 32)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad UID Uid\", lex: l}\n\t}\n\trr.Uid = uint32(i)\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *GID) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 32)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad GID Gid\", lex: l}\n\t}\n\trr.Gid = uint32(i)\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *UINFO) parse(c *zlexer, o string) *ParseError {\n\ts, e := endingToTxtSlice(c, \"bad UINFO Uinfo\")\n\tif e != nil {\n\t\treturn e\n\t}\n\tif ln := len(s); ln == 0 {\n\t\treturn nil\n\t}\n\trr.Uinfo = s[0] // silently discard anything after the first character-string\n\treturn nil\n}\n\nfunc (rr *PX) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad PX Preference\", lex: l}\n\t}\n\trr.Preference = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Map822 = l.token\n\tmap822, map822Ok := toAbsoluteName(l.token, o)\n\tif l.err || !map822Ok {\n\t\treturn &ParseError{err: \"bad PX Map822\", lex: l}\n\t}\n\trr.Map822 = map822\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Mapx400 = l.token\n\tmapx400, mapx400Ok := toAbsoluteName(l.token, o)\n\tif l.err || !mapx400Ok {\n\t\treturn &ParseError{err: \"bad PX Mapx400\", lex: l}\n\t}\n\trr.Mapx400 = mapx400\n\treturn slurpRemainder(c)\n}\n\nfunc (rr *CAA) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad CAA Flag\", lex: l}\n\t}\n\trr.Flag = uint8(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\tif l.value != zString {\n\t\treturn &ParseError{err: \"bad CAA Tag\", lex: l}\n\t}\n\trr.Tag = l.token\n\n\tc.Next() // zBlank\n\ts, e1 := endingToTxtSlice(c, \"bad CAA Value\")\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif len(s) != 1 {\n\t\treturn &ParseError{err: \"bad CAA Value\", lex: l}\n\t}\n\trr.Value = s[0]\n\treturn nil\n}\n\nfunc (rr *TKEY) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\n\t// Algorithm\n\tif l.value != zString {\n\t\treturn &ParseError{err: \"bad TKEY algorithm\", lex: l}\n\t}\n\trr.Algorithm = l.token\n\tc.Next() // zBlank\n\n\t// Get the key length and key values\n\tl, _ = c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 8)\n\tif e != nil || l.err {\n\t\treturn &ParseError{err: \"bad TKEY key length\", lex: l}\n\t}\n\trr.KeySize = uint16(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif l.value != zString {\n\t\treturn &ParseError{err: \"bad TKEY key\", lex: l}\n\t}\n\trr.Key = l.token\n\tc.Next() // zBlank\n\n\t// Get the otherdata length and string data\n\tl, _ = c.Next()\n\ti, e1 := strconv.ParseUint(l.token, 10, 8)\n\tif e1 != nil || l.err {\n\t\treturn &ParseError{err: \"bad TKEY otherdata length\", lex: l}\n\t}\n\trr.OtherLen = uint16(i)\n\tc.Next() // zBlank\n\tl, _ = c.Next()\n\tif l.value != zString {\n\t\treturn &ParseError{err: \"bad TKEY otherday\", lex: l}\n\t}\n\trr.OtherData = l.token\n\treturn nil\n}\n\nfunc (rr *APL) parse(c *zlexer, o string) *ParseError {\n\tvar prefixes []APLPrefix\n\n\tfor {\n\t\tl, _ := c.Next()\n\t\tif l.value == zNewline || l.value == zEOF {\n\t\t\tbreak\n\t\t}\n\t\tif l.value == zBlank && prefixes != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif l.value != zString {\n\t\t\treturn &ParseError{err: \"unexpected APL field\", lex: l}\n\t\t}\n\n\t\t// Expected format: [!]afi:address/prefix\n\n\t\tcolon := strings.IndexByte(l.token, ':')\n\t\tif colon == -1 {\n\t\t\treturn &ParseError{err: \"missing colon in APL field\", lex: l}\n\t\t}\n\n\t\tfamily, cidr := l.token[:colon], l.token[colon+1:]\n\n\t\tvar negation bool\n\t\tif family != \"\" && family[0] == '!' {\n\t\t\tnegation = true\n\t\t\tfamily = family[1:]\n\t\t}\n\n\t\tafi, e := strconv.ParseUint(family, 10, 16)\n\t\tif e != nil {\n\t\t\treturn &ParseError{wrappedErr: fmt.Errorf(\"failed to parse APL family: %w\", e), lex: l}\n\t\t}\n\t\tvar addrLen int\n\t\tswitch afi {\n\t\tcase 1:\n\t\t\taddrLen = net.IPv4len\n\t\tcase 2:\n\t\t\taddrLen = net.IPv6len\n\t\tdefault:\n\t\t\treturn &ParseError{err: \"unrecognized APL family\", lex: l}\n\t\t}\n\n\t\tip, subnet, e1 := net.ParseCIDR(cidr)\n\t\tif e1 != nil {\n\t\t\treturn &ParseError{wrappedErr: fmt.Errorf(\"failed to parse APL address: %w\", e1), lex: l}\n\t\t}\n\t\tif !ip.Equal(subnet.IP) {\n\t\t\treturn &ParseError{err: \"extra bits in APL address\", lex: l}\n\t\t}\n\n\t\tif len(subnet.IP) != addrLen {\n\t\t\treturn &ParseError{err: \"address mismatch with the APL family\", lex: l}\n\t\t}\n\n\t\tprefixes = append(prefixes, APLPrefix{\n\t\t\tNegation: negation,\n\t\t\tNetwork:  *subnet,\n\t\t})\n\t}\n\n\trr.Prefixes = prefixes\n\treturn nil\n}\n\n// escapedStringOffset finds the offset within a string (which may contain escape\n// sequences) that corresponds to a certain byte offset. If the input offset is\n// out of bounds, -1 is returned (which is *not* considered an error).\nfunc escapedStringOffset(s string, desiredByteOffset int) (int, bool) {\n\tif desiredByteOffset == 0 {\n\t\treturn 0, true\n\t}\n\n\tcurrentByteOffset, i := 0, 0\n\n\tfor i < len(s) {\n\t\tcurrentByteOffset += 1\n\n\t\t// Skip escape sequences\n\t\tif s[i] != '\\\\' {\n\t\t\t// Single plain byte, not an escape sequence.\n\t\t\ti++\n\t\t} else if isDDD(s[i+1:]) {\n\t\t\t// Skip backslash and DDD.\n\t\t\ti += 4\n\t\t} else if len(s[i+1:]) < 1 {\n\t\t\t// No character following the backslash; that's an error.\n\t\t\treturn 0, false\n\t\t} else {\n\t\t\t// Skip backslash and following byte.\n\t\t\ti += 2\n\t\t}\n\n\t\tif currentByteOffset >= desiredByteOffset {\n\t\t\treturn i, true\n\t\t}\n\t}\n\n\treturn -1, true\n}\n"
  },
  {
    "path": "scan_test.go",
    "content": "package dns\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"testing/fstest\"\n)\n\nfunc TestZoneParserGenerate(t *testing.T) {\n\tzone := \"$ORIGIN example.org.\\n$GENERATE 10-12 foo${2,3,d} IN A 127.0.0.$\"\n\n\twantRRs := []RR{\n\t\t&A{Hdr: RR_Header{Name: \"foo012.example.org.\"}, A: net.ParseIP(\"127.0.0.10\")},\n\t\t&A{Hdr: RR_Header{Name: \"foo013.example.org.\"}, A: net.ParseIP(\"127.0.0.11\")},\n\t\t&A{Hdr: RR_Header{Name: \"foo014.example.org.\"}, A: net.ParseIP(\"127.0.0.12\")},\n\t}\n\n\twantIdx := 0\n\n\tz := NewZoneParser(strings.NewReader(zone), \"\", \"\")\n\n\tfor rr, ok := z.Next(); ok; rr, ok = z.Next() {\n\t\tif wantIdx >= len(wantRRs) {\n\t\t\tt.Fatalf(\"expected %d RRs, but got more\", len(wantRRs))\n\t\t}\n\t\tif got, want := rr.Header().Name, wantRRs[wantIdx].Header().Name; got != want {\n\t\t\tt.Fatalf(\"expected name %s, but got %s\", want, got)\n\t\t}\n\t\ta, okA := rr.(*A)\n\t\tif !okA {\n\t\t\tt.Fatalf(\"expected *A RR, but got %T\", rr)\n\t\t}\n\t\tif got, want := a.A, wantRRs[wantIdx].(*A).A; !got.Equal(want) {\n\t\t\tt.Fatalf(\"expected A with IP %v, but got %v\", got, want)\n\t\t}\n\t\twantIdx++\n\t}\n\n\tif err := z.Err(); err != nil {\n\t\tt.Fatalf(\"expected no error, but got %s\", err)\n\t}\n\n\tif wantIdx != len(wantRRs) {\n\t\tt.Errorf(\"too few records, expected %d, got %d\", len(wantRRs), wantIdx)\n\t}\n}\n\nfunc TestZoneParserInclude(t *testing.T) {\n\ttmpfile, err := os.CreateTemp(\"\", \"dns\")\n\tif err != nil {\n\t\tt.Fatalf(\"could not create tmpfile for test: %s\", err)\n\t}\n\tdefer os.Remove(tmpfile.Name())\n\n\tif _, err := tmpfile.WriteString(\"foo\\tIN\\tA\\t127.0.0.1\"); err != nil {\n\t\tt.Fatalf(\"unable to write content to tmpfile %q: %s\", tmpfile.Name(), err)\n\t}\n\tif err := tmpfile.Close(); err != nil {\n\t\tt.Fatalf(\"could not close tmpfile %q: %s\", tmpfile.Name(), err)\n\t}\n\n\tzone := \"$ORIGIN example.org.\\n$INCLUDE \" + tmpfile.Name() + \"\\nbar\\tIN\\tA\\t127.0.0.2\"\n\n\tvar got int\n\tz := NewZoneParser(strings.NewReader(zone), \"\", \"\")\n\tz.SetIncludeAllowed(true)\n\tfor rr, ok := z.Next(); ok; _, ok = z.Next() {\n\t\tswitch rr.Header().Name {\n\t\tcase \"foo.example.org.\", \"bar.example.org.\":\n\t\tdefault:\n\t\t\tt.Fatalf(\"expected foo.example.org. or bar.example.org., but got %s\", rr.Header().Name)\n\t\t}\n\t\tgot++\n\t}\n\tif err := z.Err(); err != nil {\n\t\tt.Fatalf(\"expected no error, but got %s\", err)\n\t}\n\n\tif expected := 2; got != expected {\n\t\tt.Errorf(\"failed to parse zone after include, expected %d records, got %d\", expected, got)\n\t}\n\n\tos.Remove(tmpfile.Name())\n\n\tz = NewZoneParser(strings.NewReader(zone), \"\", \"\")\n\tz.SetIncludeAllowed(true)\n\tz.Next()\n\tif err := z.Err(); err == nil ||\n\t\t!strings.Contains(err.Error(), \"failed to open\") ||\n\t\t!strings.Contains(err.Error(), tmpfile.Name()) ||\n\t\t!strings.Contains(err.Error(), \"no such file or directory\") {\n\t\tt.Fatalf(`expected error to contain: \"failed to open\", %q and \"no such file or directory\" but got: %s`,\n\t\t\ttmpfile.Name(), err)\n\t}\n}\n\nfunc TestZoneParserIncludeFS(t *testing.T) {\n\tfsys := fstest.MapFS{\n\t\t\"db.foo\": &fstest.MapFile{\n\t\t\tData: []byte(\"foo\\tIN\\tA\\t127.0.0.1\"),\n\t\t},\n\t}\n\tzone := \"$ORIGIN example.org.\\n$INCLUDE db.foo\\nbar\\tIN\\tA\\t127.0.0.2\"\n\n\tvar got int\n\tz := NewZoneParser(strings.NewReader(zone), \"\", \"\")\n\tz.SetIncludeAllowed(true)\n\tz.SetIncludeFS(fsys)\n\tfor rr, ok := z.Next(); ok; _, ok = z.Next() {\n\t\tswitch rr.Header().Name {\n\t\tcase \"foo.example.org.\", \"bar.example.org.\":\n\t\tdefault:\n\t\t\tt.Fatalf(\"expected foo.example.org. or bar.example.org., but got %s\", rr.Header().Name)\n\t\t}\n\t\tgot++\n\t}\n\tif err := z.Err(); err != nil {\n\t\tt.Fatalf(\"expected no error, but got %s\", err)\n\t}\n\n\tif expected := 2; got != expected {\n\t\tt.Errorf(\"failed to parse zone after include, expected %d records, got %d\", expected, got)\n\t}\n\n\tfsys = fstest.MapFS{}\n\n\tz = NewZoneParser(strings.NewReader(zone), \"\", \"\")\n\tz.SetIncludeAllowed(true)\n\tz.SetIncludeFS(fsys)\n\tz.Next()\n\tif err := z.Err(); !errors.Is(err, fs.ErrNotExist) {\n\t\tt.Fatalf(`expected fs.ErrNotExist but got: %T %v`, err, err)\n\t}\n}\n\nfunc TestZoneParserIncludeFSPaths(t *testing.T) {\n\tfsys := fstest.MapFS{\n\t\t\"baz/bat/db.foo\": &fstest.MapFile{\n\t\t\tData: []byte(\"foo\\tIN\\tA\\t127.0.0.1\"),\n\t\t},\n\t}\n\n\tfor _, p := range []string{\n\t\t\"../bat/db.foo\",\n\t\t\"/baz/bat/db.foo\",\n\t} {\n\t\tzone := \"$ORIGIN example.org.\\n$INCLUDE \" + p + \"\\nbar\\tIN\\tA\\t127.0.0.2\"\n\t\tvar got int\n\t\tz := NewZoneParser(strings.NewReader(zone), \"\", \"baz/quux/db.bar\")\n\t\tz.SetIncludeAllowed(true)\n\t\tz.SetIncludeFS(fsys)\n\t\tfor rr, ok := z.Next(); ok; _, ok = z.Next() {\n\t\t\tswitch rr.Header().Name {\n\t\t\tcase \"foo.example.org.\", \"bar.example.org.\":\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"$INCLUDE %q: expected foo.example.org. or bar.example.org., but got %s\", p, rr.Header().Name)\n\t\t\t}\n\t\t\tgot++\n\t\t}\n\t\tif err := z.Err(); err != nil {\n\t\t\tt.Fatalf(\"$INCLUDE %q: expected no error, but got %s\", p, err)\n\t\t}\n\t\tif expected := 2; got != expected {\n\t\t\tt.Errorf(\"$INCLUDE %q: failed to parse zone after include, expected %d records, got %d\", p, expected, got)\n\t\t}\n\t}\n}\n\nfunc TestZoneParserIncludeDisallowed(t *testing.T) {\n\ttmpfile, err := os.CreateTemp(\"\", \"dns\")\n\tif err != nil {\n\t\tt.Fatalf(\"could not create tmpfile for test: %s\", err)\n\t}\n\tdefer os.Remove(tmpfile.Name())\n\n\tif _, err := tmpfile.WriteString(\"foo\\tIN\\tA\\t127.0.0.1\"); err != nil {\n\t\tt.Fatalf(\"unable to write content to tmpfile %q: %s\", tmpfile.Name(), err)\n\t}\n\tif err := tmpfile.Close(); err != nil {\n\t\tt.Fatalf(\"could not close tmpfile %q: %s\", tmpfile.Name(), err)\n\t}\n\n\tzp := NewZoneParser(strings.NewReader(\"$INCLUDE \"+tmpfile.Name()), \"example.org.\", \"\")\n\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t}\n\n\tconst expect = \"$INCLUDE directive not allowed\"\n\tif err := zp.Err(); err == nil || !strings.Contains(err.Error(), expect) {\n\t\tt.Errorf(\"expected error to contain %q, got %v\", expect, err)\n\t}\n}\n\nfunc TestZoneParserAddressAAAA(t *testing.T) {\n\ttests := []struct {\n\t\trecord string\n\t\twant   *AAAA\n\t}{\n\t\t{\n\t\t\trecord: \"1.example.org. 600 IN AAAA ::1\",\n\t\t\twant:   &AAAA{Hdr: RR_Header{Name: \"1.example.org.\"}, AAAA: net.IPv6loopback},\n\t\t},\n\t\t{\n\t\t\trecord: \"2.example.org. 600 IN AAAA ::FFFF:127.0.0.1\",\n\t\t\twant:   &AAAA{Hdr: RR_Header{Name: \"2.example.org.\"}, AAAA: net.ParseIP(\"::FFFF:127.0.0.1\")},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tgot, err := NewRR(tc.record)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"expected no error, but got %s\", err)\n\t\t}\n\t\taaaa, ok := got.(*AAAA)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected *AAAA RR, but got %T\", got)\n\t\t}\n\t\tif !aaaa.AAAA.Equal(tc.want.AAAA) {\n\t\t\tt.Fatalf(\"expected AAAA with IP %v, but got %v\", tc.want.AAAA, aaaa.AAAA)\n\t\t}\n\t}\n}\n\nfunc TestZoneParserTargetBad(t *testing.T) {\n\trecords := []string{\n\t\t\"bad.example.org. CNAME ; bad cname\",\n\t\t\"bad.example.org. HTTPS 10 ; bad https\",\n\t\t\"bad.example.org. MX 10 ; bad mx\",\n\t\t\"bad.example.org. SRV 1 0 80 ; bad srv\",\n\t}\n\n\tfor _, record := range records {\n\t\tconst expect = \"bad \"\n\t\tif got, err := NewRR(record); err == nil || !strings.Contains(err.Error(), expect) {\n\t\t\tt.Errorf(\"NewRR(%v) = %v, want err to contain %q\", record, got, expect)\n\t\t}\n\t}\n}\n\nfunc TestZoneParserAddressBad(t *testing.T) {\n\trecords := []string{\n\t\t\"1.bad.example.org. 600 IN A ::1\",\n\t\t\"2.bad.example.org. 600 IN A ::FFFF:127.0.0.1\",\n\t\t\"3.bad.example.org. 600 IN AAAA 127.0.0.1\",\n\t}\n\n\tfor _, record := range records {\n\t\tconst expect = \"bad A\"\n\t\tif got, err := NewRR(record); err == nil || !strings.Contains(err.Error(), expect) {\n\t\t\tt.Errorf(\"NewRR(%v) = %v, want err to contain %q\", record, got, expect)\n\t\t}\n\t}\n}\n\nfunc TestParseTA(t *testing.T) {\n\trr, err := NewRR(` Ta 0 0 0`)\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error, but got %s\", err)\n\t}\n\tif rr == nil {\n\t\tt.Fatal(`expected a normal RR, but got nil`)\n\t}\n}\n\nvar errTestReadError = &Error{\"test error\"}\n\ntype errReader struct{}\n\nfunc (errReader) Read(p []byte) (int, error) { return 0, errTestReadError }\n\nfunc TestParseZoneReadError(t *testing.T) {\n\trr, err := ReadRR(errReader{}, \"\")\n\tif err == nil || !strings.Contains(err.Error(), errTestReadError.Error()) {\n\t\tt.Errorf(\"expected error to contain %q, but got %v\", errTestReadError, err)\n\t}\n\tif rr != nil {\n\t\tt.Errorf(\"expected a nil RR, but got %v\", rr)\n\t}\n}\n\nfunc TestUnexpectedNewline(t *testing.T) {\n\tzone := `\nexample.com. 60 PX\n1000 TXT 1K\n`\n\tzp := NewZoneParser(strings.NewReader(zone), \"example.com.\", \"\")\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t}\n\n\tconst expect = `dns: unexpected newline: \"\\n\" at line: 2:18`\n\tif err := zp.Err(); err == nil || err.Error() != expect {\n\t\tt.Errorf(\"expected error to contain %q, got %v\", expect, err)\n\t}\n\n\t// Test that newlines inside braces still work.\n\tzone = `\nexample.com. 60 PX (\n1000 TXT 1K )\n`\n\tzp = NewZoneParser(strings.NewReader(zone), \"example.com.\", \"\")\n\n\tvar count int\n\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t\tcount++\n\t}\n\n\tif count != 1 {\n\t\tt.Errorf(\"expected 1 record, got %d\", count)\n\t}\n\n\tif err := zp.Err(); err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n}\n\nfunc TestParseRFC3597InvalidLength(t *testing.T) {\n\t// We need to space separate the 00s otherwise it will exceed the maximum token size\n\t// of the zone lexer.\n\t_, err := NewRR(\"example. 3600 CLASS1 TYPE1 \\\\# 65536 \" + strings.Repeat(\"00 \", 65536))\n\tif err == nil {\n\t\tt.Error(\"should not have parsed excessively long RFC3579 record\")\n\t}\n}\n\nfunc TestParseKnownRRAsRFC3597(t *testing.T) {\n\tt.Run(\"with RDATA\", func(t *testing.T) {\n\t\t// This was found by oss-fuzz.\n\t\t_, err := NewRR(\"example. 3600 tYpe44 \\\\# 03 75  0100\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to parse RFC3579 format: %v\", err)\n\t\t}\n\n\t\trr, err := NewRR(\"example. 3600 CLASS1 TYPE1 \\\\# 4 7f000001\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse RFC3579 format: %v\", err)\n\t\t}\n\n\t\tif rr.Header().Rrtype != TypeA {\n\t\t\tt.Errorf(\"expected TypeA (1) Rrtype, but got %v\", rr.Header().Rrtype)\n\t\t}\n\n\t\ta, ok := rr.(*A)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected *A RR, but got %T\", rr)\n\t\t}\n\n\t\tlocalhost := net.IPv4(127, 0, 0, 1)\n\t\tif !a.A.Equal(localhost) {\n\t\t\tt.Errorf(\"expected A with IP %v, but got %v\", localhost, a.A)\n\t\t}\n\t})\n\tt.Run(\"without RDATA\", func(t *testing.T) {\n\t\trr, err := NewRR(\"example. 3600 CLASS1 TYPE1 \\\\# 0\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse RFC3579 format: %v\", err)\n\t\t}\n\n\t\tif rr.Header().Rrtype != TypeA {\n\t\t\tt.Errorf(\"expected TypeA (1) Rrtype, but got %v\", rr.Header().Rrtype)\n\t\t}\n\n\t\ta, ok := rr.(*A)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected *A RR, but got %T\", rr)\n\t\t}\n\n\t\tif len(a.A) != 0 {\n\t\t\tt.Errorf(\"expected A with empty IP, but got %v\", a.A)\n\t\t}\n\t})\n}\n\nfunc TestParseOpenEscape(t *testing.T) {\n\tif _, err := NewRR(\"example.net IN CNAME example.net.\"); err != nil {\n\t\tt.Fatalf(\"expected no error, but got: %s\", err)\n\t}\n\tif _, err := NewRR(\"example.net IN CNAME example.org\\\\\"); err == nil {\n\t\tt.Fatalf(\"expected an error, but got none\")\n\t}\n}\n\nfunc BenchmarkNewRR(b *testing.B) {\n\tconst name1 = \"12345678901234567890123456789012345.12345678.123.\"\n\tconst s = name1 + \" 3600 IN MX 10 \" + name1\n\n\tfor n := 0; n < b.N; n++ {\n\t\t_, err := NewRR(s)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkReadRR(b *testing.B) {\n\tconst name1 = \"12345678901234567890123456789012345.12345678.123.\"\n\tconst s = name1 + \" 3600 IN MX 10 \" + name1 + \"\\n\"\n\n\tfor n := 0; n < b.N; n++ {\n\t\tr := struct{ io.Reader }{strings.NewReader(s)}\n\t\t// r is now only an io.Reader and won't benefit from the\n\t\t// io.ByteReader special-case in zlexer.Next.\n\n\t\t_, err := ReadRR(r, \"\")\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nconst benchZone = `\nfoo. IN A 10.0.0.1 ; this is comment 1\nfoo. IN A (\n\t10.0.0.2 ; this is comment 2\n)\n; this is comment 3\nfoo. IN A 10.0.0.3\nfoo. IN A ( 10.0.0.4 ); this is comment 4\n\nfoo. IN A 10.0.0.5\n; this is comment 5\n\nfoo. IN A 10.0.0.6\n\nfoo. IN DNSKEY 256 3 5 AwEAAb+8l ; this is comment 6\nfoo. IN NSEC miek.nl. TXT RRSIG NSEC; this is comment 7\nfoo. IN TXT \"THIS IS TEXT MAN\"; this is comment 8\n`\n\nfunc BenchmarkZoneParser(b *testing.B) {\n\tfor n := 0; n < b.N; n++ {\n\t\tzp := NewZoneParser(strings.NewReader(benchZone), \"example.org.\", \"\")\n\n\t\tfor _, ok := zp.Next(); ok; _, ok = zp.Next() {\n\t\t}\n\n\t\tif err := zp.Err(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestEscapedStringOffset(t *testing.T) {\n\tvar cases = []struct {\n\t\tinput          string\n\t\tinputOffset    int\n\t\texpectedOffset int\n\t\texpectedOK     bool\n\t}{\n\t\t{\"simple string with no escape sequences\", 20, 20, true},\n\t\t{\"simple string with no escape sequences\", 500, -1, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 0, 0, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 1, 2, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 2, 6, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 3, 8, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 4, 10, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 5, 14, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 6, 16, true},\n\t\t{`\\;\\088\\\\\\;\\120\\\\`, 7, -1, true},\n\t\t{`\\`, 3, 0, false},\n\t\t{`a\\`, 3, 0, false},\n\t\t{`aa\\`, 3, 0, false},\n\t\t{`aaa\\`, 3, 3, true},\n\t\t{`aaaa\\`, 3, 3, true},\n\t}\n\tfor i, test := range cases {\n\t\toutputOffset, outputOK := escapedStringOffset(test.input, test.inputOffset)\n\t\tif outputOffset != test.expectedOffset {\n\t\t\tt.Errorf(\n\t\t\t\t\"Test %d (input %#q offset %d) returned offset %d but expected %d\",\n\t\t\t\ti, test.input, test.inputOffset, outputOffset, test.expectedOffset,\n\t\t\t)\n\t\t}\n\t\tif outputOK != test.expectedOK {\n\t\t\tt.Errorf(\n\t\t\t\t\"Test %d (input %#q offset %d) returned ok=%t but expected %t\",\n\t\t\t\ti, test.input, test.inputOffset, outputOK, test.expectedOK,\n\t\t\t)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "serve_mux.go",
    "content": "package dns\n\nimport (\n\t\"sync\"\n)\n\n// ServeMux is an DNS request multiplexer. It matches the zone name of\n// each incoming request against a list of registered patterns add calls\n// the handler for the pattern that most closely matches the zone name.\n//\n// ServeMux is DNSSEC aware, meaning that queries for the DS record are\n// redirected to the parent zone (if that is also registered), otherwise\n// the child gets the query.\n//\n// ServeMux is also safe for concurrent access from multiple goroutines.\n//\n// The zero ServeMux is empty and ready for use.\ntype ServeMux struct {\n\tz map[string]Handler\n\tm sync.RWMutex\n}\n\n// NewServeMux allocates and returns a new ServeMux.\nfunc NewServeMux() *ServeMux {\n\treturn new(ServeMux)\n}\n\n// DefaultServeMux is the default ServeMux used by Serve.\nvar DefaultServeMux = NewServeMux()\n\nfunc (mux *ServeMux) match(q string, t uint16) Handler {\n\tmux.m.RLock()\n\tdefer mux.m.RUnlock()\n\tif mux.z == nil {\n\t\treturn nil\n\t}\n\n\tq = CanonicalName(q)\n\n\tvar handler Handler\n\tfor off, end := 0, false; !end; off, end = NextLabel(q, off) {\n\t\tif h, ok := mux.z[q[off:]]; ok {\n\t\t\tif t != TypeDS {\n\t\t\t\treturn h\n\t\t\t}\n\t\t\t// Continue for DS to see if we have a parent too, if so delegate to the parent\n\t\t\thandler = h\n\t\t}\n\t}\n\n\t// Wildcard match, if we have found nothing try the root zone as a last resort.\n\tif h, ok := mux.z[\".\"]; ok {\n\t\treturn h\n\t}\n\n\treturn handler\n}\n\n// Handle adds a handler to the ServeMux for pattern.\nfunc (mux *ServeMux) Handle(pattern string, handler Handler) {\n\tif pattern == \"\" {\n\t\tpanic(\"dns: invalid pattern \" + pattern)\n\t}\n\tmux.m.Lock()\n\tif mux.z == nil {\n\t\tmux.z = make(map[string]Handler)\n\t}\n\tmux.z[CanonicalName(pattern)] = handler\n\tmux.m.Unlock()\n}\n\n// HandleFunc adds a handler function to the ServeMux for pattern.\nfunc (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {\n\tmux.Handle(pattern, HandlerFunc(handler))\n}\n\n// HandleRemove deregisters the handler specific for pattern from the ServeMux.\nfunc (mux *ServeMux) HandleRemove(pattern string) {\n\tif pattern == \"\" {\n\t\tpanic(\"dns: invalid pattern \" + pattern)\n\t}\n\tmux.m.Lock()\n\tdelete(mux.z, CanonicalName(pattern))\n\tmux.m.Unlock()\n}\n\n// ServeDNS dispatches the request to the handler whose pattern most\n// closely matches the request message.\n//\n// ServeDNS is DNSSEC aware, meaning that queries for the DS record\n// are redirected to the parent zone (if that is also registered),\n// otherwise the child gets the query.\n//\n// If no handler is found, or there is no question, a standard REFUSED\n// message is returned\nfunc (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {\n\tvar h Handler\n\tif len(req.Question) >= 1 { // allow more than one question\n\t\th = mux.match(req.Question[0].Name, req.Question[0].Qtype)\n\t}\n\n\tif h != nil {\n\t\th.ServeDNS(w, req)\n\t} else {\n\t\thandleRefused(w, req)\n\t}\n}\n\n// Handle registers the handler with the given pattern\n// in the DefaultServeMux. The documentation for\n// ServeMux explains how patterns are matched.\nfunc Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }\n\n// HandleRemove deregisters the handle with the given pattern\n// in the DefaultServeMux.\nfunc HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }\n\n// HandleFunc registers the handler function with the given pattern\n// in the DefaultServeMux.\nfunc HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {\n\tDefaultServeMux.HandleFunc(pattern, handler)\n}\n"
  },
  {
    "path": "serve_mux_test.go",
    "content": "package dns\n\nimport \"testing\"\n\nfunc TestDotAsCatchAllWildcard(t *testing.T) {\n\tmux := NewServeMux()\n\tmux.Handle(\".\", HandlerFunc(HelloServer))\n\tmux.Handle(\"example.com.\", HandlerFunc(AnotherHelloServer))\n\n\thandler := mux.match(\"www.miek.nl.\", TypeTXT)\n\tif handler == nil {\n\t\tt.Error(\"wildcard match failed\")\n\t}\n\n\thandler = mux.match(\"www.example.com.\", TypeTXT)\n\tif handler == nil {\n\t\tt.Error(\"example.com match failed\")\n\t}\n\n\thandler = mux.match(\"a.www.example.com.\", TypeTXT)\n\tif handler == nil {\n\t\tt.Error(\"a.www.example.com match failed\")\n\t}\n\n\thandler = mux.match(\"boe.\", TypeTXT)\n\tif handler == nil {\n\t\tt.Error(\"boe. match failed\")\n\t}\n}\n\nfunc TestCaseFolding(t *testing.T) {\n\tmux := NewServeMux()\n\tmux.Handle(\"_udp.example.com.\", HandlerFunc(HelloServer))\n\n\thandler := mux.match(\"_dns._udp.example.com.\", TypeSRV)\n\tif handler == nil {\n\t\tt.Error(\"case sensitive characters folded\")\n\t}\n\n\thandler = mux.match(\"_DNS._UDP.EXAMPLE.COM.\", TypeSRV)\n\tif handler == nil {\n\t\tt.Error(\"case insensitive characters not folded\")\n\t}\n}\n\nfunc TestRootServer(t *testing.T) {\n\tmux := NewServeMux()\n\tmux.Handle(\".\", HandlerFunc(HelloServer))\n\n\thandler := mux.match(\".\", TypeNS)\n\tif handler == nil {\n\t\tt.Error(\"root match failed\")\n\t}\n}\n\nfunc BenchmarkMuxMatch(b *testing.B) {\n\tmux := NewServeMux()\n\tmux.Handle(\"_udp.example.com.\", HandlerFunc(HelloServer))\n\n\tbench := func(q string) func(*testing.B) {\n\t\treturn func(b *testing.B) {\n\t\t\tfor n := 0; n < b.N; n++ {\n\t\t\t\thandler := mux.match(q, TypeSRV)\n\t\t\t\tif handler == nil {\n\t\t\t\t\tb.Fatal(\"couldn't find match\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tb.Run(\"lowercase\", bench(\"_dns._udp.example.com.\"))\n\tb.Run(\"uppercase\", bench(\"_DNS._UDP.EXAMPLE.COM.\"))\n}\n"
  },
  {
    "path": "server.go",
    "content": "// DNS server implementation.\n\npackage dns\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Default maximum number of TCP queries before we close the socket.\nconst maxTCPQueries = 128\n\n// aLongTimeAgo is a non-zero time, far in the past, used for\n// immediate cancellation of network operations.\nvar aLongTimeAgo = time.Unix(1, 0)\n\n// Handler is implemented by any value that implements ServeDNS.\ntype Handler interface {\n\tServeDNS(w ResponseWriter, r *Msg)\n}\n\n// The HandlerFunc type is an adapter to allow the use of\n// ordinary functions as DNS handlers.  If f is a function\n// with the appropriate signature, HandlerFunc(f) is a\n// Handler object that calls f.\ntype HandlerFunc func(ResponseWriter, *Msg)\n\n// ServeDNS calls f(w, r).\nfunc (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {\n\tf(w, r)\n}\n\n// A ResponseWriter interface is used by an DNS handler to\n// construct an DNS response.\ntype ResponseWriter interface {\n\t// LocalAddr returns the net.Addr of the server\n\tLocalAddr() net.Addr\n\t// RemoteAddr returns the net.Addr of the client that sent the current request.\n\tRemoteAddr() net.Addr\n\t// WriteMsg writes a reply back to the client.\n\tWriteMsg(*Msg) error\n\t// Write writes a raw buffer back to the client.\n\tWrite([]byte) (int, error)\n\t// Close closes the connection.\n\tClose() error\n\t// TsigStatus returns the status of the Tsig.\n\tTsigStatus() error\n\t// TsigTimersOnly sets the tsig timers only boolean.\n\tTsigTimersOnly(bool)\n\t// Hijack lets the caller take over the connection.\n\t// After a call to Hijack(), the DNS package will not do anything with the connection.\n\tHijack()\n}\n\n// A ConnectionStater interface is used by a DNS Handler to access TLS connection state\n// when available.\ntype ConnectionStater interface {\n\tConnectionState() *tls.ConnectionState\n}\n\ntype response struct {\n\tclosed         bool // connection has been closed\n\thijacked       bool // connection has been hijacked by handler\n\ttsigTimersOnly bool\n\ttsigStatus     error\n\ttsigRequestMAC string\n\ttsigProvider   TsigProvider\n\tudp            net.PacketConn // i/o connection if UDP was used\n\ttcp            net.Conn       // i/o connection if TCP was used\n\tudpSession     *SessionUDP    // oob data to get egress interface right\n\tpcSession      net.Addr       // address to use when writing to a generic net.PacketConn\n\twriter         Writer         // writer to output the raw DNS bits\n}\n\n// handleRefused returns a HandlerFunc that returns REFUSED for every request it gets.\nfunc handleRefused(w ResponseWriter, r *Msg) {\n\tm := new(Msg)\n\tm.SetRcode(r, RcodeRefused)\n\tw.WriteMsg(m)\n}\n\n// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.\n// Deprecated: This function is going away.\nfunc HandleFailed(w ResponseWriter, r *Msg) {\n\tm := new(Msg)\n\tm.SetRcode(r, RcodeServerFailure)\n\t// does not matter if this write fails\n\tw.WriteMsg(m)\n}\n\n// ListenAndServe Starts a server on address and network specified Invoke handler\n// for incoming queries.\nfunc ListenAndServe(addr string, network string, handler Handler) error {\n\tserver := &Server{Addr: addr, Net: network, Handler: handler}\n\treturn server.ListenAndServe()\n}\n\n// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in\n// http://golang.org/pkg/net/http/#ListenAndServeTLS\nfunc ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {\n\tcert, err := tls.LoadX509KeyPair(certFile, keyFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\tserver := &Server{\n\t\tAddr:      addr,\n\t\tNet:       \"tcp-tls\",\n\t\tTLSConfig: &config,\n\t\tHandler:   handler,\n\t}\n\n\treturn server.ListenAndServe()\n}\n\n// ActivateAndServe activates a server with a listener from systemd,\n// l and p should not both be non-nil.\n// If both l and p are not nil only p will be used.\n// Invoke handler for incoming queries.\nfunc ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {\n\tserver := &Server{Listener: l, PacketConn: p, Handler: handler}\n\treturn server.ActivateAndServe()\n}\n\n// Writer writes raw DNS messages; each call to Write should send an entire message.\ntype Writer interface {\n\tio.Writer\n}\n\n// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message.\ntype Reader interface {\n\t// ReadTCP reads a raw message from a TCP connection. Implementations may alter\n\t// connection properties, for example the read-deadline.\n\tReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error)\n\t// ReadUDP reads a raw message from a UDP connection. Implementations may alter\n\t// connection properties, for example the read-deadline.\n\tReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)\n}\n\n// PacketConnReader is an optional interface that Readers can implement to support using generic net.PacketConns.\ntype PacketConnReader interface {\n\tReader\n\n\t// ReadPacketConn reads a raw message from a generic net.PacketConn UDP connection. Implementations may\n\t// alter connection properties, for example the read-deadline.\n\tReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error)\n}\n\n// defaultReader is an adapter for the Server struct that implements the Reader and\n// PacketConnReader interfaces using the readTCP, readUDP and readPacketConn funcs\n// of the embedded Server.\ntype defaultReader struct {\n\t*Server\n}\n\nvar _ PacketConnReader = defaultReader{}\n\nfunc (dr defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {\n\treturn dr.readTCP(conn, timeout)\n}\n\nfunc (dr defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {\n\treturn dr.readUDP(conn, timeout)\n}\n\nfunc (dr defaultReader) ReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) {\n\treturn dr.readPacketConn(conn, timeout)\n}\n\n// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.\n// Implementations should never return a nil Reader.\n// Readers should also implement the optional PacketConnReader interface.\n// PacketConnReader is required to use a generic net.PacketConn.\ntype DecorateReader func(Reader) Reader\n\n// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.\n// Implementations should never return a nil Writer.\ntype DecorateWriter func(Writer) Writer\n\n// MsgInvalidFunc is a listener hook for observing incoming messages that were discarded\n// because they could not be parsed.\n// Every message that is read by a Reader will eventually be provided to the Handler,\n// rejected (or ignored) by the MsgAcceptFunc, or passed to this function.\ntype MsgInvalidFunc func(m []byte, err error)\n\nvar DefaultMsgInvalidFunc MsgInvalidFunc = defaultMsgInvalidFunc\n\nfunc defaultMsgInvalidFunc(m []byte, err error) {}\n\n// A Server defines parameters for running an DNS server.\ntype Server struct {\n\t// Address to listen on, \":dns\" if empty.\n\tAddr string\n\t// if \"tcp\" or \"tcp-tls\" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one\n\tNet string\n\t// TCP Listener to use, this is to aid in systemd's socket activation.\n\tListener net.Listener\n\t// TLS connection configuration\n\tTLSConfig *tls.Config\n\t// UDP \"Listener\" to use, this is to aid in systemd's socket activation.\n\tPacketConn net.PacketConn\n\t// Handler to invoke, dns.DefaultServeMux if nil.\n\tHandler Handler\n\t// Default buffer size to use to read incoming UDP messages. If not set\n\t// it defaults to MinMsgSize (512 B).\n\tUDPSize int\n\t// The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second.\n\tReadTimeout time.Duration\n\t// The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second.\n\tWriteTimeout time.Duration\n\t// TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).\n\tIdleTimeout func() time.Duration\n\t// An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.\n\tTsigProvider TsigProvider\n\t// Secret(s) for Tsig map[<zonename>]<base64 secret>. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2).\n\tTsigSecret map[string]string\n\t// If NotifyStartedFunc is set it is called once the server has started listening.\n\tNotifyStartedFunc func()\n\t// DecorateReader is optional, allows customization of the process that reads raw DNS messages.\n\t// The decorated reader must not mutate the data read from the conn.\n\tDecorateReader DecorateReader\n\t// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.\n\tDecorateWriter DecorateWriter\n\t// Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1).\n\tMaxTCPQueries int\n\t// Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address.\n\t// It is only supported on certain GOOSes and when using ListenAndServe.\n\tReusePort bool\n\t// Whether to set the SO_REUSEADDR socket option, allowing multiple listeners to be bound to a single address.\n\t// Crucially this allows binding when an existing server is listening on `0.0.0.0` or `::`.\n\t// It is only supported on certain GOOSes and when using ListenAndServe.\n\tReuseAddr bool\n\t// AcceptMsgFunc will check the incoming message and will reject it early in the process.\n\t// By default DefaultMsgAcceptFunc will be used.\n\tMsgAcceptFunc MsgAcceptFunc\n\t// MsgInvalidFunc is optional, will be called if a message is received but cannot be parsed.\n\tMsgInvalidFunc MsgInvalidFunc\n\n\t// Shutdown handling\n\tlock     sync.RWMutex\n\tstarted  bool\n\tshutdown chan struct{}\n\tconns    map[net.Conn]struct{}\n\n\t// A pool for UDP message buffers.\n\tudpPool sync.Pool\n}\n\nfunc (srv *Server) tsigProvider() TsigProvider {\n\tif srv.TsigProvider != nil {\n\t\treturn srv.TsigProvider\n\t}\n\tif srv.TsigSecret != nil {\n\t\treturn tsigSecretProvider(srv.TsigSecret)\n\t}\n\treturn nil\n}\n\nfunc (srv *Server) isStarted() bool {\n\tsrv.lock.RLock()\n\tstarted := srv.started\n\tsrv.lock.RUnlock()\n\treturn started\n}\n\nfunc makeUDPBuffer(size int) func() interface{} {\n\treturn func() interface{} {\n\t\treturn make([]byte, size)\n\t}\n}\n\nfunc (srv *Server) init() {\n\tsrv.shutdown = make(chan struct{})\n\tsrv.conns = make(map[net.Conn]struct{})\n\n\tif srv.UDPSize == 0 {\n\t\tsrv.UDPSize = MinMsgSize\n\t}\n\tif srv.MsgAcceptFunc == nil {\n\t\tsrv.MsgAcceptFunc = DefaultMsgAcceptFunc\n\t}\n\tif srv.MsgInvalidFunc == nil {\n\t\tsrv.MsgInvalidFunc = DefaultMsgInvalidFunc\n\t}\n\tif srv.Handler == nil {\n\t\tsrv.Handler = DefaultServeMux\n\t}\n\n\tsrv.udpPool.New = makeUDPBuffer(srv.UDPSize)\n}\n\nfunc unlockOnce(l sync.Locker) func() {\n\tvar once sync.Once\n\treturn func() { once.Do(l.Unlock) }\n}\n\n// ListenAndServe starts a nameserver on the configured address in *Server.\nfunc (srv *Server) ListenAndServe() error {\n\tunlock := unlockOnce(&srv.lock)\n\tsrv.lock.Lock()\n\tdefer unlock()\n\n\tif srv.started {\n\t\treturn &Error{err: \"server already started\"}\n\t}\n\n\taddr := srv.Addr\n\tif addr == \"\" {\n\t\taddr = \":domain\"\n\t}\n\n\tsrv.init()\n\n\tswitch srv.Net {\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\tl, err := listenTCP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsrv.Listener = l\n\t\tsrv.started = true\n\t\tunlock()\n\t\treturn srv.serveTCP(l)\n\tcase \"tcp-tls\", \"tcp4-tls\", \"tcp6-tls\":\n\t\tif srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) {\n\t\t\treturn errors.New(\"neither Certificates nor GetCertificate set in config\")\n\t\t}\n\t\tnetwork := strings.TrimSuffix(srv.Net, \"-tls\")\n\t\tl, err := listenTCP(network, addr, srv.ReusePort, srv.ReuseAddr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tl = tls.NewListener(l, srv.TLSConfig)\n\t\tsrv.Listener = l\n\t\tsrv.started = true\n\t\tunlock()\n\t\treturn srv.serveTCP(l)\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\tl, err := listenUDP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tu := l.(*net.UDPConn)\n\t\tif e := setUDPSocketOptions(u); e != nil {\n\t\t\tu.Close()\n\t\t\treturn e\n\t\t}\n\t\tsrv.PacketConn = l\n\t\tsrv.started = true\n\t\tunlock()\n\t\treturn srv.serveUDP(u)\n\t}\n\treturn &Error{err: \"bad network\"}\n}\n\n// ActivateAndServe starts a nameserver with the PacketConn or Listener\n// configured in *Server. Its main use is to start a server from systemd.\nfunc (srv *Server) ActivateAndServe() error {\n\tunlock := unlockOnce(&srv.lock)\n\tsrv.lock.Lock()\n\tdefer unlock()\n\n\tif srv.started {\n\t\treturn &Error{err: \"server already started\"}\n\t}\n\n\tsrv.init()\n\n\tif srv.PacketConn != nil {\n\t\t// Check PacketConn interface's type is valid and value\n\t\t// is not nil\n\t\tif t, ok := srv.PacketConn.(*net.UDPConn); ok && t != nil {\n\t\t\tif e := setUDPSocketOptions(t); e != nil {\n\t\t\t\treturn e\n\t\t\t}\n\t\t}\n\t\tsrv.started = true\n\t\tunlock()\n\t\treturn srv.serveUDP(srv.PacketConn)\n\t}\n\tif srv.Listener != nil {\n\t\tsrv.started = true\n\t\tunlock()\n\t\treturn srv.serveTCP(srv.Listener)\n\t}\n\treturn &Error{err: \"bad listeners\"}\n}\n\n// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and\n// ActivateAndServe will return.\nfunc (srv *Server) Shutdown() error {\n\treturn srv.ShutdownContext(context.Background())\n}\n\n// ShutdownContext shuts down a server. After a call to ShutdownContext,\n// ListenAndServe and ActivateAndServe will return.\n//\n// A context.Context may be passed to limit how long to wait for connections\n// to terminate.\nfunc (srv *Server) ShutdownContext(ctx context.Context) error {\n\tsrv.lock.Lock()\n\tif !srv.started {\n\t\tsrv.lock.Unlock()\n\t\treturn &Error{err: \"server not started\"}\n\t}\n\n\tsrv.started = false\n\n\tif srv.PacketConn != nil {\n\t\tsrv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads\n\t}\n\n\tif srv.Listener != nil {\n\t\tsrv.Listener.Close()\n\t}\n\n\tfor rw := range srv.conns {\n\t\trw.SetReadDeadline(aLongTimeAgo) // Unblock reads\n\t}\n\n\tsrv.lock.Unlock()\n\n\tif testShutdownNotify != nil {\n\t\ttestShutdownNotify.Broadcast()\n\t}\n\n\tvar ctxErr error\n\tselect {\n\tcase <-srv.shutdown:\n\tcase <-ctx.Done():\n\t\tctxErr = ctx.Err()\n\t}\n\n\tif srv.PacketConn != nil {\n\t\tsrv.PacketConn.Close()\n\t}\n\n\treturn ctxErr\n}\n\nvar testShutdownNotify *sync.Cond\n\n// getReadTimeout is a helper func to use system timeout if server did not intend to change it.\nfunc (srv *Server) getReadTimeout() time.Duration {\n\tif srv.ReadTimeout != 0 {\n\t\treturn srv.ReadTimeout\n\t}\n\treturn dnsTimeout\n}\n\n// serveTCP starts a TCP listener for the server.\nfunc (srv *Server) serveTCP(l net.Listener) error {\n\tdefer l.Close()\n\n\tif srv.NotifyStartedFunc != nil {\n\t\tsrv.NotifyStartedFunc()\n\t}\n\n\tvar wg sync.WaitGroup\n\tdefer func() {\n\t\twg.Wait()\n\t\tclose(srv.shutdown)\n\t}()\n\n\tfor srv.isStarted() {\n\t\trw, err := l.Accept()\n\t\tif err != nil {\n\t\t\tif !srv.isStarted() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif neterr, ok := err.(net.Error); ok && neterr.Temporary() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tsrv.lock.Lock()\n\t\t// Track the connection to allow unblocking reads on shutdown.\n\t\tsrv.conns[rw] = struct{}{}\n\t\tsrv.lock.Unlock()\n\t\twg.Add(1)\n\t\tgo srv.serveTCPConn(&wg, rw)\n\t}\n\n\treturn nil\n}\n\n// serveUDP starts a UDP listener for the server.\nfunc (srv *Server) serveUDP(l net.PacketConn) error {\n\tdefer l.Close()\n\n\treader := Reader(defaultReader{srv})\n\tif srv.DecorateReader != nil {\n\t\treader = srv.DecorateReader(reader)\n\t}\n\n\tlUDP, isUDP := l.(*net.UDPConn)\n\treaderPC, canPacketConn := reader.(PacketConnReader)\n\tif !isUDP && !canPacketConn {\n\t\treturn &Error{err: \"PacketConnReader was not implemented on Reader returned from DecorateReader but is required for net.PacketConn\"}\n\t}\n\n\tif srv.NotifyStartedFunc != nil {\n\t\tsrv.NotifyStartedFunc()\n\t}\n\n\tvar wg sync.WaitGroup\n\tdefer func() {\n\t\twg.Wait()\n\t\tclose(srv.shutdown)\n\t}()\n\n\trtimeout := srv.getReadTimeout()\n\t// deadline is not used here\n\tfor srv.isStarted() {\n\t\tvar (\n\t\t\tm    []byte\n\t\t\tsPC  net.Addr\n\t\t\tsUDP *SessionUDP\n\t\t\terr  error\n\t\t)\n\t\tif isUDP {\n\t\t\tm, sUDP, err = reader.ReadUDP(lUDP, rtimeout)\n\t\t} else {\n\t\t\tm, sPC, err = readerPC.ReadPacketConn(l, rtimeout)\n\t\t}\n\t\tif err != nil {\n\t\t\tif !srv.isStarted() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif netErr, ok := err.(net.Error); ok && netErr.Temporary() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif len(m) < headerSize {\n\t\t\tif cap(m) == srv.UDPSize {\n\t\t\t\tsrv.udpPool.Put(m[:srv.UDPSize])\n\t\t\t}\n\t\t\tsrv.MsgInvalidFunc(m, ErrShortRead)\n\t\t\tcontinue\n\t\t}\n\t\twg.Add(1)\n\t\tgo srv.serveUDPPacket(&wg, m, l, sUDP, sPC)\n\t}\n\n\treturn nil\n}\n\n// Serve a new TCP connection.\nfunc (srv *Server) serveTCPConn(wg *sync.WaitGroup, rw net.Conn) {\n\tw := &response{tsigProvider: srv.tsigProvider(), tcp: rw}\n\tif srv.DecorateWriter != nil {\n\t\tw.writer = srv.DecorateWriter(w)\n\t} else {\n\t\tw.writer = w\n\t}\n\n\treader := Reader(defaultReader{srv})\n\tif srv.DecorateReader != nil {\n\t\treader = srv.DecorateReader(reader)\n\t}\n\n\tidleTimeout := tcpIdleTimeout\n\tif srv.IdleTimeout != nil {\n\t\tidleTimeout = srv.IdleTimeout()\n\t}\n\n\ttimeout := srv.getReadTimeout()\n\n\tlimit := srv.MaxTCPQueries\n\tif limit == 0 {\n\t\tlimit = maxTCPQueries\n\t}\n\n\tfor q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ {\n\t\tm, err := reader.ReadTCP(w.tcp, timeout)\n\t\tif err != nil {\n\t\t\t// TODO(tmthrgd): handle error\n\t\t\tbreak\n\t\t}\n\t\tsrv.serveDNS(m, w)\n\t\tif w.closed {\n\t\t\tbreak // Close() was called\n\t\t}\n\t\tif w.hijacked {\n\t\t\tbreak // client will call Close() themselves\n\t\t}\n\t\t// The first read uses the read timeout, the rest use the\n\t\t// idle timeout.\n\t\ttimeout = idleTimeout\n\t}\n\n\tif !w.hijacked {\n\t\tw.Close()\n\t}\n\n\tsrv.lock.Lock()\n\tdelete(srv.conns, w.tcp)\n\tsrv.lock.Unlock()\n\n\twg.Done()\n}\n\n// Serve a new UDP request.\nfunc (srv *Server) serveUDPPacket(wg *sync.WaitGroup, m []byte, u net.PacketConn, udpSession *SessionUDP, pcSession net.Addr) {\n\tw := &response{tsigProvider: srv.tsigProvider(), udp: u, udpSession: udpSession, pcSession: pcSession}\n\tif srv.DecorateWriter != nil {\n\t\tw.writer = srv.DecorateWriter(w)\n\t} else {\n\t\tw.writer = w\n\t}\n\n\tsrv.serveDNS(m, w)\n\twg.Done()\n}\n\nfunc (srv *Server) serveDNS(m []byte, w *response) {\n\tdh, off, err := unpackMsgHdr(m, 0)\n\tif err != nil {\n\t\tsrv.MsgInvalidFunc(m, err)\n\t\t// Let client hang, they are sending crap; any reply can be used to amplify.\n\t\treturn\n\t}\n\n\treq := new(Msg)\n\treq.setHdr(dh)\n\n\tswitch action := srv.MsgAcceptFunc(dh); action {\n\tcase MsgAccept:\n\t\terr := req.unpack(dh, m, off)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\n\t\tsrv.MsgInvalidFunc(m, err)\n\t\tfallthrough\n\tcase MsgReject, MsgRejectNotImplemented:\n\t\topcode := req.Opcode\n\t\treq.SetRcodeFormatError(req)\n\t\treq.Zero = false\n\t\tif action == MsgRejectNotImplemented {\n\t\t\treq.Opcode = opcode\n\t\t\treq.Rcode = RcodeNotImplemented\n\t\t}\n\n\t\t// Are we allowed to delete any OPT records here?\n\t\treq.Ns, req.Answer, req.Extra = nil, nil, nil\n\n\t\tw.WriteMsg(req)\n\t\tfallthrough\n\tcase MsgIgnore:\n\t\tif w.udp != nil && cap(m) == srv.UDPSize {\n\t\t\tsrv.udpPool.Put(m[:srv.UDPSize])\n\t\t}\n\n\t\treturn\n\t}\n\n\tw.tsigStatus = nil\n\tif w.tsigProvider != nil {\n\t\tif t := req.IsTsig(); t != nil {\n\t\t\tw.tsigStatus = TsigVerifyWithProvider(m, w.tsigProvider, \"\", false)\n\t\t\tw.tsigTimersOnly = false\n\t\t\tw.tsigRequestMAC = t.MAC\n\t\t}\n\t}\n\n\tif w.udp != nil && cap(m) == srv.UDPSize {\n\t\tsrv.udpPool.Put(m[:srv.UDPSize])\n\t}\n\n\tsrv.Handler.ServeDNS(w, req) // Writes back to the client\n}\n\nfunc (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {\n\t// If we race with ShutdownContext, the read deadline may\n\t// have been set in the distant past to unblock the read\n\t// below. We must not override it, otherwise we may block\n\t// ShutdownContext.\n\tsrv.lock.RLock()\n\tif srv.started {\n\t\tconn.SetReadDeadline(time.Now().Add(timeout))\n\t}\n\tsrv.lock.RUnlock()\n\n\tvar length uint16\n\tif err := binary.Read(conn, binary.BigEndian, &length); err != nil {\n\t\treturn nil, err\n\t}\n\n\tm := make([]byte, length)\n\tif _, err := io.ReadFull(conn, m); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn m, nil\n}\n\nfunc (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {\n\tsrv.lock.RLock()\n\tif srv.started {\n\t\t// See the comment in readTCP above.\n\t\tconn.SetReadDeadline(time.Now().Add(timeout))\n\t}\n\tsrv.lock.RUnlock()\n\n\tm := srv.udpPool.Get().([]byte)\n\tn, s, err := ReadFromSessionUDP(conn, m)\n\tif err != nil {\n\t\tsrv.udpPool.Put(m)\n\t\treturn nil, nil, err\n\t}\n\tm = m[:n]\n\treturn m, s, nil\n}\n\nfunc (srv *Server) readPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) {\n\tsrv.lock.RLock()\n\tif srv.started {\n\t\t// See the comment in readTCP above.\n\t\tconn.SetReadDeadline(time.Now().Add(timeout))\n\t}\n\tsrv.lock.RUnlock()\n\n\tm := srv.udpPool.Get().([]byte)\n\tn, addr, err := conn.ReadFrom(m)\n\tif err != nil {\n\t\tsrv.udpPool.Put(m)\n\t\treturn nil, nil, err\n\t}\n\tm = m[:n]\n\treturn m, addr, nil\n}\n\n// WriteMsg implements the ResponseWriter.WriteMsg method.\nfunc (w *response) WriteMsg(m *Msg) (err error) {\n\tif w.closed {\n\t\treturn &Error{err: \"WriteMsg called after Close\"}\n\t}\n\n\tvar data []byte\n\tif w.tsigProvider != nil { // if no provider, dont check for the tsig (which is a longer check)\n\t\tif t := m.IsTsig(); t != nil {\n\t\t\tdata, w.tsigRequestMAC, err = TsigGenerateWithProvider(m, w.tsigProvider, w.tsigRequestMAC, w.tsigTimersOnly)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, err = w.writer.Write(data)\n\t\t\treturn err\n\t\t}\n\t}\n\tdata, err = m.Pack()\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = w.writer.Write(data)\n\treturn err\n}\n\n// Write implements the ResponseWriter.Write method.\nfunc (w *response) Write(m []byte) (int, error) {\n\tif w.closed {\n\t\treturn 0, &Error{err: \"Write called after Close\"}\n\t}\n\n\tswitch {\n\tcase w.udp != nil:\n\t\tif u, ok := w.udp.(*net.UDPConn); ok {\n\t\t\treturn WriteToSessionUDP(u, m, w.udpSession)\n\t\t}\n\t\treturn w.udp.WriteTo(m, w.pcSession)\n\tcase w.tcp != nil:\n\t\tif len(m) > MaxMsgSize {\n\t\t\treturn 0, &Error{err: \"message too large\"}\n\t\t}\n\n\t\tmsg := make([]byte, 2+len(m))\n\t\tbinary.BigEndian.PutUint16(msg, uint16(len(m)))\n\t\tcopy(msg[2:], m)\n\t\treturn w.tcp.Write(msg)\n\tdefault:\n\t\tpanic(\"dns: internal error: udp and tcp both nil\")\n\t}\n}\n\n// LocalAddr implements the ResponseWriter.LocalAddr method.\nfunc (w *response) LocalAddr() net.Addr {\n\tswitch {\n\tcase w.udp != nil:\n\t\treturn w.udp.LocalAddr()\n\tcase w.tcp != nil:\n\t\treturn w.tcp.LocalAddr()\n\tdefault:\n\t\tpanic(\"dns: internal error: udp and tcp both nil\")\n\t}\n}\n\n// RemoteAddr implements the ResponseWriter.RemoteAddr method.\nfunc (w *response) RemoteAddr() net.Addr {\n\tswitch {\n\tcase w.udpSession != nil:\n\t\treturn w.udpSession.RemoteAddr()\n\tcase w.pcSession != nil:\n\t\treturn w.pcSession\n\tcase w.tcp != nil:\n\t\treturn w.tcp.RemoteAddr()\n\tdefault:\n\t\tpanic(\"dns: internal error: udpSession, pcSession and tcp are all nil\")\n\t}\n}\n\n// TsigStatus implements the ResponseWriter.TsigStatus method.\nfunc (w *response) TsigStatus() error { return w.tsigStatus }\n\n// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.\nfunc (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }\n\n// Hijack implements the ResponseWriter.Hijack method.\nfunc (w *response) Hijack() { w.hijacked = true }\n\n// Close implements the ResponseWriter.Close method\nfunc (w *response) Close() error {\n\tif w.closed {\n\t\treturn &Error{err: \"connection already closed\"}\n\t}\n\tw.closed = true\n\n\tswitch {\n\tcase w.udp != nil:\n\t\t// Can't close the udp conn, as that is actually the listener.\n\t\treturn nil\n\tcase w.tcp != nil:\n\t\treturn w.tcp.Close()\n\tdefault:\n\t\tpanic(\"dns: internal error: udp and tcp both nil\")\n\t}\n}\n\n// ConnectionState() implements the ConnectionStater.ConnectionState() interface.\nfunc (w *response) ConnectionState() *tls.ConnectionState {\n\ttype tlsConnectionStater interface {\n\t\tConnectionState() tls.ConnectionState\n\t}\n\tif v, ok := w.tcp.(tlsConnectionStater); ok {\n\t\tt := v.ConnectionState()\n\t\treturn &t\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server_test.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/sync/errgroup\"\n)\n\nfunc HelloServer(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\n\tm.Extra = make([]RR, 1)\n\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tw.WriteMsg(m)\n}\n\nfunc HelloServerBadID(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\tm.Id++\n\n\tm.Extra = make([]RR, 1)\n\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tw.WriteMsg(m)\n}\n\nfunc HelloServerBadThenGoodID(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\tm.Id++\n\n\tm.Extra = make([]RR, 1)\n\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tw.WriteMsg(m)\n\n\tm.Id--\n\tw.WriteMsg(m)\n}\n\nfunc HelloServerEchoAddrPort(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\n\tremoteAddr := w.RemoteAddr().String()\n\tm.Extra = make([]RR, 1)\n\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{remoteAddr}}\n\tw.WriteMsg(m)\n}\n\nfunc AnotherHelloServer(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\n\tm.Extra = make([]RR, 1)\n\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello example\"}}\n\tw.WriteMsg(m)\n}\n\nfunc RunLocalServer(pc net.PacketConn, l net.Listener, opts ...func(*Server)) (*Server, string, chan error, error) {\n\tserver := &Server{\n\t\tPacketConn: pc,\n\t\tListener:   l,\n\n\t\tReadTimeout:  time.Hour,\n\t\tWriteTimeout: time.Hour,\n\t}\n\n\twaitLock := sync.Mutex{}\n\twaitLock.Lock()\n\tserver.NotifyStartedFunc = waitLock.Unlock\n\n\tfor _, opt := range opts {\n\t\topt(server)\n\t}\n\n\tvar (\n\t\taddr   string\n\t\tcloser io.Closer\n\t)\n\tif l != nil {\n\t\taddr = l.Addr().String()\n\t\tcloser = l\n\t} else {\n\t\taddr = pc.LocalAddr().String()\n\t\tcloser = pc\n\t}\n\n\t// fin must be buffered so the goroutine below won't block\n\t// forever if fin is never read from. This always happens\n\t// if the channel is discarded and can happen in TestShutdownUDP.\n\tfin := make(chan error, 1)\n\n\tgo func() {\n\t\tfin <- server.ActivateAndServe()\n\t\tcloser.Close()\n\t}()\n\n\twaitLock.Lock()\n\treturn server, addr, fin, nil\n}\n\nfunc RunLocalUDPServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) {\n\tpc, err := net.ListenPacket(\"udp\", laddr)\n\tif err != nil {\n\t\treturn nil, \"\", nil, err\n\t}\n\n\treturn RunLocalServer(pc, nil, opts...)\n}\n\nfunc RunLocalPacketConnServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) {\n\treturn RunLocalUDPServer(laddr, append(opts, func(srv *Server) {\n\t\t// Make srv.PacketConn opaque to trigger the generic code paths.\n\t\tsrv.PacketConn = struct{ net.PacketConn }{srv.PacketConn}\n\t})...)\n}\n\nfunc RunLocalTCPServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) {\n\tl, err := net.Listen(\"tcp\", laddr)\n\tif err != nil {\n\t\treturn nil, \"\", nil, err\n\t}\n\n\treturn RunLocalServer(nil, l, opts...)\n}\n\nfunc RunLocalTLSServer(laddr string, config *tls.Config) (*Server, string, chan error, error) {\n\treturn RunLocalTCPServer(laddr, func(srv *Server) {\n\t\tsrv.Listener = tls.NewListener(srv.Listener, config)\n\t})\n}\n\nfunc RunLocalUnixServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) {\n\tl, err := net.Listen(\"unix\", laddr)\n\tif err != nil {\n\t\treturn nil, \"\", nil, err\n\t}\n\n\treturn RunLocalServer(nil, l, opts...)\n}\n\nfunc RunLocalUnixGramServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) {\n\tpc, err := net.ListenPacket(\"unixgram\", laddr)\n\tif err != nil {\n\t\treturn nil, \"\", nil, err\n\t}\n\n\treturn RunLocalServer(pc, nil, opts...)\n}\n\nfunc RunLocalUnixSeqPacketServer(laddr string) (chan interface{}, string, error) {\n\tpc, err := net.Listen(\"unixpacket\", laddr)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tshutdownChan := make(chan interface{})\n\tgo func() {\n\t\tpc.Accept()\n\t\t<-shutdownChan\n\t}()\n\n\treturn shutdownChan, pc.Addr().String(), nil\n}\n\nfunc TestServing(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tname      string\n\t\tnetwork   string\n\t\trunServer func(laddr string, opts ...func(*Server)) (*Server, string, chan error, error)\n\t}{\n\t\t{\"udp\", \"udp\", RunLocalUDPServer},\n\t\t{\"tcp\", \"tcp\", RunLocalTCPServer},\n\t\t{\"PacketConn\", \"udp\", RunLocalPacketConnServer},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tHandleFunc(\"miek.nl.\", HelloServer)\n\t\t\tHandleFunc(\"example.com.\", AnotherHelloServer)\n\t\t\tdefer HandleRemove(\"miek.nl.\")\n\t\t\tdefer HandleRemove(\"example.com.\")\n\n\t\t\ts, addrstr, _, err := tc.runServer(\":0\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t\t\t}\n\t\t\tdefer s.Shutdown()\n\n\t\t\tc := &Client{\n\t\t\t\tNet: tc.network,\n\t\t\t}\n\t\t\tm := new(Msg)\n\t\t\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\t\t\tr, _, err := c.Exchange(m, addrstr)\n\t\t\tif err != nil || len(r.Extra) == 0 {\n\t\t\t\tt.Fatal(\"failed to exchange miek.nl\", err)\n\t\t\t}\n\t\t\ttxt := r.Extra[0].(*TXT).Txt[0]\n\t\t\tif txt != \"Hello world\" {\n\t\t\t\tt.Error(\"unexpected result for miek.nl\", txt, \"!= Hello world\")\n\t\t\t}\n\n\t\t\tm.SetQuestion(\"example.com.\", TypeTXT)\n\t\t\tr, _, err = c.Exchange(m, addrstr)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"failed to exchange example.com\", err)\n\t\t\t}\n\t\t\ttxt = r.Extra[0].(*TXT).Txt[0]\n\t\t\tif txt != \"Hello example\" {\n\t\t\t\tt.Error(\"unexpected result for example.com\", txt, \"!= Hello example\")\n\t\t\t}\n\n\t\t\t// Test Mixes cased as noticed by Ask.\n\t\t\tm.SetQuestion(\"eXaMplE.cOm.\", TypeTXT)\n\t\t\tr, _, err = c.Exchange(m, addrstr)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(\"failed to exchange eXaMplE.cOm\", err)\n\t\t\t}\n\t\t\ttxt = r.Extra[0].(*TXT).Txt[0]\n\t\t\tif txt != \"Hello example\" {\n\t\t\t\tt.Error(\"unexpected result for example.com\", txt, \"!= Hello example\")\n\t\t\t}\n\t\t})\n\t}\n}\n\n// Verify that the server responds to a query with Z flag on, ignoring the flag, and does not echoes it back\nfunc TestServeIgnoresZFlag(t *testing.T) {\n\tHandleFunc(\"example.com.\", AnotherHelloServer)\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tm := new(Msg)\n\n\t// Test the Z flag is not echoed\n\tm.SetQuestion(\"example.com.\", TypeTXT)\n\tm.Zero = true\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to exchange example.com with +zflag\", err)\n\t}\n\tif r.Zero {\n\t\tt.Error(\"the response should not have Z flag set - even for a query which does\")\n\t}\n\tif r.Rcode != RcodeSuccess {\n\t\tt.Errorf(\"expected rcode %v, got %v\", RcodeSuccess, r.Rcode)\n\t}\n}\n\n// Verify that the server responds to a query with unsupported Opcode with a NotImplemented error and that Opcode is unchanged.\nfunc TestServeNotImplemented(t *testing.T) {\n\tHandleFunc(\"example.com.\", AnotherHelloServer)\n\topcode := 15\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tm := new(Msg)\n\n\t// Test that Opcode is like the unchanged from request Opcode and that Rcode is set to NotImplemented\n\tm.SetQuestion(\"example.com.\", TypeTXT)\n\tm.Opcode = opcode\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to exchange example.com with +zflag\", err)\n\t}\n\tif r.Opcode != opcode {\n\t\tt.Errorf(\"expected opcode %v, got %v\", opcode, r.Opcode)\n\t}\n\tif r.Rcode != RcodeNotImplemented {\n\t\tt.Errorf(\"expected rcode %v, got %v\", RcodeNotImplemented, r.Rcode)\n\t}\n}\n\nfunc TestServingTLS(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tHandleFunc(\"example.com.\", AnotherHelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\tdefer HandleRemove(\"example.com.\")\n\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\tconfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\ts, addrstr, _, err := RunLocalTLSServer(\":0\", &config)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tc.Net = \"tcp-tls\"\n\tc.TLSConfig = &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t}\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\tr, _, err := c.Exchange(m, addrstr)\n\tif err != nil || len(r.Extra) == 0 {\n\t\tt.Fatal(\"failed to exchange miek.nl\", err)\n\t}\n\ttxt := r.Extra[0].(*TXT).Txt[0]\n\tif txt != \"Hello world\" {\n\t\tt.Error(\"unexpected result for miek.nl\", txt, \"!= Hello world\")\n\t}\n\n\tm.SetQuestion(\"example.com.\", TypeTXT)\n\tr, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to exchange example.com\", err)\n\t}\n\ttxt = r.Extra[0].(*TXT).Txt[0]\n\tif txt != \"Hello example\" {\n\t\tt.Error(\"unexpected result for example.com\", txt, \"!= Hello example\")\n\t}\n\n\t// Test Mixes cased as noticed by Ask.\n\tm.SetQuestion(\"eXaMplE.cOm.\", TypeTXT)\n\tr, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Error(\"failed to exchange eXaMplE.cOm\", err)\n\t}\n\ttxt = r.Extra[0].(*TXT).Txt[0]\n\tif txt != \"Hello example\" {\n\t\tt.Error(\"unexpected result for example.com\", txt, \"!= Hello example\")\n\t}\n}\n\n// TestServingTLSConnectionState tests that we only can access\n// tls.ConnectionState under a DNS query handled by a TLS DNS server.\n// This test will sequentially create a TLS, UDP and TCP server, attach a custom\n// handler which will set a testing error if tls.ConnectionState is available\n// when it is not expected, or the other way around.\nfunc TestServingTLSConnectionState(t *testing.T) {\n\thandlerResponse := \"Hello example\"\n\t// tlsHandlerTLS is a HandlerFunc that can be set to expect or not TLS\n\t// connection state.\n\ttlsHandlerTLS := func(tlsExpected bool) func(ResponseWriter, *Msg) {\n\t\treturn func(w ResponseWriter, req *Msg) {\n\t\t\tm := new(Msg)\n\t\t\tm.SetReply(req)\n\t\t\ttlsFound := true\n\t\t\tif connState := w.(ConnectionStater).ConnectionState(); connState == nil {\n\t\t\t\ttlsFound = false\n\t\t\t}\n\t\t\tif tlsFound != tlsExpected {\n\t\t\t\tt.Errorf(\"TLS connection state available: %t, expected: %t\", tlsFound, tlsExpected)\n\t\t\t}\n\t\t\tm.Extra = make([]RR, 1)\n\t\t\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{handlerResponse}}\n\t\t\tw.WriteMsg(m)\n\t\t}\n\t}\n\n\t// Question used in tests\n\tm := new(Msg)\n\tm.SetQuestion(\"tlsstate.example.net.\", TypeTXT)\n\n\t// TLS DNS server\n\tHandleFunc(\".\", tlsHandlerTLS(true))\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\tconfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\ts, addrstr, _, err := RunLocalTLSServer(\":0\", &config)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\t// TLS DNS query\n\tc := &Client{\n\t\tNet: \"tcp-tls\",\n\t\tTLSConfig: &tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t},\n\t}\n\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Error(\"failed to exchange tlsstate.example.net\", err)\n\t}\n\n\tHandleRemove(\".\")\n\t// UDP DNS Server\n\tHandleFunc(\".\", tlsHandlerTLS(false))\n\tdefer HandleRemove(\".\")\n\ts, addrstr, _, err = RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\t// UDP DNS query\n\tc = new(Client)\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Error(\"failed to exchange tlsstate.example.net\", err)\n\t}\n\n\t// TCP DNS Server\n\ts, addrstr, _, err = RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\t// TCP DNS query\n\tc = &Client{Net: \"tcp\"}\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Error(\"failed to exchange tlsstate.example.net\", err)\n\t}\n}\n\nfunc TestServingListenAndServe(t *testing.T) {\n\tHandleFunc(\"example.com.\", AnotherHelloServer)\n\tdefer HandleRemove(\"example.com.\")\n\n\twaitLock := sync.Mutex{}\n\tserver := &Server{Addr: \":0\", Net: \"udp\", ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock}\n\twaitLock.Lock()\n\n\tgo func() {\n\t\tserver.ListenAndServe()\n\t}()\n\twaitLock.Lock()\n\n\tc, m := new(Client), new(Msg)\n\tm.SetQuestion(\"example.com.\", TypeTXT)\n\taddr := server.PacketConn.LocalAddr().String() // Get address via the PacketConn that gets set.\n\tr, _, err := c.Exchange(m, addr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to exchange example.com\", err)\n\t}\n\ttxt := r.Extra[0].(*TXT).Txt[0]\n\tif txt != \"Hello example\" {\n\t\tt.Error(\"unexpected result for example.com\", txt, \"!= Hello example\")\n\t}\n\tserver.Shutdown()\n}\n\nfunc TestServingListenAndServeTLS(t *testing.T) {\n\tHandleFunc(\"example.com.\", AnotherHelloServer)\n\tdefer HandleRemove(\"example.com.\")\n\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\tconfig := &tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\twaitLock := sync.Mutex{}\n\tserver := &Server{Addr: \":0\", Net: \"tcp\", TLSConfig: config, ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock}\n\twaitLock.Lock()\n\n\tgo func() {\n\t\tserver.ListenAndServe()\n\t}()\n\twaitLock.Lock()\n\n\tc, m := new(Client), new(Msg)\n\tc.Net = \"tcp\"\n\tm.SetQuestion(\"example.com.\", TypeTXT)\n\taddr := server.Listener.Addr().String() // Get address via the Listener that gets set.\n\tr, _, err := c.Exchange(m, addr)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttxt := r.Extra[0].(*TXT).Txt[0]\n\tif txt != \"Hello example\" {\n\t\tt.Error(\"unexpected result for example.com\", txt, \"!= Hello example\")\n\t}\n\tserver.Shutdown()\n}\n\nfunc BenchmarkServe(b *testing.B) {\n\tb.StopTimer()\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\ta := runtime.GOMAXPROCS(4)\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tb.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, err := c.Exchange(m, addrstr)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"Exchange failed: %v\", err)\n\t\t}\n\t}\n\truntime.GOMAXPROCS(a)\n}\n\nfunc BenchmarkServe6(b *testing.B) {\n\tb.StopTimer()\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\ta := runtime.GOMAXPROCS(4)\n\ts, addrstr, _, err := RunLocalUDPServer(\"[::1]:0\")\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"bind: cannot assign requested address\") {\n\t\t\tb.Skip(\"missing IPv6 support\")\n\t\t}\n\t\tb.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, err := c.Exchange(m, addrstr)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"Exchange failed: %v\", err)\n\t\t}\n\t}\n\truntime.GOMAXPROCS(a)\n}\n\nfunc HelloServerCompress(w ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\tm.Extra = make([]RR, 1)\n\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\tm.Compress = true\n\tw.WriteMsg(m)\n}\n\nfunc BenchmarkServeCompress(b *testing.B) {\n\tb.StopTimer()\n\tHandleFunc(\"miek.nl.\", HelloServerCompress)\n\tdefer HandleRemove(\"miek.nl.\")\n\ta := runtime.GOMAXPROCS(4)\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tb.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeSOA)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, err := c.Exchange(m, addrstr)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"Exchange failed: %v\", err)\n\t\t}\n\t}\n\truntime.GOMAXPROCS(a)\n}\n\ntype maxRec struct {\n\tmax int\n\tsync.RWMutex\n}\n\nvar M = new(maxRec)\n\nfunc HelloServerLargeResponse(resp ResponseWriter, req *Msg) {\n\tm := new(Msg)\n\tm.SetReply(req)\n\tm.Authoritative = true\n\tm1 := 0\n\tM.RLock()\n\tm1 = M.max\n\tM.RUnlock()\n\tfor i := 0; i < m1; i++ {\n\t\taRec := &A{\n\t\t\tHdr: RR_Header{\n\t\t\t\tName:   req.Question[0].Name,\n\t\t\t\tRrtype: TypeA,\n\t\t\t\tClass:  ClassINET,\n\t\t\t\tTtl:    0,\n\t\t\t},\n\t\t\tA: net.ParseIP(fmt.Sprintf(\"127.0.0.%d\", i+1)).To4(),\n\t\t}\n\t\tm.Answer = append(m.Answer, aRec)\n\t}\n\tresp.WriteMsg(m)\n}\n\nfunc TestServingLargeResponses(t *testing.T) {\n\tHandleFunc(\"example.\", HelloServerLargeResponse)\n\tdefer HandleRemove(\"example.\")\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\t// Create request\n\tm := new(Msg)\n\tm.SetQuestion(\"web.service.example.\", TypeANY)\n\n\tc := new(Client)\n\tc.Net = \"udp\"\n\tM.Lock()\n\tM.max = 2\n\tM.Unlock()\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n\t// This must fail\n\tM.Lock()\n\tM.max = 20\n\tM.Unlock()\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err == nil {\n\t\tt.Error(\"failed to fail exchange, this should generate packet error\")\n\t}\n\t// But this must work again\n\tc.UDPSize = 7000\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Errorf(\"failed to exchange: %v\", err)\n\t}\n}\n\nfunc TestServingResponse(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping test in short mode.\")\n\t}\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\tc := new(Client)\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\tm.Response = false\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to exchange\", err)\n\t}\n\tm.Response = true // this holds up the reply, set short read time out to avoid waiting too long\n\tc.ReadTimeout = 100 * time.Millisecond\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err == nil {\n\t\tt.Fatal(\"exchanged response message\")\n\t}\n}\n\nfunc TestShutdownTCP(t *testing.T) {\n\ts, _, fin, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\terr = s.Shutdown()\n\tif err != nil {\n\t\tt.Fatalf(\"could not shutdown test TCP server, %v\", err)\n\t}\n\tselect {\n\tcase err := <-fin:\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error returned from ActivateAndServe, %v\", err)\n\t\t}\n\tcase <-time.After(2 * time.Second):\n\t\tt.Error(\"could not shutdown test TCP server. Gave up waiting\")\n\t}\n}\n\nfunc init() {\n\ttestShutdownNotify = &sync.Cond{\n\t\tL: new(sync.Mutex),\n\t}\n}\n\nfunc checkInProgressQueriesAtShutdownServer(t *testing.T, srv *Server, addr string, client *Client) {\n\tconst requests = 15 // enough to make this interesting? TODO: find a proper value\n\n\tvar errOnce sync.Once\n\t// t.Fail will panic if it's called after the test function has\n\t// finished. Burning the sync.Once with a defer will prevent the\n\t// handler from calling t.Errorf after we've returned.\n\tdefer errOnce.Do(func() {})\n\n\ttoHandle := int32(requests)\n\tHandleFunc(\"example.com.\", func(w ResponseWriter, req *Msg) {\n\t\tdefer atomic.AddInt32(&toHandle, -1)\n\n\t\t// Wait until ShutdownContext is called before replying.\n\t\ttestShutdownNotify.L.Lock()\n\t\ttestShutdownNotify.Wait()\n\t\ttestShutdownNotify.L.Unlock()\n\n\t\tm := new(Msg)\n\t\tm.SetReply(req)\n\t\tm.Extra = make([]RR, 1)\n\t\tm.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{\"Hello world\"}}\n\n\t\tif err := w.WriteMsg(m); err != nil {\n\t\t\terrOnce.Do(func() {\n\t\t\t\tt.Errorf(\"ResponseWriter.WriteMsg error: %s\", err)\n\t\t\t})\n\t\t}\n\t})\n\tdefer HandleRemove(\"example.com.\")\n\n\tclient.Timeout = 1 * time.Second\n\n\tconns := make([]*Conn, requests)\n\teg := new(errgroup.Group)\n\n\tfor i := range conns {\n\t\tconn := &conns[i]\n\t\teg.Go(func() error {\n\t\t\tvar err error\n\t\t\t*conn, err = client.Dial(addr)\n\t\t\treturn err\n\t\t})\n\t}\n\n\tif eg.Wait() != nil {\n\t\tt.Fatalf(\"client.Dial error: %v\", eg.Wait())\n\t}\n\n\tm := new(Msg)\n\tm.SetQuestion(\"example.com.\", TypeTXT)\n\teg = new(errgroup.Group)\n\n\tfor _, conn := range conns {\n\t\tconn := conn\n\t\teg.Go(func() error {\n\t\t\tconn.SetWriteDeadline(time.Now().Add(client.Timeout))\n\n\t\t\treturn conn.WriteMsg(m)\n\t\t})\n\t}\n\n\tif eg.Wait() != nil {\n\t\tt.Fatalf(\"conn.WriteMsg error: %v\", eg.Wait())\n\t}\n\n\t// This sleep is needed to allow time for the requests to\n\t// pass from the client through the kernel and back into\n\t// the server. Without it, some requests may still be in\n\t// the kernel's buffer when ShutdownContext is called.\n\ttime.Sleep(100 * time.Millisecond)\n\n\teg = new(errgroup.Group)\n\n\tfor _, conn := range conns {\n\t\tconn := conn\n\t\teg.Go(func() error {\n\t\t\tconn.SetReadDeadline(time.Now().Add(client.Timeout))\n\n\t\t\t_, err := conn.ReadMsg()\n\t\t\treturn err\n\t\t})\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), client.Timeout)\n\tdefer cancel()\n\n\tif err := srv.ShutdownContext(ctx); err != nil {\n\t\tt.Errorf(\"could not shutdown test server: %v\", err)\n\t}\n\n\tif left := atomic.LoadInt32(&toHandle); left != 0 {\n\t\tt.Errorf(\"ShutdownContext returned before %d replies\", left)\n\t}\n\n\tif eg.Wait() != nil {\n\t\tt.Errorf(\"conn.ReadMsg error: %v\", eg.Wait())\n\t}\n\n\tsrv.lock.RLock()\n\tdefer srv.lock.RUnlock()\n\tif len(srv.conns) != 0 {\n\t\tt.Errorf(\"TCP connection tracking map not empty after ShutdownContext; map still contains %d connections\", len(srv.conns))\n\t}\n}\n\nfunc TestInProgressQueriesAtShutdownTCP(t *testing.T) {\n\ts, addr, _, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\n\tc := &Client{Net: \"tcp\"}\n\tcheckInProgressQueriesAtShutdownServer(t, s, addr, c)\n}\n\nfunc TestShutdownTLS(t *testing.T) {\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\tconfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\ts, _, _, err := RunLocalTLSServer(\":0\", &config)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\terr = s.Shutdown()\n\tif err != nil {\n\t\tt.Errorf(\"could not shutdown test TLS server, %v\", err)\n\t}\n}\n\nfunc TestInProgressQueriesAtShutdownTLS(t *testing.T) {\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\tconfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\ts, addr, _, err := RunLocalTLSServer(\":0\", &config)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\n\tc := &Client{\n\t\tNet: \"tcp-tls\",\n\t\tTLSConfig: &tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t},\n\t}\n\tcheckInProgressQueriesAtShutdownServer(t, s, addr, c)\n}\n\nfunc TestHandlerCloseTCP(t *testing.T) {\n\tln, err := net.Listen(\"tcp\", \":0\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\taddr := ln.Addr().String()\n\n\tserver := &Server{Addr: addr, Net: \"tcp\", Listener: ln}\n\n\thname := \"testhandlerclosetcp.\"\n\ttriggered := make(chan struct{})\n\tHandleFunc(hname, func(w ResponseWriter, r *Msg) {\n\t\tclose(triggered)\n\t\tw.Close()\n\t})\n\tdefer HandleRemove(hname)\n\n\tgo func() {\n\t\tdefer server.Shutdown()\n\t\tc := &Client{Net: \"tcp\"}\n\t\tm := new(Msg).SetQuestion(hname, 1)\n\t\ttries := 0\n\texchange:\n\t\t_, _, err := c.Exchange(m, addr)\n\t\tif err != nil && err != io.EOF {\n\t\t\tt.Errorf(\"exchange failed: %v\", err)\n\t\t\tif tries == 3 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(time.Second / 10)\n\t\t\ttries++\n\t\t\tgoto exchange\n\t\t}\n\t}()\n\tif err := server.ActivateAndServe(); err != nil {\n\t\tt.Fatalf(\"ActivateAndServe failed: %v\", err)\n\t}\n\tselect {\n\tcase <-triggered:\n\tdefault:\n\t\tt.Fatalf(\"handler never called\")\n\t}\n}\n\nfunc TestShutdownUDP(t *testing.T) {\n\ts, _, fin, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\terr = s.Shutdown()\n\tif err != nil {\n\t\tt.Errorf(\"could not shutdown test UDP server, %v\", err)\n\t}\n\tselect {\n\tcase err := <-fin:\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error returned from ActivateAndServe, %v\", err)\n\t\t}\n\tcase <-time.After(2 * time.Second):\n\t\tt.Error(\"could not shutdown test UDP server. Gave up waiting\")\n\t}\n}\n\nfunc TestShutdownPacketConn(t *testing.T) {\n\ts, _, fin, err := RunLocalPacketConnServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\terr = s.Shutdown()\n\tif err != nil {\n\t\tt.Errorf(\"could not shutdown test UDP server, %v\", err)\n\t}\n\tselect {\n\tcase err := <-fin:\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error returned from ActivateAndServe, %v\", err)\n\t\t}\n\tcase <-time.After(2 * time.Second):\n\t\tt.Error(\"could not shutdown test UDP server. Gave up waiting\")\n\t}\n}\n\nfunc TestInProgressQueriesAtShutdownUDP(t *testing.T) {\n\ts, addr, _, err := RunLocalUDPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\n\tc := &Client{Net: \"udp\"}\n\tcheckInProgressQueriesAtShutdownServer(t, s, addr, c)\n}\n\nfunc TestInProgressQueriesAtShutdownPacketConn(t *testing.T) {\n\ts, addr, _, err := RunLocalPacketConnServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\n\tc := &Client{Net: \"udp\"}\n\tcheckInProgressQueriesAtShutdownServer(t, s, addr, c)\n}\n\nfunc TestServerStartStopRace(t *testing.T) {\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < 10; i++ {\n\t\twg.Add(1)\n\t\ts, _, _, err := RunLocalUDPServer(\":0\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"could not start server: %s\", err)\n\t\t}\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tif err := s.Shutdown(); err != nil {\n\t\t\t\tt.Errorf(\"could not stop server: %s\", err)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc TestSocketOptions(t *testing.T) {\n\tif !supportsReuseAddr || !supportsReusePort {\n\t\tt.Skip(\"reuseaddr or reuseport is not supported\")\n\t}\n\n\ttestSocketOptions := func(t *testing.T, reuseAddr bool, reusePort bool) {\n\t\twait := make(chan struct{})\n\n\t\tsrv := &Server{\n\t\t\tNet:       \"udp\",\n\t\t\tAddr:      \":0\",\n\t\t\tReuseAddr: reuseAddr,\n\t\t\tReusePort: reusePort,\n\t\t}\n\n\t\tsrv.NotifyStartedFunc = func() {\n\t\t\tdefer close(wait)\n\n\t\t\tconn, ok := srv.PacketConn.(*net.UDPConn)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"unexpected conn type: %T\", srv.PacketConn)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tsyscallConn, err := conn.SyscallConn()\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"cannot cast UDP conn to syscall conn: %v\", err)\n\t\t\t\treturn\n\n\t\t\t}\n\n\t\t\terr = syscallConn.Control(func(fd uintptr) {\n\t\t\t\tactualReusePort, err := checkReuseport(fd)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"cannot get SO_REUSEPORT socket option: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif actualReusePort != reusePort {\n\t\t\t\t\tt.Errorf(\"SO_REUSEPORT is %v instead of %v\", actualReusePort, reusePort)\n\t\t\t\t}\n\n\t\t\t\tactualReuseAddr, err := checkReuseaddr(fd)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"cannot get SO_REUSEADDR socket option: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif actualReuseAddr != reuseAddr {\n\t\t\t\t\tt.Errorf(\"SO_REUSEADDR is %v instead of %v\", actualReuseAddr, reusePort)\n\t\t\t\t}\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"cannot check socket options: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\tfin := make(chan error, 1)\n\t\tgo func() {\n\t\t\tfin <- srv.ListenAndServe()\n\t\t}()\n\n\t\tselect {\n\t\tcase <-wait:\n\t\t\terr := srv.Shutdown()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"cannot shutdown server: %v\", err)\n\t\t\t}\n\n\t\t\terr = <-fin\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"listen adn serve: %v\", err)\n\t\t\t}\n\t\tcase err := <-fin:\n\t\t\tt.Fatalf(\"listen adn serve: %v\", err)\n\t\t}\n\t}\n\n\tt.Run(\"no socket options\", func(t *testing.T) {\n\t\ttestSocketOptions(t, false, false)\n\t})\n\n\tt.Run(\"SO_REUSEPORT\", func(t *testing.T) {\n\t\ttestSocketOptions(t, false, true)\n\t})\n\n\tt.Run(\"SO_REUSEADDR\", func(t *testing.T) {\n\t\ttestSocketOptions(t, true, false)\n\t})\n\n\tt.Run(\"SO_REUSEADDR and SO_REUSEPORT\", func(t *testing.T) {\n\t\ttestSocketOptions(t, true, true)\n\t})\n}\n\nfunc TestServerReuseport(t *testing.T) {\n\tif !supportsReusePort {\n\t\tt.Skip(\"reuseport is not supported\")\n\t}\n\n\tstartServer := func(addr string) (*Server, chan error) {\n\t\twait := make(chan struct{})\n\t\tsrv := &Server{\n\t\t\tNet:               \"udp\",\n\t\t\tAddr:              addr,\n\t\t\tNotifyStartedFunc: func() { close(wait) },\n\t\t\tReusePort:         true,\n\t\t}\n\n\t\tfin := make(chan error, 1)\n\t\tgo func() {\n\t\t\tfin <- srv.ListenAndServe()\n\t\t}()\n\n\t\tselect {\n\t\tcase <-wait:\n\t\tcase err := <-fin:\n\t\t\tt.Fatalf(\"failed to start server: %v\", err)\n\t\t}\n\n\t\treturn srv, fin\n\t}\n\n\tsrv1, fin1 := startServer(\":0\") // :0 is resolved to a random free port by the kernel\n\tsrv2, fin2 := startServer(srv1.PacketConn.LocalAddr().String())\n\n\tif err := srv1.Shutdown(); err != nil {\n\t\tt.Fatalf(\"failed to shutdown first server: %v\", err)\n\t}\n\tif err := srv2.Shutdown(); err != nil {\n\t\tt.Fatalf(\"failed to shutdown second server: %v\", err)\n\t}\n\n\tif err := <-fin1; err != nil {\n\t\tt.Fatalf(\"first ListenAndServe returned error after Shutdown: %v\", err)\n\t}\n\tif err := <-fin2; err != nil {\n\t\tt.Fatalf(\"second ListenAndServe returned error after Shutdown: %v\", err)\n\t}\n}\n\nfunc TestServerReuseaddr(t *testing.T) {\n\tstartServerFn := func(t *testing.T, network, addr string, expectSuccess bool) (*Server, chan error) {\n\t\tt.Helper()\n\t\twait := make(chan struct{})\n\t\tsrv := &Server{\n\t\t\tNet:               network,\n\t\t\tAddr:              addr,\n\t\t\tNotifyStartedFunc: func() { close(wait) },\n\t\t\tReuseAddr:         true,\n\t\t}\n\n\t\tfin := make(chan error, 1)\n\t\tgo func() {\n\t\t\tfin <- srv.ListenAndServe()\n\t\t}()\n\n\t\tselect {\n\t\tcase <-wait:\n\t\tcase err := <-fin:\n\t\t\tswitch {\n\t\t\tcase expectSuccess:\n\t\t\t\tt.Fatalf(\"%s: failed to start server: %v\", t.Name(), err)\n\t\t\tdefault:\n\t\t\t\tfin <- err\n\t\t\t\treturn nil, fin\n\t\t\t}\n\t\t}\n\t\treturn srv, fin\n\t}\n\n\texternalIPFn := func(t *testing.T) (string, error) {\n\t\tt.Helper()\n\t\tifaces, err := net.Interfaces()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfor _, iface := range ifaces {\n\t\t\tif iface.Flags&net.FlagUp == 0 {\n\t\t\t\tcontinue // interface down\n\t\t\t}\n\t\t\tif iface.Flags&net.FlagLoopback != 0 {\n\t\t\t\tcontinue // loopback interface\n\t\t\t}\n\t\t\taddrs, err := iface.Addrs()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tfor _, addr := range addrs {\n\t\t\t\tvar ip net.IP\n\t\t\t\tswitch v := addr.(type) {\n\t\t\t\tcase *net.IPNet:\n\t\t\t\t\tip = v.IP\n\t\t\t\tcase *net.IPAddr:\n\t\t\t\t\tip = v.IP\n\t\t\t\t}\n\t\t\t\tif ip == nil || ip.IsLoopback() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tip = ip.To4()\n\t\t\t\tif ip == nil {\n\t\t\t\t\tcontinue // not an ipv4 address\n\t\t\t\t}\n\t\t\t\treturn ip.String(), nil\n\t\t\t}\n\t\t}\n\t\treturn \"\", errors.New(\"are you connected to the network?\")\n\t}\n\n\tfreePortFn := func(t *testing.T) int {\n\t\tt.Helper()\n\t\taddr, err := net.ResolveTCPAddr(\"tcp\", \"localhost:0\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable resolve tcp addr: %s\", err)\n\t\t}\n\n\t\tl, err := net.ListenTCP(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable listen tcp: %s\", err)\n\t\t}\n\t\tdefer l.Close()\n\t\treturn l.Addr().(*net.TCPAddr).Port\n\t}\n\n\tt.Run(\"should-fail-tcp\", func(t *testing.T) {\n\t\t// ReuseAddr should fail if you try to bind to exactly the same\n\t\t// combination of source address and port.\n\t\t// This should fail whether or not ReuseAddr is supported on a\n\t\t// particular OS\n\t\tip, err := externalIPFn(t)\n\t\tif err != nil {\n\t\t\tt.Skip(\"no external IPs found\")\n\t\t\treturn\n\t\t}\n\t\tport := freePortFn(t)\n\t\tsrv1, fin1 := startServerFn(t, \"tcp\", fmt.Sprintf(\"%s:%d\", ip, port), true)\n\t\tsrv2, fin2 := startServerFn(t, \"tcp\", fmt.Sprintf(\"%s:%d\", ip, port), false)\n\t\tswitch {\n\t\tcase srv2 != nil && srv2.started:\n\t\t\tt.Fatalf(\"second ListenAndServe should not have started\")\n\t\tdefault:\n\t\t\tif err := <-fin2; err == nil {\n\t\t\t\tt.Fatalf(\"second ListenAndServe should have returned a startup error: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\tif err := srv1.Shutdown(); err != nil {\n\t\t\tt.Fatalf(\"failed to shutdown first server: %v\", err)\n\t\t}\n\t\tif err := <-fin1; err != nil {\n\t\t\tt.Fatalf(\"first ListenAndServe returned error after Shutdown: %v\", err)\n\t\t}\n\t})\n\tt.Run(\"should-succeed-tcp\", func(t *testing.T) {\n\t\tif !supportsReuseAddr {\n\t\t\tt.Skip(\"reuseaddr is not supported\")\n\t\t}\n\t\tip, err := externalIPFn(t)\n\t\tif err != nil {\n\t\t\tt.Skip(\"no external IPs found\")\n\t\t\treturn\n\t\t}\n\t\tport := freePortFn(t)\n\n\t\t// ReuseAddr should succeed if you try to bind to the same port but a different source address\n\t\tsrv1, fin1 := startServerFn(t, \"tcp\", fmt.Sprintf(\"localhost:%d\", port), true)\n\t\tsrv2, fin2 := startServerFn(t, \"tcp\", fmt.Sprintf(\"%s:%d\", ip, port), true)\n\n\t\tif err := srv1.Shutdown(); err != nil {\n\t\t\tt.Fatalf(\"failed to shutdown first server: %v\", err)\n\t\t}\n\t\tif err := srv2.Shutdown(); err != nil {\n\t\t\tt.Fatalf(\"failed to shutdown second server: %v\", err)\n\t\t}\n\t\tif err := <-fin1; err != nil {\n\t\t\tt.Fatalf(\"first ListenAndServe returned error after Shutdown: %v\", err)\n\t\t}\n\t\tif err := <-fin2; err != nil {\n\t\t\tt.Fatalf(\"second ListenAndServe returned error after Shutdown: %v\", err)\n\t\t}\n\t})\n\tt.Run(\"should-succeed-udp\", func(t *testing.T) {\n\t\tif !supportsReuseAddr {\n\t\t\tt.Skip(\"reuseaddr is not supported\")\n\t\t}\n\t\tip, err := externalIPFn(t)\n\t\tif err != nil {\n\t\t\tt.Skip(\"no external IPs found\")\n\t\t\treturn\n\t\t}\n\t\tport := freePortFn(t)\n\n\t\t// ReuseAddr should succeed if you try to bind to the same port but a different source address\n\t\tsrv1, fin1 := startServerFn(t, \"udp\", fmt.Sprintf(\"localhost:%d\", port), true)\n\t\tsrv2, fin2 := startServerFn(t, \"udp\", fmt.Sprintf(\"%s:%d\", ip, port), true)\n\n\t\tif err := srv1.Shutdown(); err != nil {\n\t\t\tt.Fatalf(\"failed to shutdown first server: %v\", err)\n\t\t}\n\t\tif err := srv2.Shutdown(); err != nil {\n\t\t\tt.Fatalf(\"failed to shutdown second server: %v\", err)\n\t\t}\n\t\tif err := <-fin1; err != nil {\n\t\t\tt.Fatalf(\"first ListenAndServe returned error after Shutdown: %v\", err)\n\t\t}\n\t\tif err := <-fin2; err != nil {\n\t\t\tt.Fatalf(\"second ListenAndServe returned error after Shutdown: %v\", err)\n\t\t}\n\t})\n}\n\nfunc TestServerRoundtripTsig(t *testing.T) {\n\tsecret := map[string]string{\"test.\": \"so6ZGir4GPAqINNh9U5c3A==\"}\n\n\ts, addrstr, _, err := RunLocalUDPServer(\":0\", func(srv *Server) {\n\t\tsrv.TsigSecret = secret\n\t\tsrv.MsgAcceptFunc = func(dh Header) MsgAcceptAction {\n\t\t\t// defaultMsgAcceptFunc does reject UPDATE queries\n\t\t\treturn MsgAccept\n\t\t}\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %v\", err)\n\t}\n\tdefer s.Shutdown()\n\n\thandlerFired := make(chan struct{})\n\tHandleFunc(\"example.com.\", func(w ResponseWriter, r *Msg) {\n\t\tclose(handlerFired)\n\n\t\tm := new(Msg)\n\t\tm.SetReply(r)\n\t\tif r.IsTsig() != nil {\n\t\t\tstatus := w.TsigStatus()\n\t\t\tif status == nil {\n\t\t\t\t// *Msg r has an TSIG record and it was validated\n\t\t\t\tm.SetTsig(\"test.\", HmacSHA256, 300, time.Now().Unix())\n\t\t\t} else {\n\t\t\t\t// *Msg r has an TSIG records and it was not validated\n\t\t\t\tt.Errorf(\"invalid TSIG: %v\", status)\n\t\t\t}\n\t\t} else {\n\t\t\tt.Error(\"missing TSIG\")\n\t\t}\n\t\tif err := w.WriteMsg(m); err != nil {\n\t\t\tt.Error(\"writemsg failed\", err)\n\t\t}\n\t})\n\n\tc := new(Client)\n\tm := new(Msg)\n\tm.Opcode = OpcodeUpdate\n\tm.SetQuestion(\"example.com.\", TypeSOA)\n\tm.Ns = []RR{&CNAME{\n\t\tHdr: RR_Header{\n\t\t\tName:   \"foo.example.com.\",\n\t\t\tRrtype: TypeCNAME,\n\t\t\tClass:  ClassINET,\n\t\t\tTtl:    300,\n\t\t},\n\t\tTarget: \"bar.example.com.\",\n\t}}\n\tc.TsigSecret = secret\n\tm.SetTsig(\"test.\", HmacSHA256, 300, time.Now().Unix())\n\t_, _, err = c.Exchange(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to exchange\", err)\n\t}\n\tselect {\n\tcase <-handlerFired:\n\t\t// ok, handler was actually called\n\tdefault:\n\t\tt.Error(\"handler was not called\")\n\t}\n}\n\nfunc TestResponseAfterClose(t *testing.T) {\n\ttestError := func(name string, err error) {\n\t\tt.Helper()\n\n\t\texpect := fmt.Sprintf(\"dns: %s called after Close\", name)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"expected error from %s after Close\", name)\n\t\t} else if err.Error() != expect {\n\t\t\tt.Errorf(\"expected explicit error from %s after Close, expected %q, got %q\", name, expect, err)\n\t\t}\n\t}\n\n\trw := &response{\n\t\tclosed: true,\n\t}\n\n\t_, err := rw.Write(make([]byte, 2))\n\ttestError(\"Write\", err)\n\n\ttestError(\"WriteMsg\", rw.WriteMsg(new(Msg)))\n}\n\nfunc TestResponseDoubleClose(t *testing.T) {\n\trw := &response{\n\t\tclosed: true,\n\t}\n\tif err, expect := rw.Close(), \"dns: connection already closed\"; err == nil || err.Error() != expect {\n\t\tt.Errorf(\"Close did not return expected: error %q, got: %v\", expect, err)\n\t}\n}\n\ntype countingConn struct {\n\tnet.Conn\n\twrites int\n}\n\nfunc (c *countingConn) Write(p []byte) (int, error) {\n\tc.writes++\n\treturn len(p), nil\n}\n\nfunc TestResponseWriteSinglePacket(t *testing.T) {\n\tc := &countingConn{}\n\trw := &response{\n\t\ttcp: c,\n\t}\n\trw.writer = rw\n\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\tm.Response = true\n\terr := rw.WriteMsg(m)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to write: %v\", err)\n\t}\n\n\tif c.writes != 1 {\n\t\tt.Fatalf(\"incorrect number of Write calls\")\n\t}\n}\n\ntype ExampleFrameLengthWriter struct {\n\tWriter\n}\n\nfunc (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) {\n\tfmt.Println(\"writing raw DNS message of length\", len(m))\n\treturn e.Writer.Write(m)\n}\n\nfunc ExampleDecorateWriter() {\n\t// instrument raw DNS message writing\n\twf := DecorateWriter(func(w Writer) Writer {\n\t\treturn &ExampleFrameLengthWriter{w}\n\t})\n\n\t// simple UDP server\n\tpc, err := net.ListenPacket(\"udp\", \":0\")\n\tif err != nil {\n\t\tfmt.Println(err.Error())\n\t\treturn\n\t}\n\tserver := &Server{\n\t\tPacketConn:     pc,\n\t\tDecorateWriter: wf,\n\t\tReadTimeout:    time.Hour, WriteTimeout: time.Hour,\n\t}\n\n\twaitLock := sync.Mutex{}\n\twaitLock.Lock()\n\tserver.NotifyStartedFunc = waitLock.Unlock\n\tdefer server.Shutdown()\n\n\tgo func() {\n\t\tserver.ActivateAndServe()\n\t\tpc.Close()\n\t}()\n\n\twaitLock.Lock()\n\n\tHandleFunc(\"miek.nl.\", HelloServer)\n\n\tc := new(Client)\n\tm := new(Msg)\n\tm.SetQuestion(\"miek.nl.\", TypeTXT)\n\t_, _, err = c.Exchange(m, pc.LocalAddr().String())\n\tif err != nil {\n\t\tfmt.Println(\"failed to exchange\", err.Error())\n\t\treturn\n\t}\n\t// Output: writing raw DNS message of length 56\n}\n\nvar (\n\t// CertPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)\n\tCertPEMBlock = []byte(`-----BEGIN CERTIFICATE-----\nMIIDAzCCAeugAwIBAgIRAJFYMkcn+b8dpU15wjf++GgwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjAxMDgxMjAzNTNaFw0xNzAxMDcxMjAz\nNTNaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQDXjqO6skvP03k58CNjQggd9G/mt+Wa+xRU+WXiKCCHttawM8x+slq5\nyfsHCwxlwsGn79HmJqecNqgHb2GWBXAvVVokFDTcC1hUP4+gp2gu9Ny27UHTjlLm\nO0l/xZ5MN8tfKyYlFw18tXu3fkaPyHj8v/D1RDkuo4ARdFvGSe8TqisbhLk2+9ow\nxfIGbEM9Fdiw8qByC2+d+FfvzIKz3GfQVwn0VoRom8L6NBIANq1IGrB5JefZB6nv\nDnfuxkBmY7F1513HKuEJ8KsLWWZWV9OPU4j4I4Rt+WJNlKjbD2srHxyrS2RDsr91\n8nCkNoWVNO3sZq0XkWKecdc921vL4ginAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIC\npDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQT\nMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGcU3iyLBIVZj\naDzSvEDHUd1bnLBl1C58Xu/CyKlPqVU7mLfK0JcgEaYQTSX6fCJVNLbbCrcGLsPJ\nfbjlBbyeLjTV413fxPVuona62pBFjqdtbli2Qe8FRH2KBdm41JUJGdo+SdsFu7nc\nBFOcubdw6LLIXvsTvwndKcHWx1rMX709QU1Vn1GAIsbJV/DWI231Jyyb+lxAUx/C\n8vce5uVxiKcGS+g6OjsN3D3TtiEQGSXLh013W6Wsih8td8yMCMZ3w8LQ38br1GUe\nahLIgUJ9l6HDguM17R7kGqxNvbElsMUHfTtXXP7UDQUiYXDakg8xDP6n9DCDhJ8Y\nbSt7OLB7NQ==\n-----END CERTIFICATE-----`)\n\n\t// KeyPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)\n\tKeyPEMBlock = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA146jurJLz9N5OfAjY0IIHfRv5rflmvsUVPll4iggh7bWsDPM\nfrJaucn7BwsMZcLBp+/R5iannDaoB29hlgVwL1VaJBQ03AtYVD+PoKdoLvTctu1B\n045S5jtJf8WeTDfLXysmJRcNfLV7t35Gj8h4/L/w9UQ5LqOAEXRbxknvE6orG4S5\nNvvaMMXyBmxDPRXYsPKgcgtvnfhX78yCs9xn0FcJ9FaEaJvC+jQSADatSBqweSXn\n2Qep7w537sZAZmOxdeddxyrhCfCrC1lmVlfTj1OI+COEbfliTZSo2w9rKx8cq0tk\nQ7K/dfJwpDaFlTTt7GatF5FinnHXPdtby+IIpwIDAQABAoIBAAJK4RDmPooqTJrC\nJA41MJLo+5uvjwCT9QZmVKAQHzByUFw1YNJkITTiognUI0CdzqNzmH7jIFs39ZeG\nproKusO2G6xQjrNcZ4cV2fgyb5g4QHStl0qhs94A+WojduiGm2IaumAgm6Mc5wDv\nld6HmknN3Mku/ZCyanVFEIjOVn2WB7ZQLTBs6ZYaebTJG2Xv6p9t2YJW7pPQ9Xce\ns9ohAWohyM4X/OvfnfnLtQp2YLw/BxwehBsCR5SXM3ibTKpFNtxJC8hIfTuWtxZu\n2ywrmXShYBRB1WgtZt5k04bY/HFncvvcHK3YfI1+w4URKtwdaQgPUQRbVwDwuyBn\nflfkCJECgYEA/eWt01iEyE/lXkGn6V9lCocUU7lCU6yk5UT8VXVUc5If4KZKPfCk\np4zJDOqwn2eM673aWz/mG9mtvAvmnugaGjcaVCyXOp/D/GDmKSoYcvW5B/yjfkLy\ndK6Yaa5LDRVYlYgyzcdCT5/9Qc626NzFwKCZNI4ncIU8g7ViATRxWJ8CgYEA2Ver\nvZ0M606sfgC0H3NtwNBxmuJ+lIF5LNp/wDi07lDfxRR1rnZMX5dnxjcpDr/zvm8J\nWtJJX3xMgqjtHuWKL3yKKony9J5ZPjichSbSbhrzfovgYIRZLxLLDy4MP9L3+CX/\nyBXnqMWuSnFX+M5fVGxdDWiYF3V+wmeOv9JvavkCgYEAiXAPDFzaY+R78O3xiu7M\nr0o3wqqCMPE/wav6O/hrYrQy9VSO08C0IM6g9pEEUwWmzuXSkZqhYWoQFb8Lc/GI\nT7CMXAxXQLDDUpbRgG79FR3Wr3AewHZU8LyiXHKwxcBMV4WGmsXGK3wbh8fyU1NO\n6NsGk+BvkQVOoK1LBAPzZ1kCgYEAsBSmD8U33T9s4dxiEYTrqyV0lH3g/SFz8ZHH\npAyNEPI2iC1ONhyjPWKlcWHpAokiyOqeUpVBWnmSZtzC1qAydsxYB6ShT+sl9BHb\nRMix/QAauzBJhQhUVJ3OIys0Q1UBDmqCsjCE8SfOT4NKOUnA093C+YT+iyrmmktZ\nzDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb\n7+a1OMhLwWbuz+pl64wFKrbSUyimMOYQpjVE/1vk/kb99pxbgol27hdKyTH1d+ov\nkFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA=\n-----END RSA PRIVATE KEY-----`)\n)\n"
  },
  {
    "path": "sig0.go",
    "content": "package dns\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/rsa\"\n\t\"encoding/binary\"\n\t\"math/big\"\n\t\"time\"\n)\n\n// Sign signs a dns.Msg. It fills the signature with the appropriate data.\n// The SIG record should have the SignerName, KeyTag, Algorithm, Inception\n// and Expiration set.\nfunc (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {\n\tif k == nil {\n\t\treturn nil, ErrPrivKey\n\t}\n\tif rr.KeyTag == 0 || rr.SignerName == \"\" || rr.Algorithm == 0 {\n\t\treturn nil, ErrKey\n\t}\n\n\trr.Hdr = RR_Header{Name: \".\", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0}\n\trr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0\n\n\tbuf := make([]byte, m.Len()+Len(rr))\n\tmbuf, err := m.PackBuffer(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif &buf[0] != &mbuf[0] {\n\t\treturn nil, ErrBuf\n\t}\n\toff, err := PackRR(rr, buf, len(mbuf), nil, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbuf = buf[:off:cap(buf)]\n\n\th, cryptohash, err := hashFromAlgorithm(rr.Algorithm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Write SIG rdata\n\th.Write(buf[len(mbuf)+1+2+2+4+2:])\n\t// Write message\n\th.Write(buf[:len(mbuf)])\n\n\tsignature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trr.Signature = toBase64(signature)\n\n\tbuf = append(buf, signature...)\n\tif len(buf) > int(^uint16(0)) {\n\t\treturn nil, ErrBuf\n\t}\n\t// Adjust sig data length\n\trdoff := len(mbuf) + 1 + 2 + 2 + 4\n\trdlen := binary.BigEndian.Uint16(buf[rdoff:])\n\trdlen += uint16(len(signature))\n\tbinary.BigEndian.PutUint16(buf[rdoff:], rdlen)\n\t// Adjust additional count\n\tadc := binary.BigEndian.Uint16(buf[10:])\n\tadc++\n\tbinary.BigEndian.PutUint16(buf[10:], adc)\n\treturn buf, nil\n}\n\n// Verify validates the message buf using the key k.\n// It's assumed that buf is a valid message from which rr was unpacked.\nfunc (rr *SIG) Verify(k *KEY, buf []byte) error {\n\tif k == nil {\n\t\treturn ErrKey\n\t}\n\tif rr.KeyTag == 0 || rr.SignerName == \"\" || rr.Algorithm == 0 {\n\t\treturn ErrKey\n\t}\n\n\th, cryptohash, err := hashFromAlgorithm(rr.Algorithm)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbuflen := len(buf)\n\tqdc := binary.BigEndian.Uint16(buf[4:])\n\tanc := binary.BigEndian.Uint16(buf[6:])\n\tauc := binary.BigEndian.Uint16(buf[8:])\n\tadc := binary.BigEndian.Uint16(buf[10:])\n\toffset := headerSize\n\tfor i := uint16(0); i < qdc && offset < buflen; i++ {\n\t\t_, offset, err = UnpackDomainName(buf, offset)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Skip past Type and Class\n\t\toffset += 2 + 2\n\t}\n\tfor i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {\n\t\t_, offset, err = UnpackDomainName(buf, offset)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Skip past Type, Class and TTL\n\t\toffset += 2 + 2 + 4\n\t\tif offset+1 >= buflen {\n\t\t\tcontinue\n\t\t}\n\t\trdlen := binary.BigEndian.Uint16(buf[offset:])\n\t\toffset += 2\n\t\toffset += int(rdlen)\n\t}\n\tif offset >= buflen {\n\t\treturn &Error{err: \"overflowing unpacking signed message\"}\n\t}\n\n\t// offset should be just prior to SIG\n\tbodyend := offset\n\t// owner name SHOULD be root\n\t_, offset, err = UnpackDomainName(buf, offset)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Skip Type, Class, TTL, RDLen\n\toffset += 2 + 2 + 4 + 2\n\tsigstart := offset\n\t// Skip Type Covered, Algorithm, Labels, Original TTL\n\toffset += 2 + 1 + 1 + 4\n\tif offset+4+4 >= buflen {\n\t\treturn &Error{err: \"overflow unpacking signed message\"}\n\t}\n\texpire := binary.BigEndian.Uint32(buf[offset:])\n\toffset += 4\n\tincept := binary.BigEndian.Uint32(buf[offset:])\n\toffset += 4\n\tnow := uint32(time.Now().Unix())\n\tif now < incept || now > expire {\n\t\treturn ErrTime\n\t}\n\t// Skip key tag\n\toffset += 2\n\tvar signername string\n\tsignername, offset, err = UnpackDomainName(buf, offset)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// If key has come from the DNS name compression might\n\t// have mangled the case of the name\n\tif !equal(signername, k.Header().Name) {\n\t\treturn &Error{err: \"signer name doesn't match key name\"}\n\t}\n\tsigend := offset\n\th.Write(buf[sigstart:sigend])\n\th.Write(buf[:10])\n\th.Write([]byte{\n\t\tbyte((adc - 1) << 8),\n\t\tbyte(adc - 1),\n\t})\n\th.Write(buf[12:bodyend])\n\n\thashed := h.Sum(nil)\n\tsig := buf[sigend:]\n\tswitch k.Algorithm {\n\tcase RSASHA1, RSASHA256, RSASHA512:\n\t\tpk := k.publicKeyRSA()\n\t\tif pk != nil {\n\t\t\treturn rsa.VerifyPKCS1v15(pk, cryptohash, hashed, sig)\n\t\t}\n\tcase ECDSAP256SHA256, ECDSAP384SHA384:\n\t\tpk := k.publicKeyECDSA()\n\t\tr := new(big.Int).SetBytes(sig[:len(sig)/2])\n\t\ts := new(big.Int).SetBytes(sig[len(sig)/2:])\n\t\tif pk != nil {\n\t\t\tif ecdsa.Verify(pk, hashed, r, s) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn ErrSig\n\t\t}\n\tcase ED25519:\n\t\tpk := k.publicKeyED25519()\n\t\tif pk != nil {\n\t\t\tif ed25519.Verify(pk, hashed, sig) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn ErrSig\n\t\t}\n\t}\n\treturn ErrKeyAlg\n}\n"
  },
  {
    "path": "sig0_test.go",
    "content": "package dns\n\nimport (\n\t\"crypto\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSIG0(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping test in short mode.\")\n\t}\n\tm := new(Msg)\n\tm.SetQuestion(\"example.org.\", TypeSOA)\n\tfor _, alg := range []uint8{ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512, ED25519} {\n\t\talgstr := AlgorithmToString[alg]\n\t\tkeyrr := new(KEY)\n\t\tkeyrr.Hdr.Name = algstr + \".\"\n\t\tkeyrr.Hdr.Rrtype = TypeKEY\n\t\tkeyrr.Hdr.Class = ClassINET\n\t\tkeyrr.Algorithm = alg\n\t\tkeysize := 512\n\t\tswitch alg {\n\t\tcase ECDSAP256SHA256, ED25519:\n\t\t\tkeysize = 256\n\t\tcase ECDSAP384SHA384:\n\t\t\tkeysize = 384\n\t\tcase RSASHA1, RSASHA256, RSASHA512:\n\t\t\tkeysize = 1024\n\t\t}\n\t\tpk, err := keyrr.Generate(keysize)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to generate key for %q: %v\", algstr, err)\n\t\t\tcontinue\n\t\t}\n\t\tnow := uint32(time.Now().Unix())\n\t\tsigrr := new(SIG)\n\t\tsigrr.Hdr.Name = \".\"\n\t\tsigrr.Hdr.Rrtype = TypeSIG\n\t\tsigrr.Hdr.Class = ClassANY\n\t\tsigrr.Algorithm = alg\n\t\tsigrr.Expiration = now + 300\n\t\tsigrr.Inception = now - 300\n\t\tsigrr.KeyTag = keyrr.KeyTag()\n\t\tsigrr.SignerName = keyrr.Hdr.Name\n\t\tmb, err := sigrr.Sign(pk.(crypto.Signer), m)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to sign message using %q: %v\", algstr, err)\n\t\t\tcontinue\n\t\t}\n\t\tm := new(Msg)\n\t\tif err := m.Unpack(mb); err != nil {\n\t\t\tt.Errorf(\"failed to unpack message signed using %q: %v\", algstr, err)\n\t\t\tcontinue\n\t\t}\n\t\tif len(m.Extra) != 1 {\n\t\t\tt.Errorf(\"missing SIG for message signed using %q\", algstr)\n\t\t\tcontinue\n\t\t}\n\t\tvar sigrrwire *SIG\n\t\tswitch rr := m.Extra[0].(type) {\n\t\tcase *SIG:\n\t\t\tsigrrwire = rr\n\t\tdefault:\n\t\t\tt.Errorf(\"expected SIG RR, instead: %v\", rr)\n\t\t\tcontinue\n\t\t}\n\t\tfor _, rr := range []*SIG{sigrr, sigrrwire} {\n\t\t\tid := \"sigrr\"\n\t\t\tif rr == sigrrwire {\n\t\t\t\tid = \"sigrrwire\"\n\t\t\t}\n\t\t\tif err := rr.Verify(keyrr, mb); err != nil {\n\t\t\t\tt.Errorf(\"failed to verify %q signed SIG(%s): %v\", algstr, id, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tmb[13]++\n\t\tif err := sigrr.Verify(keyrr, mb); err == nil {\n\t\t\tt.Errorf(\"verify succeeded on an altered message using %q\", algstr)\n\t\t\tcontinue\n\t\t}\n\t\tsigrr.Expiration = 2\n\t\tsigrr.Inception = 1\n\t\tmb, _ = sigrr.Sign(pk.(crypto.Signer), m)\n\t\tif err := sigrr.Verify(keyrr, mb); err == nil {\n\t\t\tt.Errorf(\"verify succeeded on an expired message using %q\", algstr)\n\t\t\tcontinue\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "smimea.go",
    "content": "package dns\n\nimport (\n\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n)\n\n// Sign creates a SMIMEA record from an SSL certificate.\nfunc (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {\n\tr.Hdr.Rrtype = TypeSMIMEA\n\tr.Usage = uint8(usage)\n\tr.Selector = uint8(selector)\n\tr.MatchingType = uint8(matchingType)\n\n\tr.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)\n\treturn err\n}\n\n// Verify verifies a SMIMEA record against an SSL certificate. If it is OK\n// a nil error is returned.\nfunc (r *SMIMEA) Verify(cert *x509.Certificate) error {\n\tc, err := CertificateToDANE(r.Selector, r.MatchingType, cert)\n\tif err != nil {\n\t\treturn err // Not also ErrSig?\n\t}\n\tif r.Certificate == c {\n\t\treturn nil\n\t}\n\treturn ErrSig // ErrSig, really?\n}\n\n// SMIMEAName returns the ownername of a SMIMEA resource record as per the\n// format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3\nfunc SMIMEAName(email, domain string) (string, error) {\n\thasher := sha256.New()\n\thasher.Write([]byte(email))\n\n\t// RFC Section 3: \"The local-part is hashed using the SHA2-256\n\t// algorithm with the hash truncated to 28 octets and\n\t// represented in its hexadecimal representation to become the\n\t// left-most label in the prepared domain name\"\n\treturn hex.EncodeToString(hasher.Sum(nil)[:28]) + \".\" + \"_smimecert.\" + domain, nil\n}\n"
  },
  {
    "path": "svcb.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// SVCBKey is the type of the keys used in the SVCB RR.\ntype SVCBKey uint16\n\n// Keys defined in rfc9460\nconst (\n\tSVCB_MANDATORY SVCBKey = iota\n\tSVCB_ALPN\n\tSVCB_NO_DEFAULT_ALPN\n\tSVCB_PORT\n\tSVCB_IPV4HINT\n\tSVCB_ECHCONFIG\n\tSVCB_IPV6HINT\n\tSVCB_DOHPATH // rfc9461 Section 5\n\tSVCB_OHTTP   // rfc9540 Section 8\n\n\tsvcb_RESERVED SVCBKey = 65535\n)\n\nvar svcbKeyToStringMap = map[SVCBKey]string{\n\tSVCB_MANDATORY:       \"mandatory\",\n\tSVCB_ALPN:            \"alpn\",\n\tSVCB_NO_DEFAULT_ALPN: \"no-default-alpn\",\n\tSVCB_PORT:            \"port\",\n\tSVCB_IPV4HINT:        \"ipv4hint\",\n\tSVCB_ECHCONFIG:       \"ech\",\n\tSVCB_IPV6HINT:        \"ipv6hint\",\n\tSVCB_DOHPATH:         \"dohpath\",\n\tSVCB_OHTTP:           \"ohttp\",\n}\n\nvar svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)\n\nfunc reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey {\n\tn := make(map[string]SVCBKey, len(m))\n\tfor u, s := range m {\n\t\tn[s] = u\n\t}\n\treturn n\n}\n\n// String takes the numerical code of an SVCB key and returns its name.\n// Returns an empty string for reserved keys.\n// Accepts unassigned keys as well as experimental/private keys.\nfunc (key SVCBKey) String() string {\n\tif x := svcbKeyToStringMap[key]; x != \"\" {\n\t\treturn x\n\t}\n\tif key == svcb_RESERVED {\n\t\treturn \"\"\n\t}\n\treturn \"key\" + strconv.FormatUint(uint64(key), 10)\n}\n\n// svcbStringToKey returns the numerical code of an SVCB key.\n// Returns svcb_RESERVED for reserved/invalid keys.\n// Accepts unassigned keys as well as experimental/private keys.\nfunc svcbStringToKey(s string) SVCBKey {\n\tif strings.HasPrefix(s, \"key\") {\n\t\ta, err := strconv.ParseUint(s[3:], 10, 16)\n\t\t// no leading zeros\n\t\t// key shouldn't be registered\n\t\tif err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != \"\" {\n\t\t\treturn svcb_RESERVED\n\t\t}\n\t\treturn SVCBKey(a)\n\t}\n\tif key, ok := svcbStringToKeyMap[s]; ok {\n\t\treturn key\n\t}\n\treturn svcb_RESERVED\n}\n\nfunc (rr *SVCB) parse(c *zlexer, o string) *ParseError {\n\tl, _ := c.Next()\n\ti, e := strconv.ParseUint(l.token, 10, 16)\n\tif e != nil || l.err {\n\t\treturn &ParseError{file: l.token, err: \"bad SVCB priority\", lex: l}\n\t}\n\trr.Priority = uint16(i)\n\n\tc.Next()        // zBlank\n\tl, _ = c.Next() // zString\n\trr.Target = l.token\n\n\tname, nameOk := toAbsoluteName(l.token, o)\n\tif l.err || !nameOk {\n\t\treturn &ParseError{file: l.token, err: \"bad SVCB Target\", lex: l}\n\t}\n\trr.Target = name\n\n\t// Values (if any)\n\tl, _ = c.Next()\n\tvar xs []SVCBKeyValue\n\t// Helps require whitespace between pairs.\n\t// Prevents key1000=\"a\"key1001=...\n\tcanHaveNextKey := true\n\tfor l.value != zNewline && l.value != zEOF {\n\t\tswitch l.value {\n\t\tcase zString:\n\t\t\tif !canHaveNextKey {\n\t\t\t\t// The key we can now read was probably meant to be\n\t\t\t\t// a part of the last value.\n\t\t\t\treturn &ParseError{file: l.token, err: \"bad SVCB value quotation\", lex: l}\n\t\t\t}\n\n\t\t\t// In key=value pairs, value does not have to be quoted unless value\n\t\t\t// contains whitespace. And keys don't need to have values.\n\t\t\t// Similarly, keys with an equality signs after them don't need values.\n\t\t\t// l.token includes at least up to the first equality sign.\n\t\t\tidx := strings.IndexByte(l.token, '=')\n\t\t\tvar key, value string\n\t\t\tif idx < 0 {\n\t\t\t\t// Key with no value and no equality sign\n\t\t\t\tkey = l.token\n\t\t\t} else if idx == 0 {\n\t\t\t\treturn &ParseError{file: l.token, err: \"bad SVCB key\", lex: l}\n\t\t\t} else {\n\t\t\t\tkey, value = l.token[:idx], l.token[idx+1:]\n\n\t\t\t\tif value == \"\" {\n\t\t\t\t\t// We have a key and an equality sign. Maybe we have nothing\n\t\t\t\t\t// after \"=\" or we have a double quote.\n\t\t\t\t\tl, _ = c.Next()\n\t\t\t\t\tif l.value == zQuote {\n\t\t\t\t\t\t// Only needed when value ends with double quotes.\n\t\t\t\t\t\t// Any value starting with zQuote ends with it.\n\t\t\t\t\t\tcanHaveNextKey = false\n\n\t\t\t\t\t\tl, _ = c.Next()\n\t\t\t\t\t\tswitch l.value {\n\t\t\t\t\t\tcase zString:\n\t\t\t\t\t\t\t// We have a value in double quotes.\n\t\t\t\t\t\t\tvalue = l.token\n\t\t\t\t\t\t\tl, _ = c.Next()\n\t\t\t\t\t\t\tif l.value != zQuote {\n\t\t\t\t\t\t\t\treturn &ParseError{file: l.token, err: \"SVCB unterminated value\", lex: l}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tcase zQuote:\n\t\t\t\t\t\t\t// There's nothing in double quotes.\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn &ParseError{file: l.token, err: \"bad SVCB value\", lex: l}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tkv := makeSVCBKeyValue(svcbStringToKey(key))\n\t\t\tif kv == nil {\n\t\t\t\treturn &ParseError{file: l.token, err: \"bad SVCB key\", lex: l}\n\t\t\t}\n\t\t\tif err := kv.parse(value); err != nil {\n\t\t\t\treturn &ParseError{file: l.token, wrappedErr: err, lex: l}\n\t\t\t}\n\t\t\txs = append(xs, kv)\n\t\tcase zQuote:\n\t\t\treturn &ParseError{file: l.token, err: \"SVCB key can't contain double quotes\", lex: l}\n\t\tcase zBlank:\n\t\t\tcanHaveNextKey = true\n\t\tdefault:\n\t\t\treturn &ParseError{file: l.token, err: \"bad SVCB values\", lex: l}\n\t\t}\n\t\tl, _ = c.Next()\n\t}\n\n\t// \"In AliasMode, records SHOULD NOT include any SvcParams, and recipients MUST\n\t// ignore any SvcParams that are present.\"\n\t// However, we don't check rr.Priority == 0 && len(xs) > 0 here\n\t// It is the responsibility of the user of the library to check this.\n\t// This is to encourage the fixing of the source of this error.\n\n\trr.Value = xs\n\treturn nil\n}\n\n// makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys.\nfunc makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {\n\tswitch key {\n\tcase SVCB_MANDATORY:\n\t\treturn new(SVCBMandatory)\n\tcase SVCB_ALPN:\n\t\treturn new(SVCBAlpn)\n\tcase SVCB_NO_DEFAULT_ALPN:\n\t\treturn new(SVCBNoDefaultAlpn)\n\tcase SVCB_PORT:\n\t\treturn new(SVCBPort)\n\tcase SVCB_IPV4HINT:\n\t\treturn new(SVCBIPv4Hint)\n\tcase SVCB_ECHCONFIG:\n\t\treturn new(SVCBECHConfig)\n\tcase SVCB_IPV6HINT:\n\t\treturn new(SVCBIPv6Hint)\n\tcase SVCB_DOHPATH:\n\t\treturn new(SVCBDoHPath)\n\tcase SVCB_OHTTP:\n\t\treturn new(SVCBOhttp)\n\tcase svcb_RESERVED:\n\t\treturn nil\n\tdefault:\n\t\te := new(SVCBLocal)\n\t\te.KeyCode = key\n\t\treturn e\n\t}\n}\n\n// SVCB RR. See RFC 9460.\ntype SVCB struct {\n\tHdr      RR_Header\n\tPriority uint16         // If zero, Value must be empty or discarded by the user of this library\n\tTarget   string         `dns:\"domain-name\"`\n\tValue    []SVCBKeyValue `dns:\"pairs\"`\n}\n\n// HTTPS RR. See RFC 9460. Everything valid for SVCB applies to HTTPS as well.\n// Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols.\ntype HTTPS struct {\n\tSVCB\n}\n\nfunc (rr *HTTPS) String() string {\n\treturn rr.SVCB.String()\n}\n\nfunc (rr *HTTPS) parse(c *zlexer, o string) *ParseError {\n\treturn rr.SVCB.parse(c, o)\n}\n\n// SVCBKeyValue defines a key=value pair for the SVCB RR type.\n// An SVCB RR can have multiple SVCBKeyValues appended to it.\ntype SVCBKeyValue interface {\n\tKey() SVCBKey          // Key returns the numerical key code.\n\tpack() ([]byte, error) // pack returns the encoded value.\n\tunpack([]byte) error   // unpack sets the value.\n\tString() string        // String returns the string representation of the value.\n\tparse(string) error    // parse sets the value to the given string representation of the value.\n\tcopy() SVCBKeyValue    // copy returns a deep-copy of the pair.\n\tlen() int              // len returns the length of value in the wire format.\n}\n\n// SVCBMandatory pair adds to required keys that must be interpreted for the RR\n// to be functional. If ignored, the whole RRSet must be ignored.\n// \"port\" and \"no-default-alpn\" are mandatory by default if present,\n// so they shouldn't be included here.\n//\n// It is incumbent upon the user of this library to reject the RRSet if\n// or avoid constructing such an RRSet that:\n// - \"mandatory\" is included as one of the keys of mandatory\n// - no key is listed multiple times in mandatory\n// - all keys listed in mandatory are present\n// - escape sequences are not used in mandatory\n// - mandatory, when present, lists at least one key\n//\n// Basic use pattern for creating a mandatory option:\n//\n//\ts := &dns.SVCB{Hdr: dns.RR_Header{Name: \".\", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}\n//\te := new(dns.SVCBMandatory)\n//\te.Code = []uint16{dns.SVCB_ALPN}\n//\ts.Value = append(s.Value, e)\n//\tt := new(dns.SVCBAlpn)\n//\tt.Alpn = []string{\"xmpp-client\"}\n//\ts.Value = append(s.Value, t)\ntype SVCBMandatory struct {\n\tCode []SVCBKey\n}\n\nfunc (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY }\n\nfunc (s *SVCBMandatory) String() string {\n\tstr := make([]string, len(s.Code))\n\tfor i, e := range s.Code {\n\t\tstr[i] = e.String()\n\t}\n\treturn strings.Join(str, \",\")\n}\n\nfunc (s *SVCBMandatory) pack() ([]byte, error) {\n\tcodes := cloneSlice(s.Code)\n\tsort.Slice(codes, func(i, j int) bool {\n\t\treturn codes[i] < codes[j]\n\t})\n\tb := make([]byte, 2*len(codes))\n\tfor i, e := range codes {\n\t\tbinary.BigEndian.PutUint16(b[2*i:], uint16(e))\n\t}\n\treturn b, nil\n}\n\nfunc (s *SVCBMandatory) unpack(b []byte) error {\n\tif len(b)%2 != 0 {\n\t\treturn errors.New(\"bad svcbmandatory: value length is not a multiple of 2\")\n\t}\n\tcodes := make([]SVCBKey, 0, len(b)/2)\n\tfor i := 0; i < len(b); i += 2 {\n\t\t// We assume strictly increasing order.\n\t\tcodes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:])))\n\t}\n\ts.Code = codes\n\treturn nil\n}\n\nfunc (s *SVCBMandatory) parse(b string) error {\n\tcodes := make([]SVCBKey, 0, strings.Count(b, \",\")+1)\n\tfor len(b) > 0 {\n\t\tvar key string\n\t\tkey, b, _ = strings.Cut(b, \",\")\n\t\tcodes = append(codes, svcbStringToKey(key))\n\t}\n\ts.Code = codes\n\treturn nil\n}\n\nfunc (s *SVCBMandatory) len() int {\n\treturn 2 * len(s.Code)\n}\n\nfunc (s *SVCBMandatory) copy() SVCBKeyValue {\n\treturn &SVCBMandatory{cloneSlice(s.Code)}\n}\n\n// SVCBAlpn pair is used to list supported connection protocols.\n// The user of this library must ensure that at least one protocol is listed when alpn is present.\n// Protocol IDs can be found at:\n// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\n// Basic use pattern for creating an alpn option:\n//\n//\th := new(dns.HTTPS)\n//\th.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}\n//\te := new(dns.SVCBAlpn)\n//\te.Alpn = []string{\"h2\", \"http/1.1\"}\n//\th.Value = append(h.Value, e)\ntype SVCBAlpn struct {\n\tAlpn []string\n}\n\nfunc (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }\n\nfunc (s *SVCBAlpn) String() string {\n\t// An ALPN value is a comma-separated list of values, each of which can be\n\t// an arbitrary binary value. In order to allow parsing, the comma and\n\t// backslash characters are themselves escaped.\n\t//\n\t// However, this escaping is done in addition to the normal escaping which\n\t// happens in zone files, meaning that these values must be\n\t// double-escaped. This looks terrible, so if you see a never-ending\n\t// sequence of backslash in a zone file this may be why.\n\t//\n\t// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1\n\tvar str strings.Builder\n\tfor i, alpn := range s.Alpn {\n\t\t// 4*len(alpn) is the worst case where we escape every character in the alpn as \\123, plus 1 byte for the ',' separating the alpn from others\n\t\tstr.Grow(4*len(alpn) + 1)\n\t\tif i > 0 {\n\t\t\tstr.WriteByte(',')\n\t\t}\n\t\tfor j := 0; j < len(alpn); j++ {\n\t\t\te := alpn[j]\n\t\t\tif ' ' > e || e > '~' {\n\t\t\t\tstr.WriteString(escapeByte(e))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch e {\n\t\t\t// We escape a few characters which may confuse humans or parsers.\n\t\t\tcase '\"', ';', ' ':\n\t\t\t\tstr.WriteByte('\\\\')\n\t\t\t\tstr.WriteByte(e)\n\t\t\t// The comma and backslash characters themselves must be\n\t\t\t// doubly-escaped. We use `\\\\` for the first backslash and\n\t\t\t// the escaped numeric value for the other value. We especially\n\t\t\t// don't want a comma in the output.\n\t\t\tcase ',':\n\t\t\t\tstr.WriteString(`\\\\\\044`)\n\t\t\tcase '\\\\':\n\t\t\t\tstr.WriteString(`\\\\\\092`)\n\t\t\tdefault:\n\t\t\t\tstr.WriteByte(e)\n\t\t\t}\n\t\t}\n\t}\n\treturn str.String()\n}\n\nfunc (s *SVCBAlpn) pack() ([]byte, error) {\n\t// Liberally estimate the size of an alpn as 10 octets\n\tb := make([]byte, 0, 10*len(s.Alpn))\n\tfor _, e := range s.Alpn {\n\t\tif e == \"\" {\n\t\t\treturn nil, errors.New(\"bad svcbalpn: empty alpn-id\")\n\t\t}\n\t\tif len(e) > 255 {\n\t\t\treturn nil, errors.New(\"bad svcbalpn: alpn-id too long\")\n\t\t}\n\t\tb = append(b, byte(len(e)))\n\t\tb = append(b, e...)\n\t}\n\treturn b, nil\n}\n\nfunc (s *SVCBAlpn) unpack(b []byte) error {\n\t// Estimate the size of the smallest alpn as 4 bytes\n\talpn := make([]string, 0, len(b)/4)\n\tfor i := 0; i < len(b); {\n\t\tlength := int(b[i])\n\t\ti++\n\t\tif i+length > len(b) {\n\t\t\treturn errors.New(\"bad svcbalpn: alpn array overflowing\")\n\t\t}\n\t\talpn = append(alpn, string(b[i:i+length]))\n\t\ti += length\n\t}\n\ts.Alpn = alpn\n\treturn nil\n}\n\nfunc (s *SVCBAlpn) parse(b string) error {\n\tif len(b) == 0 {\n\t\ts.Alpn = []string{}\n\t\treturn nil\n\t}\n\n\talpn := []string{}\n\ta := []byte{}\n\tfor p := 0; p < len(b); {\n\t\tc, q := nextByte(b, p)\n\t\tif q == 0 {\n\t\t\treturn errors.New(\"bad svcbalpn: unterminated escape\")\n\t\t}\n\t\tp += q\n\t\t// If we find a comma, we have finished reading an alpn.\n\t\tif c == ',' {\n\t\t\tif len(a) == 0 {\n\t\t\t\treturn errors.New(\"bad svcbalpn: empty protocol identifier\")\n\t\t\t}\n\t\t\talpn = append(alpn, string(a))\n\t\t\ta = []byte{}\n\t\t\tcontinue\n\t\t}\n\t\t// If it's a backslash, we need to handle a comma-separated list.\n\t\tif c == '\\\\' {\n\t\t\tdc, dq := nextByte(b, p)\n\t\t\tif dq == 0 {\n\t\t\t\treturn errors.New(\"bad svcbalpn: unterminated escape decoding comma-separated list\")\n\t\t\t}\n\t\t\tif dc != '\\\\' && dc != ',' {\n\t\t\t\treturn errors.New(\"bad svcbalpn: bad escaped character decoding comma-separated list\")\n\t\t\t}\n\t\t\tp += dq\n\t\t\tc = dc\n\t\t}\n\t\ta = append(a, c)\n\t}\n\t// Add the final alpn.\n\tif len(a) == 0 {\n\t\treturn errors.New(\"bad svcbalpn: last protocol identifier empty\")\n\t}\n\ts.Alpn = append(alpn, string(a))\n\treturn nil\n}\n\nfunc (s *SVCBAlpn) len() int {\n\tvar l int\n\tfor _, e := range s.Alpn {\n\t\tl += 1 + len(e)\n\t}\n\treturn l\n}\n\nfunc (s *SVCBAlpn) copy() SVCBKeyValue {\n\treturn &SVCBAlpn{cloneSlice(s.Alpn)}\n}\n\n// SVCBNoDefaultAlpn pair signifies no support for default connection protocols.\n// Should be used in conjunction with alpn.\n// Basic use pattern for creating a no-default-alpn option:\n//\n//\ts := &dns.SVCB{Hdr: dns.RR_Header{Name: \".\", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}\n//\tt := new(dns.SVCBAlpn)\n//\tt.Alpn = []string{\"xmpp-client\"}\n//\ts.Value = append(s.Value, t)\n//\te := new(dns.SVCBNoDefaultAlpn)\n//\ts.Value = append(s.Value, e)\ntype SVCBNoDefaultAlpn struct{}\n\nfunc (*SVCBNoDefaultAlpn) Key() SVCBKey          { return SVCB_NO_DEFAULT_ALPN }\nfunc (*SVCBNoDefaultAlpn) copy() SVCBKeyValue    { return &SVCBNoDefaultAlpn{} }\nfunc (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil }\nfunc (*SVCBNoDefaultAlpn) String() string        { return \"\" }\nfunc (*SVCBNoDefaultAlpn) len() int              { return 0 }\n\nfunc (*SVCBNoDefaultAlpn) unpack(b []byte) error {\n\tif len(b) != 0 {\n\t\treturn errors.New(\"bad svcbnodefaultalpn: no-default-alpn must have no value\")\n\t}\n\treturn nil\n}\n\nfunc (*SVCBNoDefaultAlpn) parse(b string) error {\n\tif b != \"\" {\n\t\treturn errors.New(\"bad svcbnodefaultalpn: no-default-alpn must have no value\")\n\t}\n\treturn nil\n}\n\n// SVCBPort pair defines the port for connection.\n// Basic use pattern for creating a port option:\n//\n//\ts := &dns.SVCB{Hdr: dns.RR_Header{Name: \".\", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}\n//\te := new(dns.SVCBPort)\n//\te.Port = 80\n//\ts.Value = append(s.Value, e)\ntype SVCBPort struct {\n\tPort uint16\n}\n\nfunc (*SVCBPort) Key() SVCBKey         { return SVCB_PORT }\nfunc (*SVCBPort) len() int             { return 2 }\nfunc (s *SVCBPort) String() string     { return strconv.FormatUint(uint64(s.Port), 10) }\nfunc (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} }\n\nfunc (s *SVCBPort) unpack(b []byte) error {\n\tif len(b) != 2 {\n\t\treturn errors.New(\"bad svcbport: port length is not exactly 2 octets\")\n\t}\n\ts.Port = binary.BigEndian.Uint16(b)\n\treturn nil\n}\n\nfunc (s *SVCBPort) pack() ([]byte, error) {\n\tb := make([]byte, 2)\n\tbinary.BigEndian.PutUint16(b, s.Port)\n\treturn b, nil\n}\n\nfunc (s *SVCBPort) parse(b string) error {\n\tport, err := strconv.ParseUint(b, 10, 16)\n\tif err != nil {\n\t\treturn errors.New(\"bad svcbport: port out of range\")\n\t}\n\ts.Port = uint16(port)\n\treturn nil\n}\n\n// SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections\n// if A and AAAA record responses for SVCB's Target domain haven't been received.\n// In that case, optionally, A and AAAA requests can be made, after which the connection\n// to the hinted IP address may be terminated and a new connection may be opened.\n// Basic use pattern for creating an ipv4hint option:\n//\n//\t\th := new(dns.HTTPS)\n//\t\th.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}\n//\t\te := new(dns.SVCBIPv4Hint)\n//\t\te.Hint = []net.IP{net.IPv4(1,1,1,1).To4()}\n//\n//\t Or\n//\n//\t\te.Hint = []net.IP{net.ParseIP(\"1.1.1.1\").To4()}\n//\t\th.Value = append(h.Value, e)\ntype SVCBIPv4Hint struct {\n\tHint []net.IP\n}\n\nfunc (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT }\nfunc (s *SVCBIPv4Hint) len() int   { return 4 * len(s.Hint) }\n\nfunc (s *SVCBIPv4Hint) pack() ([]byte, error) {\n\tb := make([]byte, 0, 4*len(s.Hint))\n\tfor _, e := range s.Hint {\n\t\tx := e.To4()\n\t\tif x == nil {\n\t\t\treturn nil, errors.New(\"bad svcbipv4hint: expected ipv4, hint is ipv6\")\n\t\t}\n\t\tb = append(b, x...)\n\t}\n\treturn b, nil\n}\n\nfunc (s *SVCBIPv4Hint) unpack(b []byte) error {\n\tif len(b) == 0 || len(b)%4 != 0 {\n\t\treturn errors.New(\"bad svcbipv4hint: ipv4 address byte array length is not a multiple of 4\")\n\t}\n\tb = cloneSlice(b)\n\tx := make([]net.IP, 0, len(b)/4)\n\tfor i := 0; i < len(b); i += 4 {\n\t\tx = append(x, net.IP(b[i:i+4]))\n\t}\n\ts.Hint = x\n\treturn nil\n}\n\nfunc (s *SVCBIPv4Hint) String() string {\n\tstr := make([]string, len(s.Hint))\n\tfor i, e := range s.Hint {\n\t\tx := e.To4()\n\t\tif x == nil {\n\t\t\treturn \"<nil>\"\n\t\t}\n\t\tstr[i] = x.String()\n\t}\n\treturn strings.Join(str, \",\")\n}\n\nfunc (s *SVCBIPv4Hint) parse(b string) error {\n\tif b == \"\" {\n\t\treturn errors.New(\"bad svcbipv4hint: empty hint\")\n\t}\n\tif strings.Contains(b, \":\") {\n\t\treturn errors.New(\"bad svcbipv4hint: expected ipv4, got ipv6\")\n\t}\n\n\thint := make([]net.IP, 0, strings.Count(b, \",\")+1)\n\tfor len(b) > 0 {\n\t\tvar e string\n\t\te, b, _ = strings.Cut(b, \",\")\n\t\tip := net.ParseIP(e).To4()\n\t\tif ip == nil {\n\t\t\treturn errors.New(\"bad svcbipv4hint: bad ip\")\n\t\t}\n\t\thint = append(hint, ip)\n\t}\n\ts.Hint = hint\n\treturn nil\n}\n\nfunc (s *SVCBIPv4Hint) copy() SVCBKeyValue {\n\thint := make([]net.IP, len(s.Hint))\n\tfor i, ip := range s.Hint {\n\t\thint[i] = cloneSlice(ip)\n\t}\n\treturn &SVCBIPv4Hint{Hint: hint}\n}\n\n// SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].\n// Basic use pattern for creating an ech option:\n//\n//\th := new(dns.HTTPS)\n//\th.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}\n//\te := new(dns.SVCBECHConfig)\n//\te.ECH = []byte{0xfe, 0x08, ...}\n//\th.Value = append(h.Value, e)\ntype SVCBECHConfig struct {\n\tECH []byte // Specifically ECHConfigList including the redundant length prefix\n}\n\nfunc (*SVCBECHConfig) Key() SVCBKey     { return SVCB_ECHCONFIG }\nfunc (s *SVCBECHConfig) String() string { return toBase64(s.ECH) }\nfunc (s *SVCBECHConfig) len() int       { return len(s.ECH) }\n\nfunc (s *SVCBECHConfig) pack() ([]byte, error) {\n\treturn cloneSlice(s.ECH), nil\n}\n\nfunc (s *SVCBECHConfig) copy() SVCBKeyValue {\n\treturn &SVCBECHConfig{cloneSlice(s.ECH)}\n}\n\nfunc (s *SVCBECHConfig) unpack(b []byte) error {\n\ts.ECH = cloneSlice(b)\n\treturn nil\n}\n\nfunc (s *SVCBECHConfig) parse(b string) error {\n\tx, err := fromBase64([]byte(b))\n\tif err != nil {\n\t\treturn errors.New(\"bad svcbech: bad base64 ech\")\n\t}\n\ts.ECH = x\n\treturn nil\n}\n\n// SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections\n// if A and AAAA record responses for SVCB's Target domain haven't been received.\n// In that case, optionally, A and AAAA requests can be made, after which the\n// connection to the hinted IP address may be terminated and a new connection may be opened.\n// Basic use pattern for creating an ipv6hint option:\n//\n//\th := new(dns.HTTPS)\n//\th.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}\n//\te := new(dns.SVCBIPv6Hint)\n//\te.Hint = []net.IP{net.ParseIP(\"2001:db8::1\")}\n//\th.Value = append(h.Value, e)\ntype SVCBIPv6Hint struct {\n\tHint []net.IP\n}\n\nfunc (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT }\nfunc (s *SVCBIPv6Hint) len() int   { return 16 * len(s.Hint) }\n\nfunc (s *SVCBIPv6Hint) pack() ([]byte, error) {\n\tb := make([]byte, 0, 16*len(s.Hint))\n\tfor _, e := range s.Hint {\n\t\tif len(e) != net.IPv6len || e.To4() != nil {\n\t\t\treturn nil, errors.New(\"bad svcbipv6hint: expected ipv6, hint is ipv4\")\n\t\t}\n\t\tb = append(b, e...)\n\t}\n\treturn b, nil\n}\n\nfunc (s *SVCBIPv6Hint) unpack(b []byte) error {\n\tif len(b) == 0 || len(b)%16 != 0 {\n\t\treturn errors.New(\"bas svcbipv6hint: ipv6 address byte array length not a multiple of 16\")\n\t}\n\tb = cloneSlice(b)\n\tx := make([]net.IP, 0, len(b)/16)\n\tfor i := 0; i < len(b); i += 16 {\n\t\tip := net.IP(b[i : i+16])\n\t\tif ip.To4() != nil {\n\t\t\treturn errors.New(\"bad svcbipv6hint: expected ipv6, got ipv4\")\n\t\t}\n\t\tx = append(x, ip)\n\t}\n\ts.Hint = x\n\treturn nil\n}\n\nfunc (s *SVCBIPv6Hint) String() string {\n\tstr := make([]string, len(s.Hint))\n\tfor i, e := range s.Hint {\n\t\tif x := e.To4(); x != nil {\n\t\t\treturn \"<nil>\"\n\t\t}\n\t\tstr[i] = e.String()\n\t}\n\treturn strings.Join(str, \",\")\n}\n\nfunc (s *SVCBIPv6Hint) parse(b string) error {\n\tif b == \"\" {\n\t\treturn errors.New(\"bad svcbipv6hint: empty hint\")\n\t}\n\n\thint := make([]net.IP, 0, strings.Count(b, \",\")+1)\n\tfor len(b) > 0 {\n\t\tvar e string\n\t\te, b, _ = strings.Cut(b, \",\")\n\t\tip := net.ParseIP(e)\n\t\tif ip == nil {\n\t\t\treturn errors.New(\"bad svcbipv6hint: bad ip\")\n\t\t}\n\t\tif ip.To4() != nil {\n\t\t\treturn errors.New(\"bad svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6\")\n\t\t}\n\t\thint = append(hint, ip)\n\t}\n\ts.Hint = hint\n\treturn nil\n}\n\nfunc (s *SVCBIPv6Hint) copy() SVCBKeyValue {\n\thint := make([]net.IP, len(s.Hint))\n\tfor i, ip := range s.Hint {\n\t\thint[i] = cloneSlice(ip)\n\t}\n\treturn &SVCBIPv6Hint{Hint: hint}\n}\n\n// SVCBDoHPath pair is used to indicate the URI template that the\n// clients may use to construct a DNS over HTTPS URI.\n//\n// See RFC 9461 (https://datatracker.ietf.org/doc/html/rfc9461)\n// and RFC 9462 (https://datatracker.ietf.org/doc/html/rfc9462).\n//\n// A basic example of using the dohpath option together with the alpn\n// option to indicate support for DNS over HTTPS on a certain path:\n//\n//\ts := new(dns.SVCB)\n//\ts.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}\n//\te := new(dns.SVCBAlpn)\n//\te.Alpn = []string{\"h2\", \"h3\"}\n//\tp := new(dns.SVCBDoHPath)\n//\tp.Template = \"/dns-query{?dns}\"\n//\ts.Value = append(s.Value, e, p)\n//\n// The parsing currently doesn't validate that Template is a valid\n// RFC 6570 URI template.\ntype SVCBDoHPath struct {\n\tTemplate string\n}\n\nfunc (*SVCBDoHPath) Key() SVCBKey            { return SVCB_DOHPATH }\nfunc (s *SVCBDoHPath) String() string        { return svcbParamToStr([]byte(s.Template)) }\nfunc (s *SVCBDoHPath) len() int              { return len(s.Template) }\nfunc (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil }\n\nfunc (s *SVCBDoHPath) unpack(b []byte) error {\n\ts.Template = string(b)\n\treturn nil\n}\n\nfunc (s *SVCBDoHPath) parse(b string) error {\n\ttemplate, err := svcbParseParam(b)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"bad svcbdohpath: %w\", err)\n\t}\n\ts.Template = string(template)\n\treturn nil\n}\n\nfunc (s *SVCBDoHPath) copy() SVCBKeyValue {\n\treturn &SVCBDoHPath{\n\t\tTemplate: s.Template,\n\t}\n}\n\n// The \"ohttp\" SvcParamKey is used to indicate that a service described in a SVCB RR\n// can be accessed as a target using an associated gateway.\n// Both the presentation and wire-format values for the \"ohttp\" parameter MUST be empty.\n//\n// See RFC 9460 (https://datatracker.ietf.org/doc/html/rfc9460/)\n// and RFC 9230 (https://datatracker.ietf.org/doc/html/rfc9230/)\n//\n// A basic example of using the dohpath option together with the alpn\n// option to indicate support for DNS over HTTPS on a certain path:\n//\n//\ts := new(dns.SVCB)\n//\ts.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}\n//\te := new(dns.SVCBAlpn)\n//\te.Alpn = []string{\"h2\", \"h3\"}\n//\tp := new(dns.SVCBOhttp)\n//\ts.Value = append(s.Value, e, p)\ntype SVCBOhttp struct{}\n\nfunc (*SVCBOhttp) Key() SVCBKey          { return SVCB_OHTTP }\nfunc (*SVCBOhttp) copy() SVCBKeyValue    { return &SVCBOhttp{} }\nfunc (*SVCBOhttp) pack() ([]byte, error) { return []byte{}, nil }\nfunc (*SVCBOhttp) String() string        { return \"\" }\nfunc (*SVCBOhttp) len() int              { return 0 }\n\nfunc (*SVCBOhttp) unpack(b []byte) error {\n\tif len(b) != 0 {\n\t\treturn errors.New(\"bad svcbotthp: svcbotthp must have no value\")\n\t}\n\treturn nil\n}\n\nfunc (*SVCBOhttp) parse(b string) error {\n\tif b != \"\" {\n\t\treturn errors.New(\"bad svcbotthp: svcbotthp must have no value\")\n\t}\n\treturn nil\n}\n\n// SVCBLocal pair is intended for experimental/private use. The key is recommended\n// to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].\n// Basic use pattern for creating a keyNNNNN option:\n//\n//\th := new(dns.HTTPS)\n//\th.Hdr = dns.RR_Header{Name: \".\", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}\n//\te := new(dns.SVCBLocal)\n//\te.KeyCode = 65400\n//\te.Data = []byte(\"abc\")\n//\th.Value = append(h.Value, e)\ntype SVCBLocal struct {\n\tKeyCode SVCBKey // Never 65535 or any assigned keys.\n\tData    []byte  // All byte sequences are allowed.\n}\n\nfunc (s *SVCBLocal) Key() SVCBKey          { return s.KeyCode }\nfunc (s *SVCBLocal) String() string        { return svcbParamToStr(s.Data) }\nfunc (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil }\nfunc (s *SVCBLocal) len() int              { return len(s.Data) }\n\nfunc (s *SVCBLocal) unpack(b []byte) error {\n\ts.Data = cloneSlice(b)\n\treturn nil\n}\n\nfunc (s *SVCBLocal) parse(b string) error {\n\tdata, err := svcbParseParam(b)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"bad svcblocal: svcb private/experimental key %w\", err)\n\t}\n\ts.Data = data\n\treturn nil\n}\n\nfunc (s *SVCBLocal) copy() SVCBKeyValue {\n\treturn &SVCBLocal{s.KeyCode, cloneSlice(s.Data)}\n}\n\nfunc (rr *SVCB) String() string {\n\ts := rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.Priority)) + \" \" +\n\t\tsprintName(rr.Target)\n\tfor _, e := range rr.Value {\n\t\ts += \" \" + e.Key().String() + \"=\\\"\" + e.String() + \"\\\"\"\n\t}\n\treturn s\n}\n\n// areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their\n// copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function.\nfunc areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {\n\ta = cloneSlice(a)\n\tb = cloneSlice(b)\n\tsort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() })\n\tsort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() })\n\tfor i, e := range a {\n\t\tif e.Key() != b[i].Key() {\n\t\t\treturn false\n\t\t}\n\t\tb1, err1 := e.pack()\n\t\tb2, err2 := b[i].pack()\n\t\tif err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string.\nfunc svcbParamToStr(s []byte) string {\n\tvar str strings.Builder\n\tstr.Grow(4 * len(s))\n\tfor _, e := range s {\n\t\tif ' ' <= e && e <= '~' {\n\t\t\tswitch e {\n\t\t\tcase '\"', ';', ' ', '\\\\':\n\t\t\t\tstr.WriteByte('\\\\')\n\t\t\t\tstr.WriteByte(e)\n\t\t\tdefault:\n\t\t\t\tstr.WriteByte(e)\n\t\t\t}\n\t\t} else {\n\t\t\tstr.WriteString(escapeByte(e))\n\t\t}\n\t}\n\treturn str.String()\n}\n\n// svcbParseParam parses a DNS presentation-format string into an SVCB parameter value.\nfunc svcbParseParam(b string) ([]byte, error) {\n\tdata := make([]byte, 0, len(b))\n\tfor i := 0; i < len(b); {\n\t\tif b[i] != '\\\\' {\n\t\t\tdata = append(data, b[i])\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\t\tif i+1 == len(b) {\n\t\t\treturn nil, errors.New(\"escape unterminated\")\n\t\t}\n\t\tif isDigit(b[i+1]) {\n\t\t\tif i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {\n\t\t\t\ta, err := strconv.ParseUint(b[i+1:i+4], 10, 8)\n\t\t\t\tif err == nil {\n\t\t\t\t\ti += 4\n\t\t\t\t\tdata = append(data, byte(a))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, errors.New(\"bad escaped octet\")\n\t\t} else {\n\t\t\tdata = append(data, b[i+1])\n\t\t\ti += 2\n\t\t}\n\t}\n\treturn data, nil\n}\n"
  },
  {
    "path": "svcb_test.go",
    "content": "package dns\n\nimport (\n\t\"testing\"\n)\n\n// This tests everything valid about SVCB but parsing.\n// Parsing tests belong to parse_test.go.\nfunc TestSVCB(t *testing.T) {\n\tsvcbs := []struct {\n\t\tkey  string\n\t\tdata string\n\t}{\n\t\t{`mandatory`, `alpn,key65000`},\n\t\t{`alpn`, `h2,h2c`},\n\t\t{`port`, `499`},\n\t\t{`ipv4hint`, `3.4.3.2,1.1.1.1`},\n\t\t{`no-default-alpn`, ``},\n\t\t{`ipv6hint`, `1::4:4:4:4,1::3:3:3:3`},\n\t\t{`ech`, `YUdWc2JHOD0=`},\n\t\t{`dohpath`, `/dns-query{?dns}`},\n\t\t{`key65000`, `4\\ 3`},\n\t\t{`key65001`, `\\\"\\ `},\n\t\t{`key65002`, ``},\n\t\t{`key65003`, `=\\\"\\\"`},\n\t\t{`key65004`, `\\254\\ \\ \\030\\000`},\n\t\t{`ohttp`, ``},\n\t}\n\n\tfor _, o := range svcbs {\n\t\tkeyCode := svcbStringToKey(o.key)\n\t\tkv := makeSVCBKeyValue(keyCode)\n\t\tif kv == nil {\n\t\t\tt.Error(\"failed to parse svc key: \", o.key)\n\t\t\tcontinue\n\t\t}\n\t\tif kv.Key() != keyCode {\n\t\t\tt.Error(\"key constant is not in sync: \", keyCode)\n\t\t\tcontinue\n\t\t}\n\t\terr := kv.parse(o.data)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse svc pair: \", o.key)\n\t\t\tcontinue\n\t\t}\n\t\tb, err := kv.pack()\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to pack value of svc pair: \", o.key, err)\n\t\t\tcontinue\n\t\t}\n\t\tif len(b) != int(kv.len()) {\n\t\t\tt.Errorf(\"expected packed svc value %s to be of length %d but got %d\", o.key, int(kv.len()), len(b))\n\t\t}\n\t\terr = kv.unpack(b)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to unpack value of svc pair: \", o.key, err)\n\t\t\tcontinue\n\t\t}\n\t\tif str := kv.String(); str != o.data {\n\t\t\tt.Errorf(\"`%s' should be equal to\\n`%s', but is     `%s'\", o.key, o.data, str)\n\t\t}\n\t}\n}\n\nfunc TestDecodeBadSVCB(t *testing.T) {\n\tsvcbs := []struct {\n\t\tkey  SVCBKey\n\t\tdata []byte\n\t}{\n\t\t{\n\t\t\tkey:  SVCB_ALPN,\n\t\t\tdata: []byte{3, 0, 0}, // There aren't three octets after 3\n\t\t},\n\t\t{\n\t\t\tkey:  SVCB_NO_DEFAULT_ALPN,\n\t\t\tdata: []byte{0},\n\t\t},\n\t\t{\n\t\t\tkey:  SVCB_PORT,\n\t\t\tdata: []byte{},\n\t\t},\n\t\t{\n\t\t\tkey:  SVCB_IPV4HINT,\n\t\t\tdata: []byte{0, 0, 0},\n\t\t},\n\t\t{\n\t\t\tkey:  SVCB_IPV6HINT,\n\t\t\tdata: []byte{0, 0, 0},\n\t\t},\n\t\t{\n\t\t\tkey:  SVCB_OHTTP,\n\t\t\tdata: []byte{0},\n\t\t},\n\t}\n\tfor _, o := range svcbs {\n\t\terr := makeSVCBKeyValue(SVCBKey(o.key)).unpack(o.data)\n\t\tif err == nil {\n\t\t\tt.Error(\"accepted invalid svc value with key \", SVCBKey(o.key).String())\n\t\t}\n\t}\n}\n\nfunc TestPresentationSVCBAlpn(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"h2\":                \"h2\",\n\t\t\"http\":              \"http\",\n\t\t\"\\xfa\":              `\\250`,\n\t\t\"some\\\"other,chars\": `some\\\"other\\\\\\044chars`,\n\t}\n\tfor input, want := range tests {\n\t\te := new(SVCBAlpn)\n\t\te.Alpn = []string{input}\n\t\tif e.String() != want {\n\t\t\tt.Errorf(\"improper conversion with String(), wanted %v got %v\", want, e.String())\n\t\t}\n\t}\n}\n\nfunc TestSVCBAlpn(t *testing.T) {\n\ttests := map[string][]string{\n\t\t`. 1 IN SVCB 10 one.test. alpn=h2`:                                         {\"h2\"},\n\t\t`. 2 IN SVCB 20 two.test. alpn=h2,h3-19`:                                   {\"h2\", \"h3-19\"},\n\t\t`. 3 IN SVCB 30 three.test. alpn=\"f\\\\\\\\oo\\\\,bar,h2\"`:                       {`f\\oo,bar`, \"h2\"},\n\t\t`. 4 IN SVCB 40 four.test. alpn=\"part1,part2,part3\\\\,part4\\\\\\\\\"`:           {\"part1\", \"part2\", `part3,part4\\`},\n\t\t`. 5 IN SVCB 50 five.test. alpn=part1\\,\\p\\a\\r\\t2\\044part3\\092,part4\\092\\\\`: {\"part1\", \"part2\", `part3,part4\\`},\n\t}\n\tfor s, v := range tests {\n\t\trr, err := NewRR(s)\n\t\tif err != nil {\n\t\t\tt.Error(\"failed to parse RR: \", err)\n\t\t\tcontinue\n\t\t}\n\t\talpn := rr.(*SVCB).Value[0].(*SVCBAlpn).Alpn\n\t\tif len(v) != len(alpn) {\n\t\t\tt.Fatalf(\"parsing alpn failed, wanted %v got %v\", v, alpn)\n\t\t}\n\t\tfor i := range v {\n\t\t\tif v[i] != alpn[i] {\n\t\t\t\tt.Fatalf(\"parsing alpn failed, wanted %v got %v\", v, alpn)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCompareSVCB(t *testing.T) {\n\tval1 := []SVCBKeyValue{\n\t\t&SVCBPort{\n\t\t\tPort: 117,\n\t\t},\n\t\t&SVCBAlpn{\n\t\t\tAlpn: []string{\"h2\", \"h3\"},\n\t\t},\n\t}\n\tval2 := []SVCBKeyValue{\n\t\t&SVCBAlpn{\n\t\t\tAlpn: []string{\"h2\", \"h3\"},\n\t\t},\n\t\t&SVCBPort{\n\t\t\tPort: 117,\n\t\t},\n\t}\n\tif !areSVCBPairArraysEqual(val1, val2) {\n\t\tt.Error(\"svcb pairs were compared without sorting\")\n\t}\n\tif val1[0].Key() != SVCB_PORT || val2[0].Key() != SVCB_ALPN {\n\t\tt.Error(\"original svcb pairs were reordered during comparison\")\n\t}\n}\n"
  },
  {
    "path": "tlsa.go",
    "content": "package dns\n\nimport (\n\t\"crypto/x509\"\n\t\"net\"\n\t\"strconv\"\n)\n\n// Sign creates a TLSA record from an SSL certificate.\nfunc (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {\n\tr.Hdr.Rrtype = TypeTLSA\n\tr.Usage = uint8(usage)\n\tr.Selector = uint8(selector)\n\tr.MatchingType = uint8(matchingType)\n\n\tr.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)\n\treturn err\n}\n\n// Verify verifies a TLSA record against an SSL certificate. If it is OK\n// a nil error is returned.\nfunc (r *TLSA) Verify(cert *x509.Certificate) error {\n\tc, err := CertificateToDANE(r.Selector, r.MatchingType, cert)\n\tif err != nil {\n\t\treturn err // Not also ErrSig?\n\t}\n\tif r.Certificate == c {\n\t\treturn nil\n\t}\n\treturn ErrSig // ErrSig, really?\n}\n\n// TLSAName returns the ownername of a TLSA resource record as per the\n// rules specified in RFC 6698, Section 3.\nfunc TLSAName(name, service, network string) (string, error) {\n\tif !IsFqdn(name) {\n\t\treturn \"\", ErrFqdn\n\t}\n\tp, err := net.LookupPort(network, service)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn \"_\" + strconv.Itoa(p) + \"._\" + network + \".\" + name, nil\n}\n"
  },
  {
    "path": "tmpdir_darwin_test.go",
    "content": "//go:build darwin\n\npackage dns\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// tempDir creates a temporary directory for tests and returns a file path as\n// a result of concatenation of said temporary directory path and provided filename.\n// The reason for this is to work around some limitations in socket file name\n// lengths on darwin.\n//\n// Ref:\n// - https://github.com/golang/go/blob/go1.20.2/src/syscall/ztypes_darwin_arm64.go#L178\n// - https://github.com/golang/go/blob/go1.20.2/src/syscall/ztypes_linux_arm64.go#L175\nfunc tempFile(t *testing.T, filename string) string {\n\tt.Helper()\n\n\tdir, err := os.MkdirTemp(\"\", strings.ReplaceAll(t.Name(), string(filepath.Separator), \"-\"))\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create temp dir: %v\", err)\n\t}\n\n\treturn filepath.Join(dir, filename)\n}\n"
  },
  {
    "path": "tmpdir_test.go",
    "content": "//go:build !darwin\n\npackage dns\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n)\n\n// tempDir creates a temporary directory for tests and returns a file path as\n// a result of concatenation of said temporary directory path and provided filename.\nfunc tempFile(t *testing.T, filename string) string {\n\tt.Helper()\n\n\treturn filepath.Join(t.TempDir(), filename)\n}\n"
  },
  {
    "path": "tools.go",
    "content": "//go:build tools\n// +build tools\n\n// We include our tool dependencies for `go generate` here to ensure they're\n// properly tracked by the go tool. See the Go Wiki for the rationale behind this:\n// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module.\n\npackage dns\n\nimport _ \"golang.org/x/tools/go/packages\"\n"
  },
  {
    "path": "tsig.go",
    "content": "package dns\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha1\"\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"hash\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// HMAC hashing codes. These are transmitted as domain names.\nconst (\n\tHmacSHA1   = \"hmac-sha1.\"\n\tHmacSHA224 = \"hmac-sha224.\"\n\tHmacSHA256 = \"hmac-sha256.\"\n\tHmacSHA384 = \"hmac-sha384.\"\n\tHmacSHA512 = \"hmac-sha512.\"\n\n\tHmacMD5 = \"hmac-md5.sig-alg.reg.int.\" // Deprecated: HmacMD5 is no longer supported.\n)\n\n// TsigProvider provides the API to plug-in a custom TSIG implementation.\ntype TsigProvider interface {\n\t// Generate is passed the DNS message to be signed and the partial TSIG RR. It returns the signature and nil, otherwise an error.\n\tGenerate(msg []byte, t *TSIG) ([]byte, error)\n\t// Verify is passed the DNS message to be verified and the TSIG RR. If the signature is valid it will return nil, otherwise an error.\n\tVerify(msg []byte, t *TSIG) error\n}\n\ntype tsigHMACProvider string\n\nfunc (key tsigHMACProvider) Generate(msg []byte, t *TSIG) ([]byte, error) {\n\t// If we barf here, the caller is to blame\n\trawsecret, err := fromBase64([]byte(key))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar h hash.Hash\n\tswitch CanonicalName(t.Algorithm) {\n\tcase HmacSHA1:\n\t\th = hmac.New(sha1.New, rawsecret)\n\tcase HmacSHA224:\n\t\th = hmac.New(sha256.New224, rawsecret)\n\tcase HmacSHA256:\n\t\th = hmac.New(sha256.New, rawsecret)\n\tcase HmacSHA384:\n\t\th = hmac.New(sha512.New384, rawsecret)\n\tcase HmacSHA512:\n\t\th = hmac.New(sha512.New, rawsecret)\n\tdefault:\n\t\treturn nil, ErrKeyAlg\n\t}\n\th.Write(msg)\n\treturn h.Sum(nil), nil\n}\n\nfunc (key tsigHMACProvider) Verify(msg []byte, t *TSIG) error {\n\tb, err := key.Generate(msg, t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmac, err := hex.DecodeString(t.MAC)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !hmac.Equal(b, mac) {\n\t\treturn ErrSig\n\t}\n\treturn nil\n}\n\ntype tsigSecretProvider map[string]string\n\nfunc (ts tsigSecretProvider) Generate(msg []byte, t *TSIG) ([]byte, error) {\n\tkey, ok := ts[t.Hdr.Name]\n\tif !ok {\n\t\treturn nil, ErrSecret\n\t}\n\treturn tsigHMACProvider(key).Generate(msg, t)\n}\n\nfunc (ts tsigSecretProvider) Verify(msg []byte, t *TSIG) error {\n\tkey, ok := ts[t.Hdr.Name]\n\tif !ok {\n\t\treturn ErrSecret\n\t}\n\treturn tsigHMACProvider(key).Verify(msg, t)\n}\n\n// TSIG is the RR the holds the transaction signature of a message.\n// See RFC 2845 and RFC 4635.\ntype TSIG struct {\n\tHdr        RR_Header\n\tAlgorithm  string `dns:\"domain-name\"`\n\tTimeSigned uint64 `dns:\"uint48\"`\n\tFudge      uint16\n\tMACSize    uint16\n\tMAC        string `dns:\"size-hex:MACSize\"`\n\tOrigId     uint16\n\tError      uint16\n\tOtherLen   uint16\n\tOtherData  string `dns:\"size-hex:OtherLen\"`\n}\n\n// TSIG has no official presentation format, but this will suffice.\n\nfunc (rr *TSIG) String() string {\n\ts := \"\\n;; TSIG PSEUDOSECTION:\\n; \" // add another semi-colon to signify TSIG does not have a presentation format\n\ts += rr.Hdr.String() +\n\t\t\" \" + rr.Algorithm +\n\t\t\" \" + tsigTimeToString(rr.TimeSigned) +\n\t\t\" \" + strconv.Itoa(int(rr.Fudge)) +\n\t\t\" \" + strconv.Itoa(int(rr.MACSize)) +\n\t\t\" \" + strings.ToUpper(rr.MAC) +\n\t\t\" \" + strconv.Itoa(int(rr.OrigId)) +\n\t\t\" \" + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR\n\t\t\" \" + strconv.Itoa(int(rr.OtherLen)) +\n\t\t\" \" + rr.OtherData\n\treturn s\n}\n\nfunc (*TSIG) parse(c *zlexer, origin string) *ParseError {\n\treturn &ParseError{err: \"TSIG records do not have a presentation format\"}\n}\n\n// The following values must be put in wireformat, so that the MAC can be calculated.\n// RFC 2845, section 3.4.2. TSIG Variables.\ntype tsigWireFmt struct {\n\t// From RR_Header\n\tName  string `dns:\"domain-name\"`\n\tClass uint16\n\tTtl   uint32\n\t// Rdata of the TSIG\n\tAlgorithm  string `dns:\"domain-name\"`\n\tTimeSigned uint64 `dns:\"uint48\"`\n\tFudge      uint16\n\t// MACSize, MAC and OrigId excluded\n\tError     uint16\n\tOtherLen  uint16\n\tOtherData string `dns:\"size-hex:OtherLen\"`\n}\n\n// If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC\ntype macWireFmt struct {\n\tMACSize uint16\n\tMAC     string `dns:\"size-hex:MACSize\"`\n}\n\n// 3.3. Time values used in TSIG calculations\ntype timerWireFmt struct {\n\tTimeSigned uint64 `dns:\"uint48\"`\n\tFudge      uint16\n}\n\n// TsigGenerate fills out the TSIG record attached to the message.\n// The message should contain a \"stub\" TSIG RR with the algorithm, key name\n// (owner name of the RR), time fudge (defaults to 300 seconds) and the current\n// time The TSIG MAC is saved in that Tsig RR. When TsigGenerate is called for\n// the first time requestMAC should be set to the empty string and timersOnly to\n// false.\nfunc TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {\n\treturn TsigGenerateWithProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly)\n}\n\n// TsigGenerateWithProvider is similar to TsigGenerate, but allows for a custom TsigProvider.\nfunc TsigGenerateWithProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) {\n\tif m.IsTsig() == nil {\n\t\tpanic(\"dns: TSIG not last RR in additional\")\n\t}\n\n\trr := m.Extra[len(m.Extra)-1].(*TSIG)\n\tm.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg\n\tmbuf, err := m.Pack()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tbuf, err := tsigBuffer(mbuf, rr, requestMAC, timersOnly)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tt := new(TSIG)\n\t// Copy all TSIG fields except MAC, its size, and time signed which are filled when signing.\n\t*t = *rr\n\tt.TimeSigned = 0\n\tt.MAC = \"\"\n\tt.MACSize = 0\n\n\t// Sign unless there is a key or MAC validation error (RFC 8945 5.3.2)\n\tif rr.Error != RcodeBadKey && rr.Error != RcodeBadSig {\n\t\tmac, err := provider.Generate(buf, rr)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", err\n\t\t}\n\t\tt.TimeSigned = rr.TimeSigned\n\t\tt.MAC = hex.EncodeToString(mac)\n\t\tt.MACSize = uint16(len(t.MAC) / 2) // Size is half!\n\t}\n\n\ttbuf := make([]byte, Len(t))\n\toff, err := PackRR(t, tbuf, 0, nil, false)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tmbuf = append(mbuf, tbuf[:off]...)\n\t// Update the ArCount directly in the buffer.\n\tbinary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))\n\n\treturn mbuf, t.MAC, nil\n}\n\n// TsigVerify verifies the TSIG on a message. If the signature does not\n// validate the returned error contains the cause. If the signature is OK, the\n// error is nil.\nfunc TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {\n\treturn tsigVerify(msg, tsigHMACProvider(secret), requestMAC, timersOnly, uint64(time.Now().Unix()))\n}\n\n// TsigVerifyWithProvider is similar to TsigVerify, but allows for a custom TsigProvider.\nfunc TsigVerifyWithProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error {\n\treturn tsigVerify(msg, provider, requestMAC, timersOnly, uint64(time.Now().Unix()))\n}\n\n// actual implementation of TsigVerify, taking the current time ('now') as a parameter for the convenience of tests.\nfunc tsigVerify(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool, now uint64) error {\n\t// Strip the TSIG from the incoming msg\n\tstripped, tsig, err := stripTsig(msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbuf, err := tsigBuffer(stripped, tsig, requestMAC, timersOnly)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := provider.Verify(buf, tsig); err != nil {\n\t\treturn err\n\t}\n\n\t// Fudge factor works both ways. A message can arrive before it was signed because\n\t// of clock skew.\n\t// We check this after verifying the signature, following draft-ietf-dnsop-rfc2845bis\n\t// instead of RFC2845, in order to prevent a security vulnerability as reported in CVE-2017-3142/3143.\n\tti := now - tsig.TimeSigned\n\tif now < tsig.TimeSigned {\n\t\tti = tsig.TimeSigned - now\n\t}\n\tif uint64(tsig.Fudge) < ti {\n\t\treturn ErrTime\n\t}\n\n\treturn nil\n}\n\n// Create a wiredata buffer for the MAC calculation.\nfunc tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) ([]byte, error) {\n\tvar buf []byte\n\tif rr.TimeSigned == 0 {\n\t\trr.TimeSigned = uint64(time.Now().Unix())\n\t}\n\tif rr.Fudge == 0 {\n\t\trr.Fudge = 300 // Standard (RFC) default.\n\t}\n\n\t// Replace message ID in header with original ID from TSIG\n\tbinary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId)\n\n\tif requestMAC != \"\" {\n\t\tm := new(macWireFmt)\n\t\tm.MACSize = uint16(len(requestMAC) / 2)\n\t\tm.MAC = requestMAC\n\t\tbuf = make([]byte, len(requestMAC)) // long enough\n\t\tn, err := packMacWire(m, buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbuf = buf[:n]\n\t}\n\n\ttsigvar := make([]byte, DefaultMsgSize)\n\tif timersOnly {\n\t\ttsig := new(timerWireFmt)\n\t\ttsig.TimeSigned = rr.TimeSigned\n\t\ttsig.Fudge = rr.Fudge\n\t\tn, err := packTimerWire(tsig, tsigvar)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttsigvar = tsigvar[:n]\n\t} else {\n\t\ttsig := new(tsigWireFmt)\n\t\ttsig.Name = CanonicalName(rr.Hdr.Name)\n\t\ttsig.Class = ClassANY\n\t\ttsig.Ttl = rr.Hdr.Ttl\n\t\ttsig.Algorithm = CanonicalName(rr.Algorithm)\n\t\ttsig.TimeSigned = rr.TimeSigned\n\t\ttsig.Fudge = rr.Fudge\n\t\ttsig.Error = rr.Error\n\t\ttsig.OtherLen = rr.OtherLen\n\t\ttsig.OtherData = rr.OtherData\n\t\tn, err := packTsigWire(tsig, tsigvar)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttsigvar = tsigvar[:n]\n\t}\n\n\tif requestMAC != \"\" {\n\t\tx := append(buf, msgbuf...)\n\t\tbuf = append(x, tsigvar...)\n\t} else {\n\t\tbuf = append(msgbuf, tsigvar...)\n\t}\n\treturn buf, nil\n}\n\n// Strip the TSIG from the raw message.\nfunc stripTsig(msg []byte) ([]byte, *TSIG, error) {\n\t// Copied from msg.go's Unpack() Header, but modified.\n\tvar (\n\t\tdh  Header\n\t\terr error\n\t)\n\toff, tsigoff := 0, 0\n\n\tif dh, off, err = unpackMsgHdr(msg, off); err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif dh.Arcount == 0 {\n\t\treturn nil, nil, ErrNoSig\n\t}\n\n\t// Rcode, see msg.go Unpack()\n\tif int(dh.Bits&0xF) == RcodeNotAuth {\n\t\treturn nil, nil, ErrAuth\n\t}\n\n\tfor i := 0; i < int(dh.Qdcount); i++ {\n\t\t_, off, err = unpackQuestion(msg, off)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\n\t_, off, err = unpackRRslice(int(dh.Ancount), msg, off)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\t_, off, err = unpackRRslice(int(dh.Nscount), msg, off)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\trr := new(TSIG)\n\tvar extra RR\n\tfor i := 0; i < int(dh.Arcount); i++ {\n\t\ttsigoff = off\n\t\textra, off, err = UnpackRR(msg, off)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tif extra.Header().Rrtype == TypeTSIG {\n\t\t\trr = extra.(*TSIG)\n\t\t\t// Adjust Arcount.\n\t\t\tarcount := binary.BigEndian.Uint16(msg[10:])\n\t\t\tbinary.BigEndian.PutUint16(msg[10:], arcount-1)\n\t\t\tbreak\n\t\t}\n\t}\n\tif rr == nil {\n\t\treturn nil, nil, ErrNoSig\n\t}\n\treturn msg[:tsigoff], rr, nil\n}\n\n// Translate the TSIG time signed into a date. There is no\n// need for RFC1982 calculations as this date is 48 bits.\nfunc tsigTimeToString(t uint64) string {\n\tti := time.Unix(int64(t), 0).UTC()\n\treturn ti.Format(\"20060102150405\")\n}\n\nfunc packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {\n\t// copied from zmsg.go TSIG packing\n\t// RR_Header\n\toff, err := PackDomainName(tw.Name, msg, 0, nil, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(tw.Class, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(tw.Ttl, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\n\toff, err = PackDomainName(tw.Algorithm, msg, off, nil, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint48(tw.TimeSigned, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(tw.Fudge, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\n\toff, err = packUint16(tw.Error, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(tw.OtherLen, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(tw.OtherData, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc packMacWire(mw *macWireFmt, msg []byte) (int, error) {\n\toff, err := packUint16(mw.MACSize, msg, 0)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(mw.MAC, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {\n\toff, err := packUint48(tw.TimeSigned, msg, 0)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(tw.Fudge, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n"
  },
  {
    "path": "tsig_test.go",
    "content": "package dns\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc newTsig(algo string) *Msg {\n\tm := new(Msg)\n\tm.SetQuestion(\"example.org.\", TypeA)\n\tm.SetTsig(\"example.\", algo, 300, time.Now().Unix())\n\treturn m\n}\n\nfunc TestTsig(t *testing.T) {\n\tm := newTsig(HmacSHA256)\n\tbuf, _, err := TsigGenerate(m, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = TsigVerify(buf, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// TSIG accounts for ID substitution. This means if the message ID is\n\t// changed by a forwarder, we should still be able to verify the TSIG.\n\tm = newTsig(HmacSHA256)\n\tbuf, _, err = TsigGenerate(m, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbinary.BigEndian.PutUint16(buf[0:2], 42)\n\terr = TsigVerify(buf, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTsigCase(t *testing.T) {\n\tm := newTsig(strings.ToUpper(HmacSHA256))\n\tbuf, _, err := TsigGenerate(m, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = TsigVerify(buf, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTsigErrorResponse(t *testing.T) {\n\tfor _, rcode := range []uint16{RcodeBadSig, RcodeBadKey} {\n\t\tm := newTsig(strings.ToUpper(HmacSHA256))\n\t\tm.IsTsig().Error = rcode\n\t\tbuf, _, err := TsigGenerate(m, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\terr = m.Unpack(buf)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tmTsig := m.IsTsig()\n\t\tif mTsig.MAC != \"\" {\n\t\t\tt.Error(\"Expected empty MAC\")\n\t\t}\n\t\tif mTsig.MACSize != 0 {\n\t\t\tt.Error(\"Expected 0 MACSize\")\n\t\t}\n\t\tif mTsig.TimeSigned != 0 {\n\t\t\tt.Errorf(\"Expected TimeSigned to be 0, got %v\", mTsig.TimeSigned)\n\t\t}\n\t}\n}\n\nfunc TestTsigBadTimeResponse(t *testing.T) {\n\tclientTime := uint64(time.Now().Unix()) - 3600\n\tm := newTsig(strings.ToUpper(HmacSHA256))\n\tm.IsTsig().Error = RcodeBadTime\n\tm.IsTsig().TimeSigned = clientTime\n\n\tbuf, _, err := TsigGenerate(m, \"pRZgBrBvI4NAHZYhxmhs/Q==\", \"\", false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\terr = m.Unpack(buf)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmTsig := m.IsTsig()\n\tif mTsig.MAC == \"\" {\n\t\tt.Error(\"Expected non-empty MAC\")\n\t}\n\tif int(mTsig.MACSize) != len(mTsig.MAC)/2 {\n\t\tt.Errorf(\"Expected MACSize %v, got %v\", len(mTsig.MAC)/2, mTsig.MACSize)\n\t}\n\n\tif mTsig.TimeSigned != clientTime {\n\t\tt.Errorf(\"Expected TimeSigned %v to be retained, got %v\", clientTime, mTsig.TimeSigned)\n\t}\n}\n\nconst (\n\t// A template wire-format DNS message (in hex form) containing a TSIG RR.\n\t// Its time signed field will be filled by tests.\n\twireMsg = \"c60028000001000000010001076578616d706c6503636f6d00000600010161c00c0001000100000e100004c0000201077465\" +\n\t\t\"73746b65790000fa00ff00000000003d0b686d61632d73686132353600\" +\n\t\t\"%012x\" + // placeholder for the \"time signed\" field\n\t\t\"012c00208cf23e0081d915478a182edcea7ff48ad102948e6c7ef8e887536957d1fa5616c60000000000\"\n\t// A secret (in base64 format) with which the TSIG in wireMsg will be validated\n\ttestSecret = \"NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk=\"\n\t// the 'time signed' field value that would make the TSIG RR valid with testSecret\n\ttimeSigned uint64 = 1594855491\n)\n\nfunc TestTsigErrors(t *testing.T) {\n\t// Helper shortcut to build wire-format test message.\n\t// TsigVerify can modify the slice, so we need to create a new one for each test case below.\n\tbuildMsgData := func(tm uint64) []byte {\n\t\tmsgData, err := hex.DecodeString(fmt.Sprintf(wireMsg, tm))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\treturn msgData\n\t}\n\n\t// the signature is valid but 'time signed' is too far from the \"current time\".\n\tif err := tsigVerify(buildMsgData(timeSigned), tsigHMACProvider(testSecret), \"\", false, timeSigned+301); err != ErrTime {\n\t\tt.Fatalf(\"expected an error '%v' but got '%v'\", ErrTime, err)\n\t}\n\tif err := tsigVerify(buildMsgData(timeSigned), tsigHMACProvider(testSecret), \"\", false, timeSigned-301); err != ErrTime {\n\t\tt.Fatalf(\"expected an error '%v' but got '%v'\", ErrTime, err)\n\t}\n\n\t// the signature is invalid and 'time signed' is too far.\n\t// the signature should be checked first, so we should see ErrSig.\n\tif err := tsigVerify(buildMsgData(timeSigned+301), tsigHMACProvider(testSecret), \"\", false, timeSigned); err != ErrSig {\n\t\tt.Fatalf(\"expected an error '%v' but got '%v'\", ErrSig, err)\n\t}\n\n\t// tweak the algorithm name in the wire data, resulting in the \"unknown algorithm\" error.\n\tmsgData := buildMsgData(timeSigned)\n\tcopy(msgData[67:], \"bogus\")\n\tif err := tsigVerify(msgData, tsigHMACProvider(testSecret), \"\", false, timeSigned); err != ErrKeyAlg {\n\t\tt.Fatalf(\"expected an error '%v' but got '%v'\", ErrKeyAlg, err)\n\t}\n\n\t// call TsigVerify with a message that doesn't contain a TSIG\n\tmsgData, tsig, err := stripTsig(buildMsgData(timeSigned))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := tsigVerify(msgData, tsigHMACProvider(testSecret), \"\", false, timeSigned); err != ErrNoSig {\n\t\tt.Fatalf(\"expected an error '%v' but got '%v'\", ErrNoSig, err)\n\t}\n\n\t// replace the test TSIG with a bogus one with large \"other data\", which would cause overflow in TsigVerify.\n\t// The overflow should be caught without disruption.\n\ttsig.OtherData = strings.Repeat(\"00\", 4096)\n\ttsig.OtherLen = uint16(len(tsig.OtherData) / 2)\n\tmsg := new(Msg)\n\tif err = msg.Unpack(msgData); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tmsg.Extra = append(msg.Extra, tsig)\n\tif msgData, err = msg.Pack(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = tsigVerify(msgData, tsigHMACProvider(testSecret), \"\", false, timeSigned)\n\tif err == nil || !strings.Contains(err.Error(), \"overflow\") {\n\t\tt.Errorf(\"expected error to contain %q, but got %v\", \"overflow\", err)\n\t}\n}\n\n// This test exercises some more corner cases for TsigGenerate.\nfunc TestTsigGenerate(t *testing.T) {\n\t// This is a template TSIG to be used for signing.\n\ttsig := TSIG{\n\t\tHdr:        RR_Header{Name: \"testkey.\", Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0},\n\t\tAlgorithm:  HmacSHA256,\n\t\tTimeSigned: timeSigned,\n\t\tFudge:      300,\n\t\tOrigId:     42,\n\t\tError:      RcodeBadTime, // use a non-0 value to make sure it's indeed used\n\t}\n\n\ttests := []struct {\n\t\tdesc        string // test description\n\t\trequestMAC  string // request MAC to be passed to TsigGenerate (arbitrary choice)\n\t\totherData   string // other data specified in the TSIG (arbitrary choice)\n\t\texpectedMAC string // pre-computed expected (correct) MAC in hex form\n\t}{\n\t\t{\"with request MAC\", \"3684c225\", \"\",\n\t\t\t\"c110e3f62694755c10761dc8717462431ee34340b7c9d1eee09449150757c5b1\"},\n\t\t{\"no request MAC\", \"\", \"\",\n\t\t\t\"385449a425c6d52b9bf2c65c0726eefa0ad8084cdaf488f24547e686605b9610\"},\n\t\t{\"with other data\", \"3684c225\", \"666f6f\",\n\t\t\t\"15b91571ca80b3b410a77e2b44f8cc4f35ace22b26020138439dd94803e23b5d\"},\n\t}\n\tfor _, tc := range tests {\n\t\ttc := tc\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t// Build TSIG for signing from the template\n\t\t\ttestTSIG := tsig\n\t\t\ttestTSIG.OtherLen = uint16(len(tc.otherData) / 2)\n\t\t\ttestTSIG.OtherData = tc.otherData\n\t\t\treq := &Msg{\n\t\t\t\tMsgHdr:   MsgHdr{Opcode: OpcodeUpdate},\n\t\t\t\tQuestion: []Question{{Name: \"example.com.\", Qtype: TypeSOA, Qclass: ClassINET}},\n\t\t\t\tExtra:    []RR{&testTSIG},\n\t\t\t}\n\n\t\t\t// Call generate, and check the returned MAC against the expected value\n\t\t\tmsgData, mac, err := TsigGenerate(req, testSecret, tc.requestMAC, false)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif mac != tc.expectedMAC {\n\t\t\t\tt.Fatalf(\"MAC doesn't match: expected '%s', but got '%s'\", tc.expectedMAC, mac)\n\t\t\t}\n\n\t\t\t// Retrieve the TSIG to be sent out, confirm the MAC in it\n\t\t\t_, outTSIG, err := stripTsig(msgData)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif outTSIG.MAC != tc.expectedMAC {\n\t\t\t\tt.Fatalf(\"MAC doesn't match: expected '%s', but got '%s'\", tc.expectedMAC, outTSIG.MAC)\n\t\t\t}\n\t\t\t// Confirm other fields of MAC.\n\t\t\t// RDLENGTH should be valid as stripTsig succeeded, so we exclude it from comparison\n\t\t\toutTSIG.MACSize = 0\n\t\t\toutTSIG.MAC = \"\"\n\t\t\ttestTSIG.Hdr.Rdlength = outTSIG.Hdr.Rdlength\n\t\t\tif *outTSIG != testTSIG {\n\t\t\t\tt.Fatalf(\"TSIG RR doesn't match: expected '%v', but got '%v'\", *outTSIG, testTSIG)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTSIGHMAC224And384(t *testing.T) {\n\ttests := []struct {\n\t\talgorithm   string // TSIG algorithm, also used as test description\n\t\tsecret      string // (arbitrarily chosen) secret suitable for the algorithm in base64 format\n\t\texpectedMAC string // pre-computed expected (correct) MAC in hex form\n\t}{\n\t\t{HmacSHA224, \"hVEkQuAqnTmBuRrT9KF1Udr91gOMGWPw9LaTtw==\",\n\t\t\t\"d6daf9ea189e48bc38f9aed63d6cc4140cdfa38a7a333ee2eefdbd31\",\n\t\t},\n\t\t{HmacSHA384, \"Qjer2TL2lAdpq9w6Gjs98/ClCQx/L3vtgVHCmrZ8l/oKEPjqUUMFO18gMCRwd5H4\",\n\t\t\t\"89a48936d29187870c325cbdba5ad71609bd038d0459d6010c844d659c570e881d3650e4fe7310be53ebe5178d0d1001\",\n\t\t},\n\t}\n\tfor _, tc := range tests {\n\t\ttc := tc\n\t\tt.Run(tc.algorithm, func(t *testing.T) {\n\t\t\t// Build a DNS message with TSIG for the test scenario\n\t\t\ttsig := TSIG{\n\t\t\t\tHdr:        RR_Header{Name: \"testkey.\", Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0},\n\t\t\t\tAlgorithm:  tc.algorithm,\n\t\t\t\tTimeSigned: timeSigned,\n\t\t\t\tFudge:      300,\n\t\t\t\tOrigId:     42,\n\t\t\t}\n\t\t\treq := &Msg{\n\t\t\t\tMsgHdr:   MsgHdr{Opcode: OpcodeUpdate},\n\t\t\t\tQuestion: []Question{{Name: \"example.com.\", Qtype: TypeSOA, Qclass: ClassINET}},\n\t\t\t\tExtra:    []RR{&tsig},\n\t\t\t}\n\n\t\t\t// Confirm both Generate and Verify recognize the algorithm and handle it correctly\n\t\t\tmsgData, mac, err := TsigGenerate(req, tc.secret, \"\", false)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif mac != tc.expectedMAC {\n\t\t\t\tt.Fatalf(\"MAC doesn't match: expected '%s' but got '%s'\", tc.expectedMAC, mac)\n\t\t\t}\n\t\t\tif err = tsigVerify(msgData, tsigHMACProvider(tc.secret), \"\", false, timeSigned); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nconst testGoodKeyName = \"goodkey.\"\n\nvar (\n\terrBadKey   = errors.New(\"this is an intentional error\")\n\ttestGoodMAC = []byte{0, 1, 2, 3}\n)\n\n// testProvider always generates the same MAC and only accepts the one signature\ntype testProvider struct {\n\tGenerateAllKeys bool\n}\n\nfunc (provider *testProvider) Generate(_ []byte, t *TSIG) ([]byte, error) {\n\tif t.Hdr.Name == testGoodKeyName || provider.GenerateAllKeys {\n\t\treturn testGoodMAC, nil\n\t}\n\treturn nil, errBadKey\n}\n\nfunc (*testProvider) Verify(_ []byte, t *TSIG) error {\n\tif t.Hdr.Name == testGoodKeyName {\n\t\treturn nil\n\t}\n\treturn errBadKey\n}\n\nfunc TestTsigGenerateProvider(t *testing.T) {\n\ttables := []struct {\n\t\tkeyname string\n\t\tmac     []byte\n\t\terr     error\n\t}{\n\t\t{\n\t\t\ttestGoodKeyName,\n\t\t\ttestGoodMAC,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"badkey.\",\n\t\t\tnil,\n\t\t\terrBadKey,\n\t\t},\n\t}\n\n\tfor _, table := range tables {\n\t\tt.Run(table.keyname, func(t *testing.T) {\n\t\t\ttsig := TSIG{\n\t\t\t\tHdr:        RR_Header{Name: table.keyname, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0},\n\t\t\t\tAlgorithm:  HmacSHA1,\n\t\t\t\tTimeSigned: timeSigned,\n\t\t\t\tFudge:      300,\n\t\t\t\tOrigId:     42,\n\t\t\t}\n\t\t\treq := &Msg{\n\t\t\t\tMsgHdr:   MsgHdr{Opcode: OpcodeUpdate},\n\t\t\t\tQuestion: []Question{{Name: \"example.com.\", Qtype: TypeSOA, Qclass: ClassINET}},\n\t\t\t\tExtra:    []RR{&tsig},\n\t\t\t}\n\n\t\t\t_, mac, err := TsigGenerateWithProvider(req, new(testProvider), \"\", false)\n\t\t\tif err != table.err {\n\t\t\t\tt.Fatalf(\"error doesn't match: expected '%s' but got '%s'\", table.err, err)\n\t\t\t}\n\t\t\texpectedMAC := hex.EncodeToString(table.mac)\n\t\t\tif mac != expectedMAC {\n\t\t\t\tt.Fatalf(\"MAC doesn't match: expected '%s' but got '%s'\", table.mac, expectedMAC)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTsigVerifyProvider(t *testing.T) {\n\ttables := []struct {\n\t\tkeyname string\n\t\terr     error\n\t}{\n\t\t{\n\t\t\ttestGoodKeyName,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"badkey.\",\n\t\t\terrBadKey,\n\t\t},\n\t}\n\n\tfor _, table := range tables {\n\t\tt.Run(table.keyname, func(t *testing.T) {\n\t\t\ttsig := TSIG{\n\t\t\t\tHdr:        RR_Header{Name: table.keyname, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0},\n\t\t\t\tAlgorithm:  HmacSHA1,\n\t\t\t\tTimeSigned: timeSigned,\n\t\t\t\tFudge:      300,\n\t\t\t\tOrigId:     42,\n\t\t\t}\n\t\t\treq := &Msg{\n\t\t\t\tMsgHdr:   MsgHdr{Opcode: OpcodeUpdate},\n\t\t\t\tQuestion: []Question{{Name: \"example.com.\", Qtype: TypeSOA, Qclass: ClassINET}},\n\t\t\t\tExtra:    []RR{&tsig},\n\t\t\t}\n\n\t\t\tprovider := &testProvider{true}\n\t\t\tmsgData, _, err := TsigGenerateWithProvider(req, provider, \"\", false)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif err = tsigVerify(msgData, provider, \"\", false, timeSigned); err != table.err {\n\t\t\t\tt.Fatalf(\"error doesn't match: expected '%s' but got '%s'\", table.err, err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "types.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype (\n\t// Type is a DNS type.\n\tType uint16\n\t// Class is a DNS class.\n\tClass uint16\n\t// Name is a DNS domain name.\n\tName string\n)\n\n// Packet formats\n\n// Wire constants and supported types.\nconst (\n\t// valid RR_Header.Rrtype and Question.qtype\n\n\tTypeNone       uint16 = 0\n\tTypeA          uint16 = 1\n\tTypeNS         uint16 = 2\n\tTypeMD         uint16 = 3\n\tTypeMF         uint16 = 4\n\tTypeCNAME      uint16 = 5\n\tTypeSOA        uint16 = 6\n\tTypeMB         uint16 = 7\n\tTypeMG         uint16 = 8\n\tTypeMR         uint16 = 9\n\tTypeNULL       uint16 = 10\n\tTypePTR        uint16 = 12\n\tTypeHINFO      uint16 = 13\n\tTypeMINFO      uint16 = 14\n\tTypeMX         uint16 = 15\n\tTypeTXT        uint16 = 16\n\tTypeRP         uint16 = 17\n\tTypeAFSDB      uint16 = 18\n\tTypeX25        uint16 = 19\n\tTypeISDN       uint16 = 20\n\tTypeRT         uint16 = 21\n\tTypeNSAPPTR    uint16 = 23\n\tTypeSIG        uint16 = 24\n\tTypeKEY        uint16 = 25\n\tTypePX         uint16 = 26\n\tTypeGPOS       uint16 = 27\n\tTypeAAAA       uint16 = 28\n\tTypeLOC        uint16 = 29\n\tTypeNXT        uint16 = 30\n\tTypeEID        uint16 = 31\n\tTypeNIMLOC     uint16 = 32\n\tTypeSRV        uint16 = 33\n\tTypeATMA       uint16 = 34\n\tTypeNAPTR      uint16 = 35\n\tTypeKX         uint16 = 36\n\tTypeCERT       uint16 = 37\n\tTypeDNAME      uint16 = 39\n\tTypeOPT        uint16 = 41 // EDNS\n\tTypeAPL        uint16 = 42\n\tTypeDS         uint16 = 43\n\tTypeSSHFP      uint16 = 44\n\tTypeIPSECKEY   uint16 = 45\n\tTypeRRSIG      uint16 = 46\n\tTypeNSEC       uint16 = 47\n\tTypeDNSKEY     uint16 = 48\n\tTypeDHCID      uint16 = 49\n\tTypeNSEC3      uint16 = 50\n\tTypeNSEC3PARAM uint16 = 51\n\tTypeTLSA       uint16 = 52\n\tTypeSMIMEA     uint16 = 53\n\tTypeHIP        uint16 = 55\n\tTypeNINFO      uint16 = 56\n\tTypeRKEY       uint16 = 57\n\tTypeTALINK     uint16 = 58\n\tTypeCDS        uint16 = 59\n\tTypeCDNSKEY    uint16 = 60\n\tTypeOPENPGPKEY uint16 = 61\n\tTypeCSYNC      uint16 = 62\n\tTypeZONEMD     uint16 = 63\n\tTypeSVCB       uint16 = 64\n\tTypeHTTPS      uint16 = 65\n\tTypeSPF        uint16 = 99\n\tTypeUINFO      uint16 = 100\n\tTypeUID        uint16 = 101\n\tTypeGID        uint16 = 102\n\tTypeUNSPEC     uint16 = 103\n\tTypeNID        uint16 = 104\n\tTypeL32        uint16 = 105\n\tTypeL64        uint16 = 106\n\tTypeLP         uint16 = 107\n\tTypeEUI48      uint16 = 108\n\tTypeEUI64      uint16 = 109\n\tTypeNXNAME     uint16 = 128\n\tTypeURI        uint16 = 256\n\tTypeCAA        uint16 = 257\n\tTypeAVC        uint16 = 258\n\tTypeAMTRELAY   uint16 = 260\n\tTypeRESINFO    uint16 = 261\n\n\tTypeTKEY uint16 = 249\n\tTypeTSIG uint16 = 250\n\n\t// valid Question.Qtype only\n\tTypeIXFR  uint16 = 251\n\tTypeAXFR  uint16 = 252\n\tTypeMAILB uint16 = 253\n\tTypeMAILA uint16 = 254\n\tTypeANY   uint16 = 255\n\n\tTypeTA       uint16 = 32768\n\tTypeDLV      uint16 = 32769\n\tTypeReserved uint16 = 65535\n\n\t// valid Question.Qclass\n\tClassINET   = 1\n\tClassCSNET  = 2\n\tClassCHAOS  = 3\n\tClassHESIOD = 4\n\tClassNONE   = 254\n\tClassANY    = 255\n\n\t// Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml\n\tRcodeSuccess                    = 0  // NoError   - No Error                          [DNS]\n\tRcodeFormatError                = 1  // FormErr   - Format Error                      [DNS]\n\tRcodeServerFailure              = 2  // ServFail  - Server Failure                    [DNS]\n\tRcodeNameError                  = 3  // NXDomain  - Non-Existent Domain               [DNS]\n\tRcodeNotImplemented             = 4  // NotImp    - Not Implemented                   [DNS]\n\tRcodeRefused                    = 5  // Refused   - Query Refused                     [DNS]\n\tRcodeYXDomain                   = 6  // YXDomain  - Name Exists when it should not    [DNS Update]\n\tRcodeYXRrset                    = 7  // YXRRSet   - RR Set Exists when it should not  [DNS Update]\n\tRcodeNXRrset                    = 8  // NXRRSet   - RR Set that should exist does not [DNS Update]\n\tRcodeNotAuth                    = 9  // NotAuth   - Server Not Authoritative for zone [DNS Update]\n\tRcodeNotZone                    = 10 // NotZone   - Name not contained in zone        [DNS Update/TSIG]\n\tRcodeStatefulTypeNotImplemented = 11 // DSOTypeNI - DSO-TYPE not implemented          [DNS Stateful Operations] https://www.rfc-editor.org/rfc/rfc8490.html#section-10.2\n\tRcodeBadSig                     = 16 // BADSIG    - TSIG Signature Failure            [TSIG]  https://www.rfc-editor.org/rfc/rfc6895.html#section-2.3\n\tRcodeBadVers                    = 16 // BADVERS   - Bad OPT Version                   [EDNS0] https://www.rfc-editor.org/rfc/rfc6895.html#section-2.3\n\tRcodeBadKey                     = 17 // BADKEY    - Key not recognized                [TSIG]\n\tRcodeBadTime                    = 18 // BADTIME   - Signature out of time window      [TSIG]\n\tRcodeBadMode                    = 19 // BADMODE   - Bad TKEY Mode                     [TKEY]\n\tRcodeBadName                    = 20 // BADNAME   - Duplicate key name                [TKEY]\n\tRcodeBadAlg                     = 21 // BADALG    - Algorithm not supported           [TKEY]\n\tRcodeBadTrunc                   = 22 // BADTRUNC  - Bad Truncation                    [TSIG]\n\tRcodeBadCookie                  = 23 // BADCOOKIE - Bad/missing Server Cookie         [DNS Cookies]\n\n\t// Message Opcodes. There is no 3.\n\tOpcodeQuery    = 0\n\tOpcodeIQuery   = 1\n\tOpcodeStatus   = 2\n\tOpcodeNotify   = 4\n\tOpcodeUpdate   = 5\n\tOpcodeStateful = 6\n)\n\n// Used in ZONEMD https://tools.ietf.org/html/rfc8976\nconst (\n\tZoneMDSchemeSimple = 1\n\n\tZoneMDHashAlgSHA384 = 1\n\tZoneMDHashAlgSHA512 = 2\n)\n\n// Used in IPSEC https://datatracker.ietf.org/doc/html/rfc4025#section-2.3\nconst (\n\tIPSECGatewayNone uint8 = iota\n\tIPSECGatewayIPv4\n\tIPSECGatewayIPv6\n\tIPSECGatewayHost\n)\n\n// Used in AMTRELAY https://datatracker.ietf.org/doc/html/rfc8777#section-4.2.3\nconst (\n\tAMTRELAYNone = IPSECGatewayNone\n\tAMTRELAYIPv4 = IPSECGatewayIPv4\n\tAMTRELAYIPv6 = IPSECGatewayIPv6\n\tAMTRELAYHost = IPSECGatewayHost\n)\n\n// Stateful types as defined in RFC 8490.\nconst (\n\tStatefulTypeKeepAlive uint16 = iota + 1\n\tStatefulTypeRetryDelay\n\tStatefulTypeEncryptionPadding\n)\n\nvar StatefulTypeToString = map[uint16]string{\n\tStatefulTypeKeepAlive:         \"KeepAlive\",\n\tStatefulTypeRetryDelay:        \"RetryDelay\",\n\tStatefulTypeEncryptionPadding: \"EncryptionPadding\",\n}\n\n// Header is the wire format for the DNS packet header.\ntype Header struct {\n\tId                                 uint16\n\tBits                               uint16\n\tQdcount, Ancount, Nscount, Arcount uint16\n}\n\nconst (\n\theaderSize = 12\n\n\t// Header.Bits\n\t_QR = 1 << 15 // query/response (response=1)\n\t_AA = 1 << 10 // authoritative\n\t_TC = 1 << 9  // truncated\n\t_RD = 1 << 8  // recursion desired\n\t_RA = 1 << 7  // recursion available\n\t_Z  = 1 << 6  // Z\n\t_AD = 1 << 5  // authenticated data\n\t_CD = 1 << 4  // checking disabled\n)\n\n// Various constants used in the LOC RR. See RFC 1876.\nconst (\n\tLOC_EQUATOR       = 1 << 31 // RFC 1876, Section 2.\n\tLOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2.\n\tLOC_HOURS         = 60 * 1000\n\tLOC_DEGREES       = 60 * LOC_HOURS\n\tLOC_ALTITUDEBASE  = 100000\n)\n\n// Different Certificate Types, see RFC 4398, Section 2.1\nconst (\n\tCertPKIX = 1 + iota\n\tCertSPKI\n\tCertPGP\n\tCertIPIX\n\tCertISPKI\n\tCertIPGP\n\tCertACPKIX\n\tCertIACPKIX\n\tCertURI = 253\n\tCertOID = 254\n)\n\n// CertTypeToString converts the Cert Type to its string representation.\n// See RFC 4398 and RFC 6944.\nvar CertTypeToString = map[uint16]string{\n\tCertPKIX:    \"PKIX\",\n\tCertSPKI:    \"SPKI\",\n\tCertPGP:     \"PGP\",\n\tCertIPIX:    \"IPIX\",\n\tCertISPKI:   \"ISPKI\",\n\tCertIPGP:    \"IPGP\",\n\tCertACPKIX:  \"ACPKIX\",\n\tCertIACPKIX: \"IACPKIX\",\n\tCertURI:     \"URI\",\n\tCertOID:     \"OID\",\n}\n\n// Prefix for IPv4 encoded as IPv6 address\nconst ipv4InIPv6Prefix = \"::ffff:\"\n\n//go:generate go run types_generate.go\n\n// Question holds a DNS question. Usually there is just one. While the\n// original DNS RFCs allow multiple questions in the question section of a\n// message, in practice it never works. Because most DNS servers see multiple\n// questions as an error, it is recommended to only have one question per\n// message.\ntype Question struct {\n\tName   string `dns:\"cdomain-name\"` // \"cdomain-name\" specifies encoding (and may be compressed)\n\tQtype  uint16\n\tQclass uint16\n}\n\nfunc (q *Question) len(off int, compression map[string]struct{}) int {\n\tl := domainNameLen(q.Name, off, compression, true)\n\tl += 2 + 2\n\treturn l\n}\n\nfunc (q *Question) String() (s string) {\n\t// prefix with ; (as in dig)\n\ts = \";\" + sprintName(q.Name) + \"\\t\"\n\ts += Class(q.Qclass).String() + \"\\t\"\n\ts += \" \" + Type(q.Qtype).String()\n\treturn s\n}\n\n// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY is named \"*\" there.\n// The ANY records can be (ab)used to create resource records without any rdata, that\n// can be used in dynamic update requests. Basic use pattern:\n//\n//\ta := &ANY{RR_Header{\n//\t\tName:   \"example.org.\",\n//\t\tRrtype: TypeA,\n//\t\tClass:  ClassINET,\n//\t}}\n//\n// Results in an A record without rdata.\ntype ANY struct {\n\tHdr RR_Header\n\t// Does not have any rdata.\n}\n\nfunc (rr *ANY) String() string { return rr.Hdr.String() }\n\nfunc (*ANY) parse(c *zlexer, origin string) *ParseError {\n\treturn &ParseError{err: \"ANY records do not have a presentation format\"}\n}\n\n// NULL RR. See RFC 1035.\ntype NULL struct {\n\tHdr  RR_Header\n\tData string `dns:\"any\"`\n}\n\nfunc (rr *NULL) String() string {\n\t// There is no presentation format; prefix string with a comment.\n\treturn \";\" + rr.Hdr.String() + rr.Data\n}\n\nfunc (*NULL) parse(c *zlexer, origin string) *ParseError {\n\treturn &ParseError{err: \"NULL records do not have a presentation format\"}\n}\n\n// NXNAME is a meta record. See https://www.iana.org/go/draft-ietf-dnsop-compact-denial-of-existence-04\n// Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml\ntype NXNAME struct {\n\tHdr RR_Header\n\t// Does not have any rdata\n}\n\nfunc (rr *NXNAME) String() string { return rr.Hdr.String() }\n\nfunc (*NXNAME) parse(c *zlexer, origin string) *ParseError {\n\treturn &ParseError{err: \"NXNAME records do not have a presentation format\"}\n}\n\n// CNAME RR. See RFC 1034.\ntype CNAME struct {\n\tHdr    RR_Header\n\tTarget string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }\n\n// HINFO RR. See RFC 1034.\ntype HINFO struct {\n\tHdr RR_Header\n\tCpu string\n\tOs  string\n}\n\nfunc (rr *HINFO) String() string {\n\treturn rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os})\n}\n\n// MB RR. See RFC 1035.\ntype MB struct {\n\tHdr RR_Header\n\tMb  string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }\n\n// MG RR. See RFC 1035.\ntype MG struct {\n\tHdr RR_Header\n\tMg  string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }\n\n// MINFO RR. See RFC 1035.\ntype MINFO struct {\n\tHdr   RR_Header\n\tRmail string `dns:\"cdomain-name\"`\n\tEmail string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MINFO) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Rmail) + \" \" + sprintName(rr.Email)\n}\n\n// MR RR. See RFC 1035.\ntype MR struct {\n\tHdr RR_Header\n\tMr  string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MR) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Mr)\n}\n\n// MF RR. See RFC 1035.\ntype MF struct {\n\tHdr RR_Header\n\tMf  string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MF) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Mf)\n}\n\n// MD RR. See RFC 1035.\ntype MD struct {\n\tHdr RR_Header\n\tMd  string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MD) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Md)\n}\n\n// MX RR. See RFC 1035.\ntype MX struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tMx         string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *MX) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + \" \" + sprintName(rr.Mx)\n}\n\n// AFSDB RR. See RFC 1183.\ntype AFSDB struct {\n\tHdr      RR_Header\n\tSubtype  uint16\n\tHostname string `dns:\"domain-name\"`\n}\n\nfunc (rr *AFSDB) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + \" \" + sprintName(rr.Hostname)\n}\n\n// X25 RR. See RFC 1183, Section 3.1.\ntype X25 struct {\n\tHdr         RR_Header\n\tPSDNAddress string\n}\n\nfunc (rr *X25) String() string {\n\treturn rr.Hdr.String() + rr.PSDNAddress\n}\n\n// ISDN RR. See RFC 1183, Section 3.2.\ntype ISDN struct {\n\tHdr        RR_Header\n\tAddress    string\n\tSubAddress string\n}\n\nfunc (rr *ISDN) String() string {\n\treturn rr.Hdr.String() + sprintTxt([]string{rr.Address, rr.SubAddress})\n}\n\n// RT RR. See RFC 1183, Section 3.3.\ntype RT struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tHost       string `dns:\"domain-name\"` // RFC 3597 prohibits compressing records not defined in RFC 1035.\n}\n\nfunc (rr *RT) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + \" \" + sprintName(rr.Host)\n}\n\n// NS RR. See RFC 1035.\ntype NS struct {\n\tHdr RR_Header\n\tNs  string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *NS) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Ns)\n}\n\n// PTR RR. See RFC 1035.\ntype PTR struct {\n\tHdr RR_Header\n\tPtr string `dns:\"cdomain-name\"`\n}\n\nfunc (rr *PTR) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Ptr)\n}\n\n// RP RR. See RFC 1138, Section 2.2.\ntype RP struct {\n\tHdr  RR_Header\n\tMbox string `dns:\"domain-name\"`\n\tTxt  string `dns:\"domain-name\"`\n}\n\nfunc (rr *RP) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Mbox) + \" \" + sprintName(rr.Txt)\n}\n\n// SOA RR. See RFC 1035.\ntype SOA struct {\n\tHdr     RR_Header\n\tNs      string `dns:\"cdomain-name\"`\n\tMbox    string `dns:\"cdomain-name\"`\n\tSerial  uint32\n\tRefresh uint32\n\tRetry   uint32\n\tExpire  uint32\n\tMinttl  uint32\n}\n\nfunc (rr *SOA) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Ns) + \" \" + sprintName(rr.Mbox) +\n\t\t\" \" + strconv.FormatInt(int64(rr.Serial), 10) +\n\t\t\" \" + strconv.FormatInt(int64(rr.Refresh), 10) +\n\t\t\" \" + strconv.FormatInt(int64(rr.Retry), 10) +\n\t\t\" \" + strconv.FormatInt(int64(rr.Expire), 10) +\n\t\t\" \" + strconv.FormatInt(int64(rr.Minttl), 10)\n}\n\n// TXT RR. See RFC 1035.\ntype TXT struct {\n\tHdr RR_Header\n\tTxt []string `dns:\"txt\"`\n}\n\nfunc (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }\n\nfunc sprintName(s string) string {\n\tvar dst strings.Builder\n\n\tfor i := 0; i < len(s); {\n\t\tif s[i] == '.' {\n\t\t\tif dst.Len() != 0 {\n\t\t\t\tdst.WriteByte('.')\n\t\t\t}\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\n\t\tb, n := nextByte(s, i)\n\t\tif n == 0 {\n\t\t\t// Drop \"dangling\" incomplete escapes.\n\t\t\tif dst.Len() == 0 {\n\t\t\t\treturn s[:i]\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif isDomainNameLabelSpecial(b) {\n\t\t\tif dst.Len() == 0 {\n\t\t\t\tdst.Grow(len(s) * 2)\n\t\t\t\tdst.WriteString(s[:i])\n\t\t\t}\n\t\t\tdst.WriteByte('\\\\')\n\t\t\tdst.WriteByte(b)\n\t\t} else if b < ' ' || b > '~' { // unprintable, use \\DDD\n\t\t\tif dst.Len() == 0 {\n\t\t\t\tdst.Grow(len(s) * 2)\n\t\t\t\tdst.WriteString(s[:i])\n\t\t\t}\n\t\t\tdst.WriteString(escapeByte(b))\n\t\t} else {\n\t\t\tif dst.Len() != 0 {\n\t\t\t\tdst.WriteByte(b)\n\t\t\t}\n\t\t}\n\t\ti += n\n\t}\n\tif dst.Len() == 0 {\n\t\treturn s\n\t}\n\treturn dst.String()\n}\n\nfunc sprintTxtOctet(s string) string {\n\tvar dst strings.Builder\n\tdst.Grow(2 + len(s))\n\tdst.WriteByte('\"')\n\tfor i := 0; i < len(s); {\n\t\tif i+1 < len(s) && s[i] == '\\\\' && s[i+1] == '.' {\n\t\t\tdst.WriteString(s[i : i+2])\n\t\t\ti += 2\n\t\t\tcontinue\n\t\t}\n\n\t\tb, n := nextByte(s, i)\n\t\tif n == 0 {\n\t\t\ti++ // dangling back slash\n\t\t} else {\n\t\t\twriteTXTStringByte(&dst, b)\n\t\t}\n\t\ti += n\n\t}\n\tdst.WriteByte('\"')\n\treturn dst.String()\n}\n\nfunc sprintTxt(txt []string) string {\n\tvar out strings.Builder\n\tfor i, s := range txt {\n\t\tout.Grow(3 + len(s))\n\t\tif i > 0 {\n\t\t\tout.WriteString(` \"`)\n\t\t} else {\n\t\t\tout.WriteByte('\"')\n\t\t}\n\t\tfor j := 0; j < len(s); {\n\t\t\tb, n := nextByte(s, j)\n\t\t\tif n == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\twriteTXTStringByte(&out, b)\n\t\t\tj += n\n\t\t}\n\t\tout.WriteByte('\"')\n\t}\n\treturn out.String()\n}\n\nfunc writeTXTStringByte(s *strings.Builder, b byte) {\n\tswitch {\n\tcase b == '\"' || b == '\\\\':\n\t\ts.WriteByte('\\\\')\n\t\ts.WriteByte(b)\n\tcase b < ' ' || b > '~':\n\t\ts.WriteString(escapeByte(b))\n\tdefault:\n\t\ts.WriteByte(b)\n\t}\n}\n\nconst (\n\tescapedByteSmall = \"\" +\n\t\t`\\000\\001\\002\\003\\004\\005\\006\\007\\008\\009` +\n\t\t`\\010\\011\\012\\013\\014\\015\\016\\017\\018\\019` +\n\t\t`\\020\\021\\022\\023\\024\\025\\026\\027\\028\\029` +\n\t\t`\\030\\031`\n\tescapedByteLarge = `\\127\\128\\129` +\n\t\t`\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139` +\n\t\t`\\140\\141\\142\\143\\144\\145\\146\\147\\148\\149` +\n\t\t`\\150\\151\\152\\153\\154\\155\\156\\157\\158\\159` +\n\t\t`\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169` +\n\t\t`\\170\\171\\172\\173\\174\\175\\176\\177\\178\\179` +\n\t\t`\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189` +\n\t\t`\\190\\191\\192\\193\\194\\195\\196\\197\\198\\199` +\n\t\t`\\200\\201\\202\\203\\204\\205\\206\\207\\208\\209` +\n\t\t`\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219` +\n\t\t`\\220\\221\\222\\223\\224\\225\\226\\227\\228\\229` +\n\t\t`\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239` +\n\t\t`\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249` +\n\t\t`\\250\\251\\252\\253\\254\\255`\n)\n\n// escapeByte returns the \\DDD escaping of b which must\n// satisfy b < ' ' || b > '~'.\nfunc escapeByte(b byte) string {\n\tif b < ' ' {\n\t\treturn escapedByteSmall[b*4 : b*4+4]\n\t}\n\n\tb -= '~' + 1\n\t// The cast here is needed as b*4 may overflow byte.\n\treturn escapedByteLarge[int(b)*4 : int(b)*4+4]\n}\n\n// isDomainNameLabelSpecial returns true if\n// a domain name label byte should be prefixed\n// with an escaping backslash.\nfunc isDomainNameLabelSpecial(b byte) bool {\n\tswitch b {\n\tcase '.', ' ', '\\'', '@', ';', '(', ')', '\"', '\\\\':\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc nextByte(s string, offset int) (byte, int) {\n\tif offset >= len(s) {\n\t\treturn 0, 0\n\t}\n\tif s[offset] != '\\\\' {\n\t\t// not an escape sequence\n\t\treturn s[offset], 1\n\t}\n\tswitch len(s) - offset {\n\tcase 1: // dangling escape\n\t\treturn 0, 0\n\tcase 2, 3: // too short to be \\ddd\n\tdefault: // maybe \\ddd\n\t\tif isDDD(s[offset+1:]) {\n\t\t\treturn dddToByte(s[offset+1:]), 4\n\t\t}\n\t}\n\t// not \\ddd, just an RFC 1035 \"quoted\" character\n\treturn s[offset+1], 2\n}\n\n// SPF RR. See RFC 4408, Section 3.1.1.\ntype SPF struct {\n\tHdr RR_Header\n\tTxt []string `dns:\"txt\"`\n}\n\nfunc (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }\n\n// AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template.\ntype AVC struct {\n\tHdr RR_Header\n\tTxt []string `dns:\"txt\"`\n}\n\nfunc (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }\n\n// SRV RR. See RFC 2782.\ntype SRV struct {\n\tHdr      RR_Header\n\tPriority uint16\n\tWeight   uint16\n\tPort     uint16\n\tTarget   string `dns:\"domain-name\"`\n}\n\nfunc (rr *SRV) String() string {\n\treturn rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.Priority)) + \" \" +\n\t\tstrconv.Itoa(int(rr.Weight)) + \" \" +\n\t\tstrconv.Itoa(int(rr.Port)) + \" \" + sprintName(rr.Target)\n}\n\n// NAPTR RR. See RFC 2915.\ntype NAPTR struct {\n\tHdr         RR_Header\n\tOrder       uint16\n\tPreference  uint16\n\tFlags       string\n\tService     string\n\tRegexp      string\n\tReplacement string `dns:\"domain-name\"`\n}\n\nfunc (rr *NAPTR) String() string {\n\treturn rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.Order)) + \" \" +\n\t\tstrconv.Itoa(int(rr.Preference)) + \" \" +\n\t\t\"\\\"\" + rr.Flags + \"\\\" \" +\n\t\t\"\\\"\" + rr.Service + \"\\\" \" +\n\t\t\"\\\"\" + rr.Regexp + \"\\\" \" +\n\t\trr.Replacement\n}\n\n// CERT RR. See RFC 4398.\ntype CERT struct {\n\tHdr         RR_Header\n\tType        uint16\n\tKeyTag      uint16\n\tAlgorithm   uint8\n\tCertificate string `dns:\"base64\"`\n}\n\nfunc (rr *CERT) String() string {\n\tvar (\n\t\tok                  bool\n\t\tcerttype, algorithm string\n\t)\n\tif certtype, ok = CertTypeToString[rr.Type]; !ok {\n\t\tcerttype = strconv.Itoa(int(rr.Type))\n\t}\n\tif algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok {\n\t\talgorithm = strconv.Itoa(int(rr.Algorithm))\n\t}\n\treturn rr.Hdr.String() + certtype +\n\t\t\" \" + strconv.Itoa(int(rr.KeyTag)) +\n\t\t\" \" + algorithm +\n\t\t\" \" + rr.Certificate\n}\n\n// DNAME RR. See RFC 2672.\ntype DNAME struct {\n\tHdr    RR_Header\n\tTarget string `dns:\"domain-name\"`\n}\n\nfunc (rr *DNAME) String() string {\n\treturn rr.Hdr.String() + sprintName(rr.Target)\n}\n\n// A RR. See RFC 1035.\ntype A struct {\n\tHdr RR_Header\n\tA   net.IP `dns:\"a\"`\n}\n\nfunc (rr *A) String() string {\n\tif rr.A == nil {\n\t\treturn rr.Hdr.String()\n\t}\n\treturn rr.Hdr.String() + rr.A.String()\n}\n\n// AAAA RR. See RFC 3596.\ntype AAAA struct {\n\tHdr  RR_Header\n\tAAAA net.IP `dns:\"aaaa\"`\n}\n\nfunc (rr *AAAA) String() string {\n\tif rr.AAAA == nil {\n\t\treturn rr.Hdr.String()\n\t}\n\n\tif rr.AAAA.To4() != nil {\n\t\treturn rr.Hdr.String() + ipv4InIPv6Prefix + rr.AAAA.String()\n\t}\n\n\treturn rr.Hdr.String() + rr.AAAA.String()\n}\n\n// PX RR. See RFC 2163.\ntype PX struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tMap822     string `dns:\"domain-name\"`\n\tMapx400    string `dns:\"domain-name\"`\n}\n\nfunc (rr *PX) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + \" \" + sprintName(rr.Map822) + \" \" + sprintName(rr.Mapx400)\n}\n\n// GPOS RR. See RFC 1712.\ntype GPOS struct {\n\tHdr       RR_Header\n\tLongitude string\n\tLatitude  string\n\tAltitude  string\n}\n\nfunc (rr *GPOS) String() string {\n\treturn rr.Hdr.String() + rr.Longitude + \" \" + rr.Latitude + \" \" + rr.Altitude\n}\n\n// LOC RR. See RFC 1876.\ntype LOC struct {\n\tHdr       RR_Header\n\tVersion   uint8\n\tSize      uint8\n\tHorizPre  uint8\n\tVertPre   uint8\n\tLatitude  uint32\n\tLongitude uint32\n\tAltitude  uint32\n}\n\n// cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent\n// format and returns a string in m (two decimals for the cm).\nfunc cmToM(x uint8) string {\n\tm := x & 0xf0 >> 4\n\te := x & 0x0f\n\n\tif e < 2 {\n\t\tif e == 1 {\n\t\t\tm *= 10\n\t\t}\n\n\t\treturn fmt.Sprintf(\"0.%02d\", m)\n\t}\n\n\ts := fmt.Sprintf(\"%d\", m)\n\tfor e > 2 {\n\t\ts += \"0\"\n\t\te--\n\t}\n\treturn s\n}\n\nfunc (rr *LOC) String() string {\n\ts := rr.Hdr.String()\n\n\tlat := rr.Latitude\n\tns := \"N\"\n\tif lat > LOC_EQUATOR {\n\t\tlat = lat - LOC_EQUATOR\n\t} else {\n\t\tns = \"S\"\n\t\tlat = LOC_EQUATOR - lat\n\t}\n\th := lat / LOC_DEGREES\n\tlat = lat % LOC_DEGREES\n\tm := lat / LOC_HOURS\n\tlat = lat % LOC_HOURS\n\ts += fmt.Sprintf(\"%02d %02d %0.3f %s \", h, m, float64(lat)/1000, ns)\n\n\tlon := rr.Longitude\n\tew := \"E\"\n\tif lon > LOC_PRIMEMERIDIAN {\n\t\tlon = lon - LOC_PRIMEMERIDIAN\n\t} else {\n\t\tew = \"W\"\n\t\tlon = LOC_PRIMEMERIDIAN - lon\n\t}\n\th = lon / LOC_DEGREES\n\tlon = lon % LOC_DEGREES\n\tm = lon / LOC_HOURS\n\tlon = lon % LOC_HOURS\n\ts += fmt.Sprintf(\"%02d %02d %0.3f %s \", h, m, float64(lon)/1000, ew)\n\n\talt := float64(rr.Altitude) / 100\n\talt -= LOC_ALTITUDEBASE\n\tif rr.Altitude%100 != 0 {\n\t\ts += fmt.Sprintf(\"%.2fm \", alt)\n\t} else {\n\t\ts += fmt.Sprintf(\"%.0fm \", alt)\n\t}\n\n\ts += cmToM(rr.Size) + \"m \"\n\ts += cmToM(rr.HorizPre) + \"m \"\n\ts += cmToM(rr.VertPre) + \"m\"\n\treturn s\n}\n\n// SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931.\ntype SIG struct {\n\tRRSIG\n}\n\n// RRSIG RR. See RFC 4034 and RFC 3755.\ntype RRSIG struct {\n\tHdr         RR_Header\n\tTypeCovered uint16\n\tAlgorithm   uint8\n\tLabels      uint8\n\tOrigTtl     uint32\n\tExpiration  uint32\n\tInception   uint32\n\tKeyTag      uint16\n\tSignerName  string `dns:\"domain-name\"`\n\tSignature   string `dns:\"base64\"`\n}\n\nfunc (rr *RRSIG) String() string {\n\ts := rr.Hdr.String()\n\ts += Type(rr.TypeCovered).String()\n\ts += \" \" + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + strconv.Itoa(int(rr.Labels)) +\n\t\t\" \" + strconv.FormatInt(int64(rr.OrigTtl), 10) +\n\t\t\" \" + TimeToString(rr.Expiration) +\n\t\t\" \" + TimeToString(rr.Inception) +\n\t\t\" \" + strconv.Itoa(int(rr.KeyTag)) +\n\t\t\" \" + sprintName(rr.SignerName) +\n\t\t\" \" + rr.Signature\n\treturn s\n}\n\n// NXT RR. See RFC 2535.\ntype NXT struct {\n\tNSEC\n}\n\n// NSEC RR. See RFC 4034 and RFC 3755.\ntype NSEC struct {\n\tHdr        RR_Header\n\tNextDomain string   `dns:\"domain-name\"`\n\tTypeBitMap []uint16 `dns:\"nsec\"`\n}\n\nfunc (rr *NSEC) String() string {\n\ts := rr.Hdr.String() + sprintName(rr.NextDomain)\n\tfor _, t := range rr.TypeBitMap {\n\t\ts += \" \" + Type(t).String()\n\t}\n\treturn s\n}\n\nfunc (rr *NSEC) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.NextDomain, off+l, compression, false)\n\tl += typeBitMapLen(rr.TypeBitMap)\n\treturn l\n}\n\n// DLV RR. See RFC 4431.\ntype DLV struct{ DS }\n\n// CDS RR. See RFC 7344.\ntype CDS struct{ DS }\n\n// DS RR. See RFC 4034 and RFC 3658.\ntype DS struct {\n\tHdr        RR_Header\n\tKeyTag     uint16\n\tAlgorithm  uint8\n\tDigestType uint8\n\tDigest     string `dns:\"hex\"`\n}\n\nfunc (rr *DS) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +\n\t\t\" \" + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + strconv.Itoa(int(rr.DigestType)) +\n\t\t\" \" + strings.ToUpper(rr.Digest)\n}\n\n// KX RR. See RFC 2230.\ntype KX struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tExchanger  string `dns:\"domain-name\"`\n}\n\nfunc (rr *KX) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +\n\t\t\" \" + sprintName(rr.Exchanger)\n}\n\n// TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf.\ntype TA struct {\n\tHdr        RR_Header\n\tKeyTag     uint16\n\tAlgorithm  uint8\n\tDigestType uint8\n\tDigest     string `dns:\"hex\"`\n}\n\nfunc (rr *TA) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +\n\t\t\" \" + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + strconv.Itoa(int(rr.DigestType)) +\n\t\t\" \" + strings.ToUpper(rr.Digest)\n}\n\n// TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template.\ntype TALINK struct {\n\tHdr          RR_Header\n\tPreviousName string `dns:\"domain-name\"`\n\tNextName     string `dns:\"domain-name\"`\n}\n\nfunc (rr *TALINK) String() string {\n\treturn rr.Hdr.String() +\n\t\tsprintName(rr.PreviousName) + \" \" + sprintName(rr.NextName)\n}\n\n// SSHFP RR. See RFC 4255.\ntype SSHFP struct {\n\tHdr         RR_Header\n\tAlgorithm   uint8\n\tType        uint8\n\tFingerPrint string `dns:\"hex\"`\n}\n\nfunc (rr *SSHFP) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + strconv.Itoa(int(rr.Type)) +\n\t\t\" \" + strings.ToUpper(rr.FingerPrint)\n}\n\n// KEY RR. See RFC 2535.\ntype KEY struct {\n\tDNSKEY\n}\n\n// CDNSKEY RR. See RFC 7344.\ntype CDNSKEY struct {\n\tDNSKEY\n}\n\n// DNSKEY RR. See RFC 4034 and RFC 3755.\ntype DNSKEY struct {\n\tHdr       RR_Header\n\tFlags     uint16\n\tProtocol  uint8\n\tAlgorithm uint8\n\tPublicKey string `dns:\"base64\"`\n}\n\nfunc (rr *DNSKEY) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +\n\t\t\" \" + strconv.Itoa(int(rr.Protocol)) +\n\t\t\" \" + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + rr.PublicKey\n}\n\n// IPSECKEY RR. See RFC 4025.\ntype IPSECKEY struct {\n\tHdr         RR_Header\n\tPrecedence  uint8\n\tGatewayType uint8\n\tAlgorithm   uint8\n\tGatewayAddr net.IP `dns:\"-\"` // packing/unpacking/parsing/etc handled together with GatewayHost\n\tGatewayHost string `dns:\"ipsechost\"`\n\tPublicKey   string `dns:\"base64\"`\n}\n\nfunc (rr *IPSECKEY) String() string {\n\tvar gateway string\n\tswitch rr.GatewayType {\n\tcase IPSECGatewayIPv4, IPSECGatewayIPv6:\n\t\tgateway = rr.GatewayAddr.String()\n\tcase IPSECGatewayHost:\n\t\tgateway = rr.GatewayHost\n\tcase IPSECGatewayNone:\n\t\tfallthrough\n\tdefault:\n\t\tgateway = \".\"\n\t}\n\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) +\n\t\t\" \" + strconv.Itoa(int(rr.GatewayType)) +\n\t\t\" \" + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + gateway +\n\t\t\" \" + rr.PublicKey\n}\n\n// AMTRELAY RR. See RFC 8777.\ntype AMTRELAY struct {\n\tHdr         RR_Header\n\tPrecedence  uint8\n\tGatewayType uint8  // discovery is packed in here at bit 0x80\n\tGatewayAddr net.IP `dns:\"-\"` // packing/unpacking/parsing/etc handled together with GatewayHost\n\tGatewayHost string `dns:\"amtrelayhost\"`\n}\n\nfunc (rr *AMTRELAY) String() string {\n\tvar gateway string\n\tswitch rr.GatewayType & 0x7f {\n\tcase AMTRELAYIPv4, AMTRELAYIPv6:\n\t\tgateway = rr.GatewayAddr.String()\n\tcase AMTRELAYHost:\n\t\tgateway = rr.GatewayHost\n\tcase AMTRELAYNone:\n\t\tfallthrough\n\tdefault:\n\t\tgateway = \".\"\n\t}\n\tboolS := \"0\"\n\tif rr.GatewayType&0x80 == 0x80 {\n\t\tboolS = \"1\"\n\t}\n\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) +\n\t\t\" \" + boolS +\n\t\t\" \" + strconv.Itoa(int(rr.GatewayType&0x7f)) +\n\t\t\" \" + gateway\n}\n\n// RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template.\ntype RKEY struct {\n\tHdr       RR_Header\n\tFlags     uint16\n\tProtocol  uint8\n\tAlgorithm uint8\n\tPublicKey string `dns:\"base64\"`\n}\n\nfunc (rr *RKEY) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +\n\t\t\" \" + strconv.Itoa(int(rr.Protocol)) +\n\t\t\" \" + strconv.Itoa(int(rr.Algorithm)) +\n\t\t\" \" + rr.PublicKey\n}\n\n// NSAPPTR RR. See RFC 1348.\ntype NSAPPTR struct {\n\tHdr RR_Header\n\tPtr string `dns:\"domain-name\"`\n}\n\nfunc (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }\n\n// NSEC3 RR. See RFC 5155.\ntype NSEC3 struct {\n\tHdr        RR_Header\n\tHash       uint8\n\tFlags      uint8\n\tIterations uint16\n\tSaltLength uint8\n\tSalt       string `dns:\"size-hex:SaltLength\"`\n\tHashLength uint8\n\tNextDomain string   `dns:\"size-base32:HashLength\"`\n\tTypeBitMap []uint16 `dns:\"nsec\"`\n}\n\nfunc (rr *NSEC3) String() string {\n\ts := rr.Hdr.String()\n\ts += strconv.Itoa(int(rr.Hash)) +\n\t\t\" \" + strconv.Itoa(int(rr.Flags)) +\n\t\t\" \" + strconv.Itoa(int(rr.Iterations)) +\n\t\t\" \" + saltToString(rr.Salt) +\n\t\t\" \" + rr.NextDomain\n\tfor _, t := range rr.TypeBitMap {\n\t\ts += \" \" + Type(t).String()\n\t}\n\treturn s\n}\n\nfunc (rr *NSEC3) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1\n\tl += typeBitMapLen(rr.TypeBitMap)\n\treturn l\n}\n\n// NSEC3PARAM RR. See RFC 5155.\ntype NSEC3PARAM struct {\n\tHdr        RR_Header\n\tHash       uint8\n\tFlags      uint8\n\tIterations uint16\n\tSaltLength uint8\n\tSalt       string `dns:\"size-hex:SaltLength\"`\n}\n\nfunc (rr *NSEC3PARAM) String() string {\n\ts := rr.Hdr.String()\n\ts += strconv.Itoa(int(rr.Hash)) +\n\t\t\" \" + strconv.Itoa(int(rr.Flags)) +\n\t\t\" \" + strconv.Itoa(int(rr.Iterations)) +\n\t\t\" \" + saltToString(rr.Salt)\n\treturn s\n}\n\n// TKEY RR. See RFC 2930.\ntype TKEY struct {\n\tHdr        RR_Header\n\tAlgorithm  string `dns:\"domain-name\"`\n\tInception  uint32\n\tExpiration uint32\n\tMode       uint16\n\tError      uint16\n\tKeySize    uint16\n\tKey        string `dns:\"size-hex:KeySize\"`\n\tOtherLen   uint16\n\tOtherData  string `dns:\"size-hex:OtherLen\"`\n}\n\n// TKEY has no official presentation format, but this will suffice.\nfunc (rr *TKEY) String() string {\n\ts := \";\" + rr.Hdr.String() +\n\t\t\" \" + rr.Algorithm +\n\t\t\" \" + TimeToString(rr.Inception) +\n\t\t\" \" + TimeToString(rr.Expiration) +\n\t\t\" \" + strconv.Itoa(int(rr.Mode)) +\n\t\t\" \" + strconv.Itoa(int(rr.Error)) +\n\t\t\" \" + strconv.Itoa(int(rr.KeySize)) +\n\t\t\" \" + rr.Key +\n\t\t\" \" + strconv.Itoa(int(rr.OtherLen)) +\n\t\t\" \" + rr.OtherData\n\treturn s\n}\n\n// RFC3597 represents an unknown/generic RR. See RFC 3597.\ntype RFC3597 struct {\n\tHdr   RR_Header\n\tRdata string `dns:\"hex\"`\n}\n\nfunc (rr *RFC3597) String() string {\n\t// Let's call it a hack\n\ts := rfc3597Header(rr.Hdr)\n\n\ts += \"\\\\# \" + strconv.Itoa(len(rr.Rdata)/2) + \" \" + rr.Rdata\n\treturn s\n}\n\nfunc rfc3597Header(h RR_Header) string {\n\tvar s string\n\n\ts += sprintName(h.Name) + \"\\t\"\n\ts += strconv.FormatInt(int64(h.Ttl), 10) + \"\\t\"\n\ts += \"CLASS\" + strconv.Itoa(int(h.Class)) + \"\\t\"\n\ts += \"TYPE\" + strconv.Itoa(int(h.Rrtype)) + \"\\t\"\n\treturn s\n}\n\n// URI RR. See RFC 7553.\ntype URI struct {\n\tHdr      RR_Header\n\tPriority uint16\n\tWeight   uint16\n\tTarget   string `dns:\"octet\"`\n}\n\n// rr.Target to be parsed as a sequence of character encoded octets according to RFC 3986\nfunc (rr *URI) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +\n\t\t\" \" + strconv.Itoa(int(rr.Weight)) + \" \" + sprintTxtOctet(rr.Target)\n}\n\n// DHCID RR. See RFC 4701.\ntype DHCID struct {\n\tHdr    RR_Header\n\tDigest string `dns:\"base64\"`\n}\n\nfunc (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }\n\n// TLSA RR. See RFC 6698.\ntype TLSA struct {\n\tHdr          RR_Header\n\tUsage        uint8\n\tSelector     uint8\n\tMatchingType uint8\n\tCertificate  string `dns:\"hex\"`\n}\n\nfunc (rr *TLSA) String() string {\n\treturn rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.Usage)) +\n\t\t\" \" + strconv.Itoa(int(rr.Selector)) +\n\t\t\" \" + strconv.Itoa(int(rr.MatchingType)) +\n\t\t\" \" + rr.Certificate\n}\n\n// SMIMEA RR. See RFC 8162.\ntype SMIMEA struct {\n\tHdr          RR_Header\n\tUsage        uint8\n\tSelector     uint8\n\tMatchingType uint8\n\tCertificate  string `dns:\"hex\"`\n}\n\nfunc (rr *SMIMEA) String() string {\n\ts := rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.Usage)) +\n\t\t\" \" + strconv.Itoa(int(rr.Selector)) +\n\t\t\" \" + strconv.Itoa(int(rr.MatchingType))\n\n\t// Every Nth char needs a space on this output. If we output\n\t// this as one giant line, we can't read it can in because in some cases\n\t// the cert length overflows scan.maxTok (2048).\n\tsx := splitN(rr.Certificate, 1024) // conservative value here\n\ts += \" \" + strings.Join(sx, \" \")\n\treturn s\n}\n\n// HIP RR. See RFC 8005.\ntype HIP struct {\n\tHdr                RR_Header\n\tHitLength          uint8\n\tPublicKeyAlgorithm uint8\n\tPublicKeyLength    uint16\n\tHit                string   `dns:\"size-hex:HitLength\"`\n\tPublicKey          string   `dns:\"size-base64:PublicKeyLength\"`\n\tRendezvousServers  []string `dns:\"domain-name\"`\n}\n\nfunc (rr *HIP) String() string {\n\ts := rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.PublicKeyAlgorithm)) +\n\t\t\" \" + rr.Hit +\n\t\t\" \" + rr.PublicKey\n\tfor _, d := range rr.RendezvousServers {\n\t\ts += \" \" + sprintName(d)\n\t}\n\treturn s\n}\n\n// NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template.\ntype NINFO struct {\n\tHdr    RR_Header\n\tZSData []string `dns:\"txt\"`\n}\n\nfunc (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }\n\n// NID RR. See RFC 6742.\ntype NID struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tNodeID     uint64\n}\n\nfunc (rr *NID) String() string {\n\ts := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))\n\tnode := fmt.Sprintf(\"%0.16x\", rr.NodeID)\n\ts += \" \" + node[0:4] + \":\" + node[4:8] + \":\" + node[8:12] + \":\" + node[12:16]\n\treturn s\n}\n\n// L32 RR, See RFC 6742.\ntype L32 struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tLocator32  net.IP `dns:\"a\"`\n}\n\nfunc (rr *L32) String() string {\n\tif rr.Locator32 == nil {\n\t\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference))\n\t}\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +\n\t\t\" \" + rr.Locator32.String()\n}\n\n// L64 RR, See RFC 6742.\ntype L64 struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tLocator64  uint64\n}\n\nfunc (rr *L64) String() string {\n\ts := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))\n\tnode := fmt.Sprintf(\"%0.16X\", rr.Locator64)\n\ts += \" \" + node[0:4] + \":\" + node[4:8] + \":\" + node[8:12] + \":\" + node[12:16]\n\treturn s\n}\n\n// LP RR. See RFC 6742.\ntype LP struct {\n\tHdr        RR_Header\n\tPreference uint16\n\tFqdn       string `dns:\"domain-name\"`\n}\n\nfunc (rr *LP) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + \" \" + sprintName(rr.Fqdn)\n}\n\n// EUI48 RR. See RFC 7043.\ntype EUI48 struct {\n\tHdr     RR_Header\n\tAddress uint64 `dns:\"uint48\"`\n}\n\nfunc (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }\n\n// EUI64 RR. See RFC 7043.\ntype EUI64 struct {\n\tHdr     RR_Header\n\tAddress uint64\n}\n\nfunc (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }\n\n// CAA RR. See RFC 6844.\ntype CAA struct {\n\tHdr   RR_Header\n\tFlag  uint8\n\tTag   string\n\tValue string `dns:\"octet\"`\n}\n\n// rr.Value Is the character-string encoding of the value field as specified in RFC 1035, Section 5.1.\nfunc (rr *CAA) String() string {\n\treturn rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + \" \" + rr.Tag + \" \" + sprintTxtOctet(rr.Value)\n}\n\n// UID RR. Deprecated, IANA-Reserved.\ntype UID struct {\n\tHdr RR_Header\n\tUid uint32\n}\n\nfunc (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }\n\n// GID RR. Deprecated, IANA-Reserved.\ntype GID struct {\n\tHdr RR_Header\n\tGid uint32\n}\n\nfunc (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }\n\n// UINFO RR. Deprecated, IANA-Reserved.\ntype UINFO struct {\n\tHdr   RR_Header\n\tUinfo string\n}\n\nfunc (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }\n\n// EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.\ntype EID struct {\n\tHdr      RR_Header\n\tEndpoint string `dns:\"hex\"`\n}\n\nfunc (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }\n\n// NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.\ntype NIMLOC struct {\n\tHdr     RR_Header\n\tLocator string `dns:\"hex\"`\n}\n\nfunc (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }\n\n// OPENPGPKEY RR. See RFC 7929.\ntype OPENPGPKEY struct {\n\tHdr       RR_Header\n\tPublicKey string `dns:\"base64\"`\n}\n\nfunc (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }\n\n// CSYNC RR. See RFC 7477.\ntype CSYNC struct {\n\tHdr        RR_Header\n\tSerial     uint32\n\tFlags      uint16\n\tTypeBitMap []uint16 `dns:\"nsec\"`\n}\n\nfunc (rr *CSYNC) String() string {\n\ts := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + \" \" + strconv.Itoa(int(rr.Flags))\n\n\tfor _, t := range rr.TypeBitMap {\n\t\ts += \" \" + Type(t).String()\n\t}\n\treturn s\n}\n\nfunc (rr *CSYNC) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 4 + 2\n\tl += typeBitMapLen(rr.TypeBitMap)\n\treturn l\n}\n\n// ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest\ntype ZONEMD struct {\n\tHdr    RR_Header\n\tSerial uint32\n\tScheme uint8\n\tHash   uint8\n\tDigest string `dns:\"hex\"`\n}\n\nfunc (rr *ZONEMD) String() string {\n\treturn rr.Hdr.String() +\n\t\tstrconv.Itoa(int(rr.Serial)) +\n\t\t\" \" + strconv.Itoa(int(rr.Scheme)) +\n\t\t\" \" + strconv.Itoa(int(rr.Hash)) +\n\t\t\" \" + rr.Digest\n}\n\n// RESINFO RR. See RFC 9606.\n\ntype RESINFO struct {\n\tHdr RR_Header\n\tTxt []string `dns:\"txt\"`\n}\n\nfunc (rr *RESINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }\n\n// APL RR. See RFC 3123.\ntype APL struct {\n\tHdr      RR_Header\n\tPrefixes []APLPrefix `dns:\"apl\"`\n}\n\n// APLPrefix is an address prefix hold by an APL record.\ntype APLPrefix struct {\n\tNegation bool\n\tNetwork  net.IPNet\n}\n\n// String returns presentation form of the APL record.\nfunc (rr *APL) String() string {\n\tvar sb strings.Builder\n\tsb.WriteString(rr.Hdr.String())\n\tfor i, p := range rr.Prefixes {\n\t\tif i > 0 {\n\t\t\tsb.WriteByte(' ')\n\t\t}\n\t\tsb.WriteString(p.str())\n\t}\n\treturn sb.String()\n}\n\n// str returns presentation form of the APL prefix.\nfunc (a *APLPrefix) str() string {\n\tvar sb strings.Builder\n\tif a.Negation {\n\t\tsb.WriteByte('!')\n\t}\n\n\tswitch len(a.Network.IP) {\n\tcase net.IPv4len:\n\t\tsb.WriteByte('1')\n\tcase net.IPv6len:\n\t\tsb.WriteByte('2')\n\t}\n\n\tsb.WriteByte(':')\n\n\tswitch len(a.Network.IP) {\n\tcase net.IPv4len:\n\t\tsb.WriteString(a.Network.IP.String())\n\tcase net.IPv6len:\n\t\t// add prefix for IPv4-mapped IPv6\n\t\tif v4 := a.Network.IP.To4(); v4 != nil {\n\t\t\tsb.WriteString(ipv4InIPv6Prefix)\n\t\t}\n\t\tsb.WriteString(a.Network.IP.String())\n\t}\n\n\tsb.WriteByte('/')\n\n\tprefix, _ := a.Network.Mask.Size()\n\tsb.WriteString(strconv.Itoa(prefix))\n\n\treturn sb.String()\n}\n\n// equals reports whether two APL prefixes are identical.\nfunc (a *APLPrefix) equals(b *APLPrefix) bool {\n\treturn a.Negation == b.Negation &&\n\t\ta.Network.IP.Equal(b.Network.IP) &&\n\t\tbytes.Equal(a.Network.Mask, b.Network.Mask)\n}\n\n// copy returns a copy of the APL prefix.\nfunc (a *APLPrefix) copy() APLPrefix {\n\treturn APLPrefix{\n\t\tNegation: a.Negation,\n\t\tNetwork:  copyNet(a.Network),\n\t}\n}\n\n// len returns size of the prefix in wire format.\nfunc (a *APLPrefix) len() int {\n\t// 4-byte header and the network address prefix (see Section 4 of RFC 3123)\n\tprefix, _ := a.Network.Mask.Size()\n\treturn 4 + (prefix+7)/8\n}\n\n// TimeToString translates the RRSIG's incep. and expir. times to the\n// string representation used when printing the record.\n// It takes serial arithmetic (RFC 1982) into account.\nfunc TimeToString(t uint32) string {\n\tmod := (int64(t)-time.Now().Unix())/year68 - 1\n\tif mod < 0 {\n\t\tmod = 0\n\t}\n\tti := time.Unix(int64(t)-mod*year68, 0).UTC()\n\treturn ti.Format(\"20060102150405\")\n}\n\n// StringToTime translates the RRSIG's incep. and expir. times from\n// string values like \"20110403154150\" to an 32 bit integer.\n// It takes serial arithmetic (RFC 1982) into account.\nfunc StringToTime(s string) (uint32, error) {\n\tt, err := time.Parse(\"20060102150405\", s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tmod := t.Unix()/year68 - 1\n\tif mod < 0 {\n\t\tmod = 0\n\t}\n\treturn uint32(t.Unix() - mod*year68), nil\n}\n\n// saltToString converts a NSECX salt to uppercase and returns \"-\" when it is empty.\nfunc saltToString(s string) string {\n\tif s == \"\" {\n\t\treturn \"-\"\n\t}\n\treturn strings.ToUpper(s)\n}\n\nfunc euiToString(eui uint64, bits int) (hex string) {\n\tswitch bits {\n\tcase 64:\n\t\thex = fmt.Sprintf(\"%16.16x\", eui)\n\t\thex = hex[0:2] + \"-\" + hex[2:4] + \"-\" + hex[4:6] + \"-\" + hex[6:8] +\n\t\t\t\"-\" + hex[8:10] + \"-\" + hex[10:12] + \"-\" + hex[12:14] + \"-\" + hex[14:16]\n\tcase 48:\n\t\thex = fmt.Sprintf(\"%12.12x\", eui)\n\t\thex = hex[0:2] + \"-\" + hex[2:4] + \"-\" + hex[4:6] + \"-\" + hex[6:8] +\n\t\t\t\"-\" + hex[8:10] + \"-\" + hex[10:12]\n\t}\n\treturn\n}\n\n// cloneSlice returns a shallow copy of s.\nfunc cloneSlice[E any, S ~[]E](s S) S {\n\tif s == nil {\n\t\treturn nil\n\t}\n\treturn append(S(nil), s...)\n}\n\n// copyNet returns a copy of a subnet.\nfunc copyNet(n net.IPNet) net.IPNet {\n\treturn net.IPNet{\n\t\tIP:   cloneSlice(n.IP),\n\t\tMask: cloneSlice(n.Mask),\n\t}\n}\n\n// SplitN splits a string into N sized string chunks.\n// This might become an exported function once.\nfunc splitN(s string, n int) []string {\n\tif len(s) < n {\n\t\treturn []string{s}\n\t}\n\tsx := []string{}\n\tp, i := 0, n\n\tfor {\n\t\tif i <= len(s) {\n\t\t\tsx = append(sx, s[p:i])\n\t\t} else {\n\t\t\tsx = append(sx, s[p:])\n\t\t\tbreak\n\n\t\t}\n\t\tp, i = p+n, i+n\n\t}\n\n\treturn sx\n}\n"
  },
  {
    "path": "types_generate.go",
    "content": "//go:build ignore\n// +build ignore\n\n// types_generate.go is meant to run with go generate. It will use\n// go/{importer,types} to track down all the RR struct types. Then for each type\n// it will generate conversion tables (TypeToRR and TypeToString) and banal\n// methods (len, Header, copy) based on the struct tags. The generated source is\n// written to ztypes.go, and is meant to be checked into git.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"text/template\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nvar skipLen = map[string]struct{}{\n\t\"NSEC\":  {},\n\t\"NSEC3\": {},\n\t\"OPT\":   {},\n\t\"CSYNC\": {},\n}\n\nvar packageHdr = `\n// Code generated by \"go run types_generate.go\"; DO NOT EDIT.\n\npackage dns\n\nimport (\n\t\"encoding/base64\"\n\t\"net\"\n)\n\n`\n\nvar TypeToRR = template.Must(template.New(\"TypeToRR\").Parse(`\n// TypeToRR is a map of constructors for each RR type.\nvar TypeToRR = map[uint16]func() RR{\n{{range .}}{{if ne . \"RFC3597\"}}  Type{{.}}:  func() RR { return new({{.}}) },\n{{end}}{{end}}                    }\n\n`))\n\nvar typeToString = template.Must(template.New(\"typeToString\").Parse(`\n// TypeToString is a map of strings for each RR type.\nvar TypeToString = map[uint16]string{\n{{range .}}{{if ne . \"NSAPPTR\"}}  Type{{.}}: \"{{.}}\",\n{{end}}{{end}}                    TypeNSAPPTR:    \"NSAP-PTR\",\n}\n\n`))\n\nvar headerFunc = template.Must(template.New(\"headerFunc\").Parse(`\n{{range .}}  func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr }\n{{end}}\n\n`))\n\n// getTypeStruct will take a type and the package scope, and return the\n// (innermost) struct if the type is considered a RR type (currently defined as\n// those structs beginning with a RR_Header, could be redefined as implementing\n// the RR interface). The bool return value indicates if embedded structs were\n// resolved.\nfunc getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {\n\tst, ok := t.Underlying().(*types.Struct)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tif st.NumFields() == 0 {\n\t\treturn nil, false\n\t}\n\tif st.Field(0).Type() == scope.Lookup(\"RR_Header\").Type() {\n\t\treturn st, false\n\t}\n\tif st.Field(0).Anonymous() {\n\t\tst, _ := getTypeStruct(st.Field(0).Type(), scope)\n\t\treturn st, true\n\t}\n\treturn nil, false\n}\n\n// loadModule retrieves package description for a given module.\nfunc loadModule(name string) (*types.Package, error) {\n\tconf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo}\n\tpkgs, err := packages.Load(&conf, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pkgs[0].Types, nil\n}\n\nfunc main() {\n\t// Import and type-check the package\n\tpkg, err := loadModule(\"github.com/miekg/dns\")\n\tfatalIfErr(err)\n\tscope := pkg.Scope()\n\n\t// Collect constants like TypeX\n\tvar numberedTypes []string\n\tfor _, name := range scope.Names() {\n\t\to := scope.Lookup(name)\n\t\tif o == nil || !o.Exported() {\n\t\t\tcontinue\n\t\t}\n\t\tb, ok := o.Type().(*types.Basic)\n\t\tif !ok || b.Kind() != types.Uint16 {\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.HasPrefix(o.Name(), \"Type\") {\n\t\t\tcontinue\n\t\t}\n\t\tname := strings.TrimPrefix(o.Name(), \"Type\")\n\t\tif name == \"PrivateRR\" {\n\t\t\tcontinue\n\t\t}\n\t\tnumberedTypes = append(numberedTypes, name)\n\t}\n\n\t// Collect actual types (*X)\n\tvar namedTypes []string\n\tfor _, name := range scope.Names() {\n\t\to := scope.Lookup(name)\n\t\tif o == nil || !o.Exported() {\n\t\t\tcontinue\n\t\t}\n\t\tif st, _ := getTypeStruct(o.Type(), scope); st == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif name == \"PrivateRR\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check if corresponding TypeX exists\n\t\tif scope.Lookup(\"Type\"+o.Name()) == nil && o.Name() != \"RFC3597\" {\n\t\t\tlog.Fatalf(\"Constant Type%s does not exist.\", o.Name())\n\t\t}\n\n\t\tnamedTypes = append(namedTypes, o.Name())\n\t}\n\n\tb := &bytes.Buffer{}\n\tb.WriteString(packageHdr)\n\n\t// Generate TypeToRR\n\tfatalIfErr(TypeToRR.Execute(b, namedTypes))\n\n\t// Generate typeToString\n\tfatalIfErr(typeToString.Execute(b, numberedTypes))\n\n\t// Generate headerFunc\n\tfatalIfErr(headerFunc.Execute(b, namedTypes))\n\n\t// Generate len()\n\tfmt.Fprint(b, \"// len() functions\\n\")\n\tfor _, name := range namedTypes {\n\t\tif _, ok := skipLen[name]; ok {\n\t\t\tcontinue\n\t\t}\n\t\to := scope.Lookup(name)\n\t\tst, isEmbedded := getTypeStruct(o.Type(), scope)\n\t\tif isEmbedded {\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Fprintf(b, \"func (rr *%s) len(off int, compression map[string]struct{}) int {\\n\", name)\n\t\tfmt.Fprintf(b, \"l := rr.Hdr.len(off, compression)\\n\")\n\t\tfor i := 1; i < st.NumFields(); i++ {\n\t\t\to := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }\n\n\t\t\tif _, ok := st.Field(i).Type().(*types.Slice); ok {\n\t\t\t\tswitch st.Tag(i) {\n\t\t\t\tcase `dns:\"-\"`:\n\t\t\t\t\t// ignored\n\t\t\t\tcase `dns:\"cdomain-name\"`:\n\t\t\t\t\to(\"for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, true) }\\n\")\n\t\t\t\tcase `dns:\"domain-name\"`:\n\t\t\t\t\to(\"for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\\n\")\n\t\t\t\tcase `dns:\"txt\"`:\n\t\t\t\t\to(\"for _, x := range rr.%s { l += len(x) + 1 }\\n\")\n\t\t\t\tcase `dns:\"apl\"`:\n\t\t\t\t\to(\"for _, x := range rr.%s { l += x.len() }\\n\")\n\t\t\t\tcase `dns:\"pairs\"`:\n\t\t\t\t\to(\"for _, x := range rr.%s { l += 4 + int(x.len()) }\\n\")\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch {\n\t\t\tcase st.Tag(i) == `dns:\"-\"`:\n\t\t\t\t// ignored\n\t\t\tcase st.Tag(i) == `dns:\"cdomain-name\"`:\n\t\t\t\to(\"l += domainNameLen(rr.%s, off+l, compression, true)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"domain-name\"`:\n\t\t\t\to(\"l += domainNameLen(rr.%s, off+l, compression, false)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"octet\"`:\n\t\t\t\to(\"l += len(rr.%s)\\n\")\n\t\t\tcase strings.HasPrefix(st.Tag(i), `dns:\"size-base64`):\n\t\t\t\tfallthrough\n\t\t\tcase st.Tag(i) == `dns:\"base64\"`:\n\t\t\t\to(\"l += base64.StdEncoding.DecodedLen(len(rr.%s))\\n\")\n\t\t\tcase strings.HasPrefix(st.Tag(i), `dns:\"size-hex:`): // this has an extra field where the length is stored\n\t\t\t\to(\"l += len(rr.%s)/2\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"hex\"`:\n\t\t\t\to(\"l += len(rr.%s)/2\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"any\"`:\n\t\t\t\to(\"l += len(rr.%s)\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"a\"`:\n\t\t\t\to(\"if len(rr.%s) != 0 { l += net.IPv4len }\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"aaaa\"`:\n\t\t\t\to(\"if len(rr.%s) != 0 { l += net.IPv6len }\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"txt\"`:\n\t\t\t\to(\"for _, t := range rr.%s { l += len(t) + 1 }\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"uint48\"`:\n\t\t\t\to(\"l += 6 // %s\\n\")\n\t\t\tcase st.Tag(i) == `dns:\"ipsechost\"`:\n\t\t\t\to(`switch rr.GatewayType {\n\t\t\t\tcase IPSECGatewayIPv4:\n\t\t\t\t\tl += net.IPv4len\n\t\t\t\tcase IPSECGatewayIPv6:\n\t\t\t\t\tl += net.IPv6len\n\t\t\t\tcase IPSECGatewayHost:\n\t\t\t\t\tl += len(rr.%s) + 1\n\t\t\t\t}\n\t\t\t\t`)\n\t\t\tcase st.Tag(i) == `dns:\"amtrelayhost\"`:\n\t\t\t\to(`switch rr.GatewayType {\n\t\t\t\tcase AMTRELAYIPv4:\n\t\t\t\t\tl += net.IPv4len\n\t\t\t\tcase AMTRELAYIPv6:\n\t\t\t\t\tl += net.IPv6len\n\t\t\t\tcase AMTRELAYHost:\n\t\t\t\t\tl += len(rr.%s) + 1\n\t\t\t\t}\n\t\t\t\t`)\n\t\t\tcase st.Tag(i) == `dns:\"amtrelaytype\"`:\n\t\t\t\to(\"l++ // %s\\n\")\n\t\t\tcase st.Tag(i) == \"\":\n\t\t\t\tswitch st.Field(i).Type().(*types.Basic).Kind() {\n\t\t\t\tcase types.Uint8:\n\t\t\t\t\to(\"l++ // %s\\n\")\n\t\t\t\tcase types.Uint16:\n\t\t\t\t\to(\"l += 2 // %s\\n\")\n\t\t\t\tcase types.Uint32:\n\t\t\t\t\to(\"l += 4 // %s\\n\")\n\t\t\t\tcase types.Uint64:\n\t\t\t\t\to(\"l += 8 // %s\\n\")\n\t\t\t\tcase types.String:\n\t\t\t\t\to(\"l += len(rr.%s) + 1\\n\")\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Fatalln(name, st.Field(i).Name())\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tlog.Fatalln(name, st.Field(i).Name(), st.Tag(i))\n\t\t\t}\n\t\t}\n\t\tfmt.Fprint(b, \"return l }\\n\\n\")\n\t}\n\n\t// Generate copy()\n\tfmt.Fprint(b, \"// copy() functions\\n\")\n\tfor _, name := range namedTypes {\n\t\to := scope.Lookup(name)\n\t\tst, isEmbedded := getTypeStruct(o.Type(), scope)\n\t\tfmt.Fprintf(b, \"func (rr *%s) copy() RR {\\n\", name)\n\t\tfields := make([]string, 0, st.NumFields())\n\t\tif isEmbedded {\n\t\t\ta, _ := o.Type().Underlying().(*types.Struct)\n\t\t\tparent := a.Field(0).Name()\n\t\t\tfields = append(fields, \"*rr.\"+parent+\".copy().(*\"+parent+\")\")\n\t\t\tgoto WriteCopy\n\t\t}\n\t\tfields = append(fields, \"rr.Hdr\")\n\t\tfor i := 1; i < st.NumFields(); i++ {\n\t\t\tf := st.Field(i).Name()\n\t\t\tif sl, ok := st.Field(i).Type().(*types.Slice); ok {\n\t\t\t\tt := sl.Underlying().String()\n\t\t\t\tt = strings.TrimPrefix(t, \"[]\")\n\t\t\t\tif idx := strings.LastIndex(t, \".\"); idx >= 0 {\n\t\t\t\t\tt = t[idx+1:]\n\t\t\t\t}\n\t\t\t\t// For the EDNS0 interface (and others), we need to call the copy method on each element.\n\t\t\t\tif t == \"EDNS0\" || t == \"APLPrefix\" || t == \"SVCBKeyValue\" {\n\t\t\t\t\tfmt.Fprintf(b, \"%s := make([]%s, len(rr.%s));\\nfor i,e := range rr.%s {\\n %s[i] = e.copy()\\n}\\n\",\n\t\t\t\t\t\tf, t, f, f, f)\n\t\t\t\t\tfields = append(fields, f)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfields = append(fields, \"cloneSlice(rr.\"+f+\")\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif st.Field(i).Type().String() == \"net.IP\" {\n\t\t\t\tfields = append(fields, \"cloneSlice(rr.\"+f+\")\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfields = append(fields, \"rr.\"+f)\n\t\t}\n\tWriteCopy:\n\t\tif len(fields) > 3 {\n\t\t\tfmt.Fprintf(b, \"return &%s{\\n%s,\\n}\\n\", name, strings.Join(fields, \",\\n\"))\n\t\t} else {\n\t\t\tfmt.Fprintf(b, \"return &%s{%s}\\n\", name, strings.Join(fields, \",\"))\n\t\t}\n\t\tfmt.Fprint(b, \"}\\n\\n\")\n\t}\n\n\t// gofmt\n\tres, err := format.Source(b.Bytes())\n\tif err != nil {\n\t\tb.WriteTo(os.Stderr)\n\t\tlog.Fatal(err)\n\t}\n\n\t// write result\n\tf, err := os.Create(\"ztypes.go\")\n\tfatalIfErr(err)\n\tdefer f.Close()\n\tf.Write(res)\n}\n\nfunc fatalIfErr(err error) {\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "types_test.go",
    "content": "package dns\n\nimport (\n\t\"testing\"\n)\n\nfunc TestCmToM(t *testing.T) {\n\ts := cmToM((0 << 4) + 0)\n\tif s != \"0.00\" {\n\t\tt.Error(\"0, 0\")\n\t}\n\n\ts = cmToM((1 << 4) + 0)\n\tif s != \"0.01\" {\n\t\tt.Error(\"1, 0\")\n\t}\n\n\ts = cmToM((3 << 4) + 1)\n\tif s != \"0.30\" {\n\t\tt.Error(\"3, 1\")\n\t}\n\n\ts = cmToM((4 << 4) + 2)\n\tif s != \"4\" {\n\t\tt.Error(\"4, 2\")\n\t}\n\n\ts = cmToM((5 << 4) + 3)\n\tif s != \"50\" {\n\t\tt.Error(\"5, 3\")\n\t}\n\n\ts = cmToM((7 << 4) + 5)\n\tif s != \"7000\" {\n\t\tt.Error(\"7, 5\")\n\t}\n\n\ts = cmToM((9 << 4) + 9)\n\tif s != \"90000000\" {\n\t\tt.Error(\"9, 9\")\n\t}\n}\n\nfunc TestSplitN(t *testing.T) {\n\txs := splitN(\"abc\", 5)\n\tif len(xs) != 1 && xs[0] != \"abc\" {\n\t\tt.Errorf(\"failure to split abc\")\n\t}\n\n\ts := \"\"\n\tfor i := 0; i < 255; i++ {\n\t\ts += \"a\"\n\t}\n\n\txs = splitN(s, 255)\n\tif len(xs) != 1 && xs[0] != s {\n\t\tt.Errorf(\"failure to split 255 char long string\")\n\t}\n\n\ts += \"b\"\n\txs = splitN(s, 255)\n\tif len(xs) != 2 || xs[1] != \"b\" {\n\t\tt.Errorf(\"failure to split 256 char long string: %d\", len(xs))\n\t}\n\n\t// Make s longer\n\tfor i := 0; i < 255; i++ {\n\t\ts += \"a\"\n\t}\n\txs = splitN(s, 255)\n\tif len(xs) != 3 || xs[2] != \"a\" {\n\t\tt.Errorf(\"failure to split 510 char long string: %d\", len(xs))\n\t}\n}\n\nfunc TestSprintName(t *testing.T) {\n\ttests := map[string]string{\n\t\t// Non-numeric escaping of special printable characters.\n\t\t\" '@;()\\\"\\\\..example\": `\\ \\'\\@\\;\\(\\)\\\"\\..example`,\n\t\t\"\\\\032\\\\039\\\\064\\\\059\\\\040\\\\041\\\\034\\\\046\\\\092.example\": `\\ \\'\\@\\;\\(\\)\\\"\\.\\\\.example`,\n\n\t\t// Numeric escaping of nonprintable characters.\n\t\t\"\\x00\\x07\\x09\\x0a\\x1f.\\x7f\\x80\\xad\\xef\\xff\":           `\\000\\007\\009\\010\\031.\\127\\128\\173\\239\\255`,\n\t\t\"\\\\000\\\\007\\\\009\\\\010\\\\031.\\\\127\\\\128\\\\173\\\\239\\\\255\": `\\000\\007\\009\\010\\031.\\127\\128\\173\\239\\255`,\n\n\t\t// No escaping of other printable characters, at least after a prior escape.\n\t\t\";[a-zA-Z0-9_]+/*.~\": `\\;[a-zA-Z0-9_]+/*.~`,\n\t\t\";\\\\091\\\\097\\\\045\\\\122\\\\065\\\\045\\\\090\\\\048\\\\045\\\\057\\\\095\\\\093\\\\043\\\\047\\\\042.\\\\126\": `\\;[a-zA-Z0-9_]+/*.~`,\n\t\t// \"\\\\091\\\\097\\\\045\\\\122\\\\065\\\\045\\\\090\\\\048\\\\045\\\\057\\\\095\\\\093\\\\043\\\\047\\\\042.\\\\126\": `[a-zA-Z0-9_]+/*.~`,\n\n\t\t// Incomplete \"dangling\" escapes are dropped regardless of prior escaping.\n\t\t\"a\\\\\": `a`,\n\t\t\";\\\\\": `\\;`,\n\n\t\t// Escaped dots stay escaped regardless of prior escaping.\n\t\t\"a\\\\.\\\\046.\\\\.\\\\046\": `a\\.\\..\\.\\.`,\n\t\t\"a\\\\046\\\\..\\\\046\\\\.\": `a\\.\\..\\.\\.`,\n\t}\n\tfor input, want := range tests {\n\t\tgot := sprintName(input)\n\t\tif got != want {\n\t\t\tt.Errorf(\"input %q: expected %q, got %q\", input, want, got)\n\t\t}\n\t}\n}\n\nfunc TestSprintTxtOctet(t *testing.T) {\n\tgot := sprintTxtOctet(\"abc\\\\.def\\007\\\"\\127@\\255\\x05\\xef\\\\\")\n\n\tif want := \"\\\"abc\\\\.def\\\\007\\\\\\\"W@\\\\173\\\\005\\\\239\\\"\"; got != want {\n\t\tt.Errorf(\"expected %q, got %q\", want, got)\n\t}\n}\n\nfunc TestSprintTxt(t *testing.T) {\n\tgot := sprintTxt([]string{\n\t\t\"abc\\\\.def\\007\\\"\\127@\\255\\x05\\xef\\\\\",\n\t\t\"example.com\",\n\t})\n\n\tif want := \"\\\"abc.def\\\\007\\\\\\\"W@\\\\173\\\\005\\\\239\\\" \\\"example.com\\\"\"; got != want {\n\t\tt.Errorf(\"expected %q, got %q\", want, got)\n\t}\n}\n\nfunc TestRPStringer(t *testing.T) {\n\trp := &RP{\n\t\tHdr: RR_Header{\n\t\t\tName:   \"test.example.com.\",\n\t\t\tRrtype: TypeRP,\n\t\t\tClass:  ClassINET,\n\t\t\tTtl:    600,\n\t\t},\n\t\tMbox: \"\\x05first.example.com.\",\n\t\tTxt:  \"second.\\x07example.com.\",\n\t}\n\n\tconst expected = \"test.example.com.\\t600\\tIN\\tRP\\t\\\\005first.example.com. second.\\\\007example.com.\"\n\tif rp.String() != expected {\n\t\tt.Errorf(\"expected %v, got %v\", expected, rp)\n\t}\n\n\t_, err := NewRR(rp.String())\n\tif err != nil {\n\t\tt.Fatalf(\"error parsing %q: %v\", rp, err)\n\t}\n}\n\nfunc BenchmarkSprintName(b *testing.B) {\n\tfor n := 0; n < b.N; n++ {\n\t\tgot := sprintName(\"abc\\\\.def\\007\\\"\\127@\\255\\x05\\xef\\\\\")\n\n\t\tif want := \"abc\\\\.def\\\\007\\\\\\\"W\\\\@\\\\173\\\\005\\\\239\"; got != want {\n\t\t\tb.Fatalf(\"expected %q, got %q\", want, got)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSprintName_NoEscape(b *testing.B) {\n\tfor n := 0; n < b.N; n++ {\n\t\tgot := sprintName(\"large.example.com\")\n\n\t\tif want := \"large.example.com\"; got != want {\n\t\t\tb.Fatalf(\"expected %q, got %q\", want, got)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSprintTxtOctet(b *testing.B) {\n\tfor n := 0; n < b.N; n++ {\n\t\tgot := sprintTxtOctet(\"abc\\\\.def\\007\\\"\\127@\\255\\x05\\xef\\\\\")\n\n\t\tif want := \"\\\"abc\\\\.def\\\\007\\\\\\\"W@\\\\173\\\\005\\\\239\\\"\"; got != want {\n\t\t\tb.Fatalf(\"expected %q, got %q\", want, got)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSprintTxt(b *testing.B) {\n\ttxt := []string{\n\t\t\"abc\\\\.def\\007\\\"\\127@\\255\\x05\\xef\\\\\",\n\t\t\"example.com\",\n\t}\n\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tgot := sprintTxt(txt)\n\n\t\tif want := \"\\\"abc.def\\\\007\\\\\\\"W@\\\\173\\\\005\\\\239\\\" \\\"example.com\\\"\"; got != want {\n\t\t\tb.Fatalf(\"expected %q, got %q\", got, want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "udp.go",
    "content": "//go:build !windows && !darwin\n// +build !windows,!darwin\n\npackage dns\n\nimport (\n\t\"net\"\n\n\t\"golang.org/x/net/ipv4\"\n\t\"golang.org/x/net/ipv6\"\n)\n\n// This is the required size of the OOB buffer to pass to ReadMsgUDP.\nvar udpOOBSize = func() int {\n\t// We can't know whether we'll get an IPv4 control message or an\n\t// IPv6 control message ahead of time. To get around this, we size\n\t// the buffer equal to the largest of the two.\n\n\toob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface)\n\toob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface)\n\n\tif len(oob4) > len(oob6) {\n\t\treturn len(oob4)\n\t}\n\n\treturn len(oob6)\n}()\n\n// SessionUDP holds the remote address and the associated\n// out-of-band data.\ntype SessionUDP struct {\n\traddr   *net.UDPAddr\n\tcontext []byte\n}\n\n// RemoteAddr returns the remote network address.\nfunc (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }\n\n// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a\n// net.UDPAddr.\nfunc ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {\n\toob := make([]byte, udpOOBSize)\n\tn, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)\n\tif err != nil {\n\t\treturn n, nil, err\n\t}\n\treturn n, &SessionUDP{raddr, oob[:oobn]}, err\n}\n\n// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.\nfunc WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {\n\toob := correctSource(session.context)\n\tn, _, err := conn.WriteMsgUDP(b, oob, session.raddr)\n\treturn n, err\n}\n\nfunc setUDPSocketOptions(conn *net.UDPConn) error {\n\t// Try setting the flags for both families and ignore the errors unless they\n\t// both error.\n\terr6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)\n\terr4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)\n\tif err6 != nil && err4 != nil {\n\t\treturn err4\n\t}\n\treturn nil\n}\n\n// parseDstFromOOB takes oob data and returns the destination IP.\nfunc parseDstFromOOB(oob []byte) net.IP {\n\t// Start with IPv6 and then fallback to IPv4\n\t// TODO(fastest963): Figure out a way to prefer one or the other. Looking at\n\t// the lvl of the header for a 0 or 41 isn't cross-platform.\n\tcm6 := new(ipv6.ControlMessage)\n\tif cm6.Parse(oob) == nil && cm6.Dst != nil {\n\t\treturn cm6.Dst\n\t}\n\tcm4 := new(ipv4.ControlMessage)\n\tif cm4.Parse(oob) == nil && cm4.Dst != nil {\n\t\treturn cm4.Dst\n\t}\n\treturn nil\n}\n\n// correctSource takes oob data and returns new oob data with the Src equal to the Dst\nfunc correctSource(oob []byte) []byte {\n\tdst := parseDstFromOOB(oob)\n\tif dst == nil {\n\t\treturn nil\n\t}\n\t// If the dst is definitely an IPv6, then use ipv6's ControlMessage to\n\t// respond otherwise use ipv4's because ipv6's marshal ignores ipv4\n\t// addresses.\n\tif dst.To4() == nil {\n\t\tcm := new(ipv6.ControlMessage)\n\t\tcm.Src = dst\n\t\toob = cm.Marshal()\n\t} else {\n\t\tcm := new(ipv4.ControlMessage)\n\t\tcm.Src = dst\n\t\toob = cm.Marshal()\n\t}\n\treturn oob\n}\n"
  },
  {
    "path": "udp_no_control.go",
    "content": "//go:build windows || darwin\n// +build windows darwin\n\n// TODO(tmthrgd): Remove this Windows-specific code if go.dev/issue/7175 and\n//   go.dev/issue/7174 are ever fixed.\n\n// NOTICE(stek29): darwin supports PKTINFO in sendmsg, but it unbinds sockets, see https://github.com/miekg/dns/issues/724\n\npackage dns\n\nimport \"net\"\n\n// SessionUDP holds the remote address\ntype SessionUDP struct {\n\traddr *net.UDPAddr\n}\n\n// RemoteAddr returns the remote network address.\nfunc (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }\n\n// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a\n// net.UDPAddr.\nfunc ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {\n\tn, raddr, err := conn.ReadFrom(b)\n\tif err != nil {\n\t\treturn n, nil, err\n\t}\n\treturn n, &SessionUDP{raddr.(*net.UDPAddr)}, err\n}\n\n// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.\nfunc WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {\n\treturn conn.WriteTo(b, session.raddr)\n}\n\nfunc setUDPSocketOptions(*net.UDPConn) error { return nil }\nfunc parseDstFromOOB([]byte, net.IP) net.IP  { return nil }\n"
  },
  {
    "path": "udp_test.go",
    "content": "//go:build linux && !appengine\n// +build linux,!appengine\n\npackage dns\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/net/ipv4\"\n\t\"golang.org/x/net/ipv6\"\n)\n\nfunc TestSetUDPSocketOptions(t *testing.T) {\n\t// returns an error if we cannot resolve that address\n\ttestFamily := func(n, addr string) error {\n\t\ta, err := net.ResolveUDPAddr(n, addr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc, err := net.ListenUDP(n, a)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := setUDPSocketOptions(c); err != nil {\n\t\t\tt.Fatalf(\"failed to set socket options: %v\", err)\n\t\t}\n\t\tch := make(chan *SessionUDP)\n\t\tgo func() {\n\t\t\t// Set some deadline so this goroutine doesn't hang forever\n\t\t\tc.SetReadDeadline(time.Now().Add(time.Minute))\n\t\t\tb := make([]byte, 1)\n\t\t\t_, sess, err := ReadFromSessionUDP(c, b)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"failed to read from conn: %v\", err)\n\t\t\t\t// fallthrough to chan send below\n\t\t\t}\n\t\t\tch <- sess\n\t\t}()\n\n\t\tc2, err := net.Dial(\"udp\", c.LocalAddr().String())\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to dial udp: %v\", err)\n\t\t}\n\t\tif _, err := c2.Write([]byte{1}); err != nil {\n\t\t\tt.Fatalf(\"failed to write to conn: %v\", err)\n\t\t}\n\t\tsess := <-ch\n\t\tif sess == nil {\n\t\t\t// t.Error was already called in the goroutine above.\n\t\t\tt.FailNow()\n\t\t}\n\t\tif len(sess.context) == 0 {\n\t\t\tt.Fatalf(\"empty session context: %v\", sess)\n\t\t}\n\t\tip := parseDstFromOOB(sess.context)\n\t\tif ip == nil {\n\t\t\tt.Fatalf(\"failed to parse dst: %v\", sess)\n\t\t}\n\t\tif !strings.Contains(c.LocalAddr().String(), ip.String()) {\n\t\t\tt.Fatalf(\"dst was different than listen addr: %v != %v\", ip.String(), c.LocalAddr().String())\n\t\t}\n\t\treturn nil\n\t}\n\n\t// we require that ipv4 be supported\n\tif err := testFamily(\"udp4\", \"127.0.0.1:0\"); err != nil {\n\t\tt.Fatalf(\"failed to test socket options on IPv4: %v\", err)\n\t}\n\t// IPv6 might not be supported so these will just log\n\tif err := testFamily(\"udp6\", \"[::1]:0\"); err != nil {\n\t\tt.Logf(\"failed to test socket options on IPv6-only: %v\", err)\n\t}\n\tif err := testFamily(\"udp\", \"[::1]:0\"); err != nil {\n\t\tt.Logf(\"failed to test socket options on IPv6/IPv4: %v\", err)\n\t}\n}\n\nfunc TestParseDstFromOOB(t *testing.T) {\n\tif runtime.GOARCH != \"amd64\" {\n\t\t// The cmsghdr struct differs in the width (32/64-bit) of\n\t\t// lengths and the struct padding between architectures.\n\t\t// The data below was only written with amd64 in mind, and\n\t\t// thus the test must be skipped on other architectures.\n\t\tt.Skip(\"skipping test on unsupported architecture\")\n\t}\n\n\t// dst is :ffff:100.100.100.100\n\toob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}\n\tdst := parseDstFromOOB(oob)\n\tdst4 := dst.To4()\n\tif dst4 == nil {\n\t\tt.Errorf(\"failed to parse IPv4 in IPv6: %v\", dst)\n\t} else if dst4.String() != \"100.100.100.100\" {\n\t\tt.Errorf(\"unexpected IPv4: %v\", dst4)\n\t}\n\n\t// dst is 2001:db8::1\n\toob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}\n\tdst = parseDstFromOOB(oob)\n\tdst6 := dst.To16()\n\tif dst6 == nil {\n\t\tt.Errorf(\"failed to parse IPv6: %v\", dst)\n\t} else if dst6.String() != \"2001:db8::1\" {\n\t\tt.Errorf(\"unexpected IPv6: %v\", dst4)\n\t}\n\n\t// dst is 100.100.100.100 but was received on 10.10.10.10\n\toob = []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10, 10, 100, 100, 100, 100, 0, 0, 0, 0}\n\tdst = parseDstFromOOB(oob)\n\tdst4 = dst.To4()\n\tif dst4 == nil {\n\t\tt.Errorf(\"failed to parse IPv4: %v\", dst)\n\t} else if dst4.String() != \"100.100.100.100\" {\n\t\tt.Errorf(\"unexpected IPv4: %v\", dst4)\n\t}\n}\n\nfunc TestCorrectSource(t *testing.T) {\n\tif runtime.GOARCH != \"amd64\" {\n\t\t// See comment above in TestParseDstFromOOB.\n\t\tt.Skip(\"skipping test on unsupported architecture\")\n\t}\n\n\t// dst is :ffff:100.100.100.100 which should be counted as IPv4\n\toob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}\n\tsoob := correctSource(oob)\n\tcm4 := new(ipv4.ControlMessage)\n\tcm4.Src = net.ParseIP(\"100.100.100.100\")\n\tif !bytes.Equal(soob, cm4.Marshal()) {\n\t\tt.Errorf(\"unexpected oob for ipv4 address: %v\", soob)\n\t}\n\n\t// dst is 2001:db8::1\n\toob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}\n\tsoob = correctSource(oob)\n\tcm6 := new(ipv6.ControlMessage)\n\tcm6.Src = net.ParseIP(\"2001:db8::1\")\n\tif !bytes.Equal(soob, cm6.Marshal()) {\n\t\tt.Errorf(\"unexpected oob for IPv6 address: %v\", soob)\n\t}\n}\n"
  },
  {
    "path": "update.go",
    "content": "package dns\n\n// NameUsed sets the RRs in the prereq section to\n// \"Name is in use\" RRs. RFC 2136 section 2.4.4.\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) NameUsed(rr []RR) {\n\tif u.Answer == nil {\n\t\tu.Answer = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\tu.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})\n\t}\n}\n\n// NameNotUsed sets the RRs in the prereq section to\n// \"Name is in not use\" RRs. RFC 2136 section 2.4.5.\nfunc (u *Msg) NameNotUsed(rr []RR) {\n\tif u.Answer == nil {\n\t\tu.Answer = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\tu.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}})\n\t}\n}\n\n// Used sets the RRs in the prereq section to\n// \"RRset exists (value dependent -- with rdata)\" RRs. RFC 2136 section 2.4.2.\nfunc (u *Msg) Used(rr []RR) {\n\tif len(u.Question) == 0 {\n\t\tpanic(\"dns: empty question section\")\n\t}\n\tif u.Answer == nil {\n\t\tu.Answer = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\thdr := r.Header()\n\t\thdr.Class = u.Question[0].Qclass\n\t\thdr.Ttl = 0\n\t\tu.Answer = append(u.Answer, r)\n\t}\n}\n\n// RRsetUsed sets the RRs in the prereq section to\n// \"RRset exists (value independent -- no rdata)\" RRs. RFC 2136 section 2.4.1.\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) RRsetUsed(rr []RR) {\n\tif u.Answer == nil {\n\t\tu.Answer = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\th := r.Header()\n\t\tu.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}})\n\t}\n}\n\n// RRsetNotUsed sets the RRs in the prereq section to\n// \"RRset does not exist\" RRs. RFC 2136 section 2.4.3.\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) RRsetNotUsed(rr []RR) {\n\tif u.Answer == nil {\n\t\tu.Answer = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\th := r.Header()\n\t\tu.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassNONE}})\n\t}\n}\n\n// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1.\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) Insert(rr []RR) {\n\tif len(u.Question) == 0 {\n\t\tpanic(\"dns: empty question section\")\n\t}\n\tif u.Ns == nil {\n\t\tu.Ns = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\tr.Header().Class = u.Question[0].Qclass\n\t\tu.Ns = append(u.Ns, r)\n\t}\n}\n\n// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) RemoveRRset(rr []RR) {\n\tif u.Ns == nil {\n\t\tu.Ns = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\th := r.Header()\n\t\tu.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}})\n\t}\n}\n\n// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) RemoveName(rr []RR) {\n\tif u.Ns == nil {\n\t\tu.Ns = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\tu.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})\n\t}\n}\n\n// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4\n// See [ANY] on how to make RRs without rdata.\nfunc (u *Msg) Remove(rr []RR) {\n\tif u.Ns == nil {\n\t\tu.Ns = make([]RR, 0, len(rr))\n\t}\n\tfor _, r := range rr {\n\t\th := r.Header()\n\t\th.Class = ClassNONE\n\t\th.Ttl = 0\n\t\tu.Ns = append(u.Ns, r)\n\t}\n}\n"
  },
  {
    "path": "update_test.go",
    "content": "package dns\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestDynamicUpdateParsing(t *testing.T) {\n\tconst prefix = \"example.com. IN \"\n\n\tfor typ, name := range TypeToString {\n\t\tswitch typ {\n\t\tcase TypeNone, TypeReserved:\n\t\t\tcontinue\n\t\tcase TypeANY:\n\t\t\t// ANY is ambiguous here and ends up parsed as a CLASS.\n\t\t\t//\n\t\t\t// TODO(tmthrgd): Using TYPE255 here doesn't seem to work and also\n\t\t\t//   seems to fail for some other record types. Investigate.\n\t\t\tcontinue\n\t\t}\n\n\t\ts := prefix + name\n\t\tif _, err := NewRR(s); err != nil {\n\t\t\tt.Errorf(\"failure to parse: %s: %v\", s, err)\n\t\t}\n\n\t\ts += \" \\\\# 0\"\n\t\tif _, err := NewRR(s); err != nil {\n\t\t\tt.Errorf(\"failure to parse: %s: %v\", s, err)\n\t\t}\n\t}\n}\n\nfunc TestDynamicUpdateUnpack(t *testing.T) {\n\t// From https://github.com/miekg/dns/issues/150#issuecomment-62296803\n\t// It should be an update message for the zone \"example.\",\n\t// deleting the A RRset \"example.\" and then adding an A record at \"example.\".\n\t// class ANY, TYPE A\n\tbuf := []byte{171, 68, 40, 0, 0, 1, 0, 0, 0, 2, 0, 0, 7, 101, 120, 97, 109, 112, 108, 101, 0, 0, 6, 0, 1, 192, 12, 0, 1, 0, 255, 0, 0, 0, 0, 0, 0, 192, 12, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 127, 0, 0, 1}\n\tmsg := new(Msg)\n\terr := msg.Unpack(buf)\n\tif err != nil {\n\t\tt.Errorf(\"failed to unpack: %v\\n%s\", err, msg.String())\n\t}\n}\n\nfunc TestDynamicUpdateZeroRdataUnpack(t *testing.T) {\n\tm := new(Msg)\n\trr := &RR_Header{Name: \".\", Rrtype: 0, Class: 1, Ttl: ^uint32(0), Rdlength: 0}\n\tm.Answer = []RR{rr, rr, rr, rr, rr}\n\tm.Ns = m.Answer\n\tfor n, s := range TypeToString {\n\t\trr.Rrtype = n\n\t\tbytes, err := m.Pack()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"failed to pack %s: %v\", s, err)\n\t\t\tcontinue\n\t\t}\n\t\tif err := new(Msg).Unpack(bytes); err != nil {\n\t\t\tt.Errorf(\"failed to unpack %s: %v\", s, err)\n\t\t}\n\t}\n}\n\nfunc TestRemoveRRset(t *testing.T) {\n\t// Should add a zero data RR in Class ANY with a TTL of 0\n\t// for each set mentioned in the RRs provided to it.\n\trr := testRR(\". 100 IN A 127.0.0.1\")\n\tm := new(Msg)\n\tm.Ns = []RR{&RR_Header{Name: \".\", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}}\n\texpectstr := m.String()\n\texpect, err := m.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"error packing expected msg: %v\", err)\n\t}\n\n\tm.Ns = nil\n\tm.RemoveRRset([]RR{rr})\n\tactual, err := m.Pack()\n\tif err != nil {\n\t\tt.Fatalf(\"error packing actual msg: %v\", err)\n\t}\n\tif !bytes.Equal(actual, expect) {\n\t\ttmp := new(Msg)\n\t\tif err := tmp.Unpack(actual); err != nil {\n\t\t\tt.Fatalf(\"error unpacking actual msg: %v\\nexpected: %v\\ngot: %v\\n\", err, expect, actual)\n\t\t}\n\t\tt.Errorf(\"expected msg:\\n%s\", expectstr)\n\t\tt.Errorf(\"actual msg:\\n%v\", tmp)\n\t}\n}\n\nfunc TestPreReqAndRemovals(t *testing.T) {\n\t// Build a list of multiple prereqs and then some removes followed by an insert.\n\t// We should be able to add multiple prereqs and updates.\n\tm := new(Msg)\n\tm.SetUpdate(\"example.org.\")\n\tm.Id = 1234\n\n\t// Use a full set of RRs each time, so we are sure the rdata is stripped.\n\trrName1 := testRR(\"name_used. 3600 IN A 127.0.0.1\")\n\trrName2 := testRR(\"name_not_used. 3600 IN A 127.0.0.1\")\n\trrRemove1 := testRR(\"remove1. 3600 IN A 127.0.0.1\")\n\trrRemove2 := testRR(\"remove2. 3600 IN A 127.0.0.1\")\n\trrRemove3 := testRR(\"remove3. 3600 IN A 127.0.0.1\")\n\trrInsert := testRR(\"insert. 3600 IN A 127.0.0.1\")\n\trrRrset1 := testRR(\"rrset_used1. 3600 IN A 127.0.0.1\")\n\trrRrset2 := testRR(\"rrset_used2. 3600 IN A 127.0.0.1\")\n\trrRrset3 := testRR(\"rrset_not_used. 3600 IN A 127.0.0.1\")\n\n\t// Handle the prereqs.\n\tm.NameUsed([]RR{rrName1})\n\tm.NameNotUsed([]RR{rrName2})\n\tm.RRsetUsed([]RR{rrRrset1})\n\tm.Used([]RR{rrRrset2})\n\tm.RRsetNotUsed([]RR{rrRrset3})\n\n\t// and now the updates.\n\tm.RemoveName([]RR{rrRemove1})\n\tm.RemoveRRset([]RR{rrRemove2})\n\tm.Remove([]RR{rrRemove3})\n\tm.Insert([]RR{rrInsert})\n\n\t// This test function isn't a Example function because we print these RR with tabs at the\n\t// end and the Example function trim these, thus they never match.\n\t// TODO(miek): don't print these tabs and make this into an Example function.\n\texpect := `;; opcode: UPDATE, status: NOERROR, id: 1234\n;; flags:; ZONE: 1, PREREQ: 5, UPDATE: 4, ADDITIONAL: 0\n\n;; ZONE SECTION:\n;example.org.\tIN\t SOA\n\n;; PREREQUISITE SECTION:\nname_used.\t0\tCLASS255\tANY\t\nname_not_used.\t0\tNONE\tANY\t\nrrset_used1.\t0\tCLASS255\tA\t\nrrset_used2.\t0\tIN\tA\t127.0.0.1\nrrset_not_used.\t0\tNONE\tA\t\n\n;; UPDATE SECTION:\nremove1.\t0\tCLASS255\tANY\t\nremove2.\t0\tCLASS255\tA\t\nremove3.\t0\tNONE\tA\t127.0.0.1\ninsert.\t3600\tIN\tA\t127.0.0.1\n`\n\n\tif m.String() != expect {\n\t\tt.Errorf(\"expected msg:\\n%s\", expect)\n\t\tt.Errorf(\"actual msg:\\n%v\", m.String())\n\t}\n}\n"
  },
  {
    "path": "version.go",
    "content": "package dns\n\nimport \"fmt\"\n\n// Version is current version of this library.\nvar Version = v{1, 1, 72}\n\n// v holds the version of this library.\ntype v struct {\n\tMajor, Minor, Patch int\n}\n\nfunc (v v) String() string {\n\treturn fmt.Sprintf(\"%d.%d.%d\", v.Major, v.Minor, v.Patch)\n}\n"
  },
  {
    "path": "version_test.go",
    "content": "package dns\n\nimport \"testing\"\n\nfunc TestVersion(t *testing.T) {\n\tv := v{1, 0, 0}\n\tif x := v.String(); x != \"1.0.0\" {\n\t\tt.Fatalf(\"Failed to convert version %v, got: %s\", v, x)\n\t}\n}\n"
  },
  {
    "path": "xfr.go",
    "content": "package dns\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// Envelope is used when doing a zone transfer with a remote server.\ntype Envelope struct {\n\tRR    []RR  // The set of RRs in the answer section of the xfr reply message.\n\tError error // If something went wrong, this contains the error.\n}\n\n// A Transfer defines parameters that are used during a zone transfer.\ntype Transfer struct {\n\t*Conn\n\tDialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds\n\tReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds\n\tWriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds\n\tTsigProvider   TsigProvider      // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.\n\tTsigSecret     map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)\n\ttsigTimersOnly bool\n\tTLS            *tls.Config // TLS config. If Xfr over TLS will be attempted\n}\n\nfunc (t *Transfer) tsigProvider() TsigProvider {\n\tif t.TsigProvider != nil {\n\t\treturn t.TsigProvider\n\t}\n\tif t.TsigSecret != nil {\n\t\treturn tsigSecretProvider(t.TsigSecret)\n\t}\n\treturn nil\n}\n\n// TODO: Think we need to away to stop the transfer\n\n// In performs an incoming transfer with the server in a.\n// If you would like to set the source IP, or some other attribute\n// of a Dialer for a Transfer, you can do so by specifying the attributes\n// in the Transfer.Conn:\n//\n//\td := net.Dialer{LocalAddr: transfer_source}\n//\tcon, err := d.Dial(\"tcp\", master)\n//\tdnscon := &dns.Conn{Conn:con}\n//\ttransfer = &dns.Transfer{Conn: dnscon}\n//\tchannel, err := transfer.In(message, master)\nfunc (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {\n\tswitch q.Question[0].Qtype {\n\tcase TypeAXFR, TypeIXFR:\n\tdefault:\n\t\treturn nil, &Error{\"unsupported question type\"}\n\t}\n\n\ttimeout := dnsTimeout\n\tif t.DialTimeout != 0 {\n\t\ttimeout = t.DialTimeout\n\t}\n\n\tif t.Conn == nil {\n\t\tif t.TLS != nil {\n\t\t\tt.Conn, err = DialTimeoutWithTLS(\"tcp-tls\", a, t.TLS, timeout)\n\t\t} else {\n\t\t\tt.Conn, err = DialTimeout(\"tcp\", a, timeout)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := t.WriteMsg(q); err != nil {\n\t\treturn nil, err\n\t}\n\n\tenv = make(chan *Envelope)\n\tswitch q.Question[0].Qtype {\n\tcase TypeAXFR:\n\t\tgo t.inAxfr(q, env)\n\tcase TypeIXFR:\n\t\tgo t.inIxfr(q, env)\n\t}\n\n\treturn env, nil\n}\n\nfunc (t *Transfer) inAxfr(q *Msg, c chan *Envelope) {\n\tfirst := true\n\tdefer func() {\n\t\t// First close the connection, then the channel. This allows functions blocked on\n\t\t// the channel to assume that the connection is closed and no further operations are\n\t\t// pending when they resume.\n\t\tt.Close()\n\t\tclose(c)\n\t}()\n\ttimeout := dnsTimeout\n\tif t.ReadTimeout != 0 {\n\t\ttimeout = t.ReadTimeout\n\t}\n\tfor {\n\t\tt.Conn.SetReadDeadline(time.Now().Add(timeout))\n\t\tin, err := t.ReadMsg()\n\t\tif err != nil {\n\t\t\tc <- &Envelope{nil, err}\n\t\t\treturn\n\t\t}\n\t\tif q.Id != in.Id {\n\t\t\tc <- &Envelope{in.Answer, ErrId}\n\t\t\treturn\n\t\t}\n\t\tif first {\n\t\t\tif in.Rcode != RcodeSuccess {\n\t\t\t\tc <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !isSOAFirst(in) {\n\t\t\t\tc <- &Envelope{in.Answer, ErrSoa}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfirst = !first\n\t\t\t// only one answer that is SOA, receive more\n\t\t\tif len(in.Answer) == 1 {\n\t\t\t\tt.tsigTimersOnly = true\n\t\t\t\tc <- &Envelope{in.Answer, nil}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif !first {\n\t\t\tt.tsigTimersOnly = true // Subsequent envelopes use this.\n\t\t\tif isSOALast(in) {\n\t\t\t\tc <- &Envelope{in.Answer, nil}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tc <- &Envelope{in.Answer, nil}\n\t\t}\n\t}\n}\n\nfunc (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {\n\tvar serial uint32 // The first serial seen is the current server serial\n\taxfr := true\n\tn := 0\n\tqser := q.Ns[0].(*SOA).Serial\n\tdefer func() {\n\t\t// First close the connection, then the channel. This allows functions blocked on\n\t\t// the channel to assume that the connection is closed and no further operations are\n\t\t// pending when they resume.\n\t\tt.Close()\n\t\tclose(c)\n\t}()\n\ttimeout := dnsTimeout\n\tif t.ReadTimeout != 0 {\n\t\ttimeout = t.ReadTimeout\n\t}\n\tfor {\n\t\tt.SetReadDeadline(time.Now().Add(timeout))\n\t\tin, err := t.ReadMsg()\n\t\tif err != nil {\n\t\t\tc <- &Envelope{nil, err}\n\t\t\treturn\n\t\t}\n\t\tif q.Id != in.Id {\n\t\t\tc <- &Envelope{in.Answer, ErrId}\n\t\t\treturn\n\t\t}\n\t\tif in.Rcode != RcodeSuccess {\n\t\t\tc <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}\n\t\t\treturn\n\t\t}\n\t\tif n == 0 {\n\t\t\t// Check if the returned answer is ok\n\t\t\tif !isSOAFirst(in) {\n\t\t\t\tc <- &Envelope{in.Answer, ErrSoa}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// This serial is important\n\t\t\tserial = in.Answer[0].(*SOA).Serial\n\t\t\t// Check if there are no changes in zone\n\t\t\tif qser >= serial {\n\t\t\t\tc <- &Envelope{in.Answer, nil}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// Now we need to check each message for SOA records, to see what we need to do\n\t\tt.tsigTimersOnly = true\n\t\tfor _, rr := range in.Answer {\n\t\t\tif v, ok := rr.(*SOA); ok {\n\t\t\t\tif v.Serial == serial {\n\t\t\t\t\tn++\n\t\t\t\t\t// quit if it's a full axfr or the servers' SOA is repeated the third time\n\t\t\t\t\tif axfr && n == 2 || n == 3 {\n\t\t\t\t\t\tc <- &Envelope{in.Answer, nil}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else if axfr {\n\t\t\t\t\t// it's an ixfr\n\t\t\t\t\taxfr = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tc <- &Envelope{in.Answer, nil}\n\t}\n}\n\n// Out performs an outgoing transfer with the client connecting in w.\n// Basic use pattern:\n//\n//\tch := make(chan *dns.Envelope)\n//\ttr := new(dns.Transfer)\n//\tvar wg sync.WaitGroup\n//\twg.Add(1)\n//\tgo func() {\n//\t\ttr.Out(w, r, ch)\n//\t\twg.Done()\n//\t}()\n//\tch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}\n//\tclose(ch)\n//\twg.Wait() // wait until everything is written out\n//\tw.Close() // close connection\n//\n// The server is responsible for sending the correct sequence of RRs through the channel ch.\nfunc (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {\n\tfor x := range ch {\n\t\tr := new(Msg)\n\t\t// Compress?\n\t\tr.SetReply(q)\n\t\tr.Authoritative = true\n\t\t// assume it fits TODO(miek): fix\n\t\tr.Answer = append(r.Answer, x.RR...)\n\t\tif tsig := q.IsTsig(); tsig != nil && w.TsigStatus() == nil {\n\t\t\tr.SetTsig(tsig.Hdr.Name, tsig.Algorithm, tsig.Fudge, time.Now().Unix())\n\t\t}\n\t\tif err := w.WriteMsg(r); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tw.TsigTimersOnly(true)\n\t}\n\treturn nil\n}\n\n// ReadMsg reads a message from the transfer connection t.\nfunc (t *Transfer) ReadMsg() (*Msg, error) {\n\tm := new(Msg)\n\tp := make([]byte, MaxMsgSize)\n\tn, err := t.Read(p)\n\tif err != nil && n == 0 {\n\t\treturn nil, err\n\t}\n\tp = p[:n]\n\tif err := m.Unpack(p); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif tp := t.tsigProvider(); tp != nil {\n\t\t// Need to work on the original message p, as that was used to calculate the tsig.\n\t\terr = TsigVerifyWithProvider(p, tp, t.tsigRequestMAC, t.tsigTimersOnly)\n\t\tif ts := m.IsTsig(); ts != nil {\n\t\t\tt.tsigRequestMAC = ts.MAC\n\t\t}\n\t}\n\treturn m, err\n}\n\n// WriteMsg writes a message through the transfer connection t.\nfunc (t *Transfer) WriteMsg(m *Msg) (err error) {\n\tvar out []byte\n\tif ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {\n\t\tout, t.tsigRequestMAC, err = TsigGenerateWithProvider(m, tp, t.tsigRequestMAC, t.tsigTimersOnly)\n\t} else {\n\t\tout, err = m.Pack()\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = t.Write(out)\n\treturn err\n}\n\nfunc isSOAFirst(in *Msg) bool {\n\treturn len(in.Answer) > 0 &&\n\t\tin.Answer[0].Header().Rrtype == TypeSOA\n}\n\nfunc isSOALast(in *Msg) bool {\n\treturn len(in.Answer) > 0 &&\n\t\tin.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA\n}\n\nconst errXFR = \"bad xfr rcode: %d\"\n"
  },
  {
    "path": "xfr_test.go",
    "content": "package dns\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar (\n\ttsigSecret  = map[string]string{\"axfr.\": \"so6ZGir4GPAqINNh9U5c3A==\"}\n\txfrSoa      = testRR(`miek.nl.\t0\tIN\tSOA\tlinode.atoom.net. miek.miek.nl. 2009032802 21600 7200 604800 3600`)\n\txfrA        = testRR(`x.miek.nl.\t1792\tIN\tA\t10.0.0.1`)\n\txfrMX       = testRR(`miek.nl.\t1800\tIN\tMX\t1\tx.miek.nl.`)\n\txfrTestData = []RR{xfrSoa, xfrA, xfrMX, xfrSoa}\n)\n\nfunc InvalidXfrServer(w ResponseWriter, req *Msg) {\n\tch := make(chan *Envelope)\n\ttr := new(Transfer)\n\n\tgo tr.Out(w, req, ch)\n\tch <- &Envelope{RR: []RR{}}\n\tclose(ch)\n\tw.Hijack()\n}\n\nfunc SingleEnvelopeXfrServer(w ResponseWriter, req *Msg) {\n\tch := make(chan *Envelope)\n\ttr := new(Transfer)\n\n\tgo tr.Out(w, req, ch)\n\tch <- &Envelope{RR: xfrTestData}\n\tclose(ch)\n\tw.Hijack()\n}\n\nfunc MultipleEnvelopeXfrServer(w ResponseWriter, req *Msg) {\n\tch := make(chan *Envelope)\n\ttr := new(Transfer)\n\n\tgo tr.Out(w, req, ch)\n\n\tfor _, rr := range xfrTestData {\n\t\tch <- &Envelope{RR: []RR{rr}}\n\t}\n\tclose(ch)\n\tw.Hijack()\n}\n\nfunc TestInvalidXfr(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", InvalidXfrServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\ttr := new(Transfer)\n\tm := new(Msg)\n\tm.SetAxfr(\"miek.nl.\")\n\n\tc, err := tr.In(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to zone transfer in\", err)\n\t}\n\n\tfor msg := range c {\n\t\tif msg.Error == nil {\n\t\t\tt.Fatal(\"failed to catch 'no SOA' error\")\n\t\t}\n\t}\n}\n\nfunc TestSingleEnvelopeXfr(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", SingleEnvelopeXfrServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\", func(srv *Server) {\n\t\tsrv.TsigSecret = tsigSecret\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\taxfrTestingSuite(t, addrstr)\n}\n\nfunc TestSingleEnvelopeXfrTLS(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", SingleEnvelopeXfrServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\tcert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to build certificate: %v\", err)\n\t}\n\n\ttlsConfig := tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\ts, addrstr, _, err := RunLocalTLSServer(\":0\", &tlsConfig)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\taxfrTestingSuiteTLS(t, addrstr)\n}\n\nfunc TestMultiEnvelopeXfr(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", MultipleEnvelopeXfrServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\", func(srv *Server) {\n\t\tsrv.TsigSecret = tsigSecret\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\taxfrTestingSuite(t, addrstr)\n}\n\nfunc axfrTestingSuite(t *testing.T, addrstr string) {\n\ttr := new(Transfer)\n\tm := new(Msg)\n\tm.SetAxfr(\"miek.nl.\")\n\n\tc, err := tr.In(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to zone transfer in\", err)\n\t}\n\n\tvar records []RR\n\tfor msg := range c {\n\t\tif msg.Error != nil {\n\t\t\tt.Fatal(msg.Error)\n\t\t}\n\t\trecords = append(records, msg.RR...)\n\t}\n\n\tif len(records) != len(xfrTestData) {\n\t\tt.Fatalf(\"bad axfr: expected %v, got %v\", records, xfrTestData)\n\t}\n\n\tfor i, rr := range records {\n\t\tif !IsDuplicate(rr, xfrTestData[i]) {\n\t\t\tt.Fatalf(\"bad axfr: expected %v, got %v\", records, xfrTestData)\n\t\t}\n\t}\n}\n\nfunc axfrTestingSuiteTLS(t *testing.T, addrstr string) {\n\ttr := new(Transfer)\n\tm := new(Msg)\n\tm.SetAxfr(\"miek.nl.\")\n\n\ttr.TLS = &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t}\n\tc, err := tr.In(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to zone transfer in\", err)\n\t}\n\n\tvar records []RR\n\tfor msg := range c {\n\t\tif msg.Error != nil {\n\t\t\tt.Fatal(msg.Error)\n\t\t}\n\t\trecords = append(records, msg.RR...)\n\t}\n\n\tif len(records) != len(xfrTestData) {\n\t\tt.Fatalf(\"bad axfr: expected %v, got %v\", records, xfrTestData)\n\t}\n\n\tfor i, rr := range records {\n\t\tif !IsDuplicate(rr, xfrTestData[i]) {\n\t\t\tt.Fatalf(\"bad axfr: expected %v, got %v\", records, xfrTestData)\n\t\t}\n\t}\n}\n\nfunc axfrTestingSuiteWithCustomTsig(t *testing.T, addrstr string, provider TsigProvider) {\n\ttr := new(Transfer)\n\tm := new(Msg)\n\tvar err error\n\ttr.Conn, err = Dial(\"tcp\", addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to dial\", err)\n\t}\n\ttr.TsigProvider = provider\n\tm.SetAxfr(\"miek.nl.\")\n\tm.SetTsig(\"axfr.\", HmacSHA256, 300, time.Now().Unix())\n\n\tc, err := tr.In(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to zone transfer in\", err)\n\t}\n\n\tvar records []RR\n\tfor msg := range c {\n\t\tif msg.Error != nil {\n\t\t\tt.Fatal(msg.Error)\n\t\t}\n\t\trecords = append(records, msg.RR...)\n\t}\n\n\tif len(records) != len(xfrTestData) {\n\t\tt.Fatalf(\"bad axfr: expected %v, got %v\", records, xfrTestData)\n\t}\n\n\tfor i, rr := range records {\n\t\tif !IsDuplicate(rr, xfrTestData[i]) {\n\t\t\tt.Errorf(\"bad axfr: expected %v, got %v\", records, xfrTestData)\n\t\t}\n\t}\n}\n\nfunc axfrTestingSuiteWithMsgNotSigned(t *testing.T, addrstr string, provider TsigProvider) {\n\ttr := new(Transfer)\n\tm := new(Msg)\n\tvar err error\n\ttr.Conn, err = Dial(\"tcp\", addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to dial\", err)\n\t}\n\ttr.TsigProvider = provider\n\tm.SetAxfr(\"miek.nl.\")\n\n\tc, err := tr.In(m, addrstr)\n\tif err != nil {\n\t\tt.Fatal(\"failed to zone transfer in\", err)\n\t}\n\n\tfor msg := range c {\n\t\tif !errors.Is(msg.Error, ErrNoSig) {\n\t\t\tt.Fatal(\"expecting ErrNoSig error\")\n\t\t}\n\t}\n}\n\nfunc TestCustomTsigProvider(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", SingleEnvelopeXfrServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\", func(srv *Server) {\n\t\tsrv.TsigProvider = tsigSecretProvider(tsigSecret)\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\taxfrTestingSuiteWithCustomTsig(t, addrstr, tsigSecretProvider(tsigSecret))\n}\n\nfunc TestTSIGNotSigned(t *testing.T) {\n\tHandleFunc(\"miek.nl.\", SingleEnvelopeXfrServer)\n\tdefer HandleRemove(\"miek.nl.\")\n\n\ts, addrstr, _, err := RunLocalTCPServer(\":0\")\n\tif err != nil {\n\t\tt.Fatalf(\"unable to run test server: %s\", err)\n\t}\n\tdefer s.Shutdown()\n\n\taxfrTestingSuiteWithMsgNotSigned(t, addrstr, tsigSecretProvider(tsigSecret))\n}\n"
  },
  {
    "path": "zduplicate.go",
    "content": "// Code generated by \"go run duplicate_generate.go\"; DO NOT EDIT.\n\npackage dns\n\n// isDuplicate() functions\n\nfunc (r1 *A) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*A)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !r1.A.Equal(r2.A) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *AAAA) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*AAAA)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !r1.AAAA.Equal(r2.AAAA) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *AFSDB) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*AFSDB)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Subtype != r2.Subtype {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Hostname, r2.Hostname) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *AMTRELAY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*AMTRELAY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Precedence != r2.Precedence {\n\t\treturn false\n\t}\n\tif r1.GatewayType != r2.GatewayType {\n\t\treturn false\n\t}\n\tswitch r1.GatewayType {\n\tcase IPSECGatewayIPv4, IPSECGatewayIPv6:\n\t\tif !r1.GatewayAddr.Equal(r2.GatewayAddr) {\n\t\t\treturn false\n\t\t}\n\tcase IPSECGatewayHost:\n\t\tif !isDuplicateName(r1.GatewayHost, r2.GatewayHost) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc (r1 *ANY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*ANY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\treturn true\n}\n\nfunc (r1 *APL) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*APL)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif len(r1.Prefixes) != len(r2.Prefixes) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.Prefixes); i++ {\n\t\tif !r1.Prefixes[i].equals(&r2.Prefixes[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *AVC) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*AVC)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif len(r1.Txt) != len(r2.Txt) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.Txt); i++ {\n\t\tif r1.Txt[i] != r2.Txt[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *CAA) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*CAA)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Flag != r2.Flag {\n\t\treturn false\n\t}\n\tif r1.Tag != r2.Tag {\n\t\treturn false\n\t}\n\tif r1.Value != r2.Value {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *CDNSKEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*CDNSKEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Protocol != r2.Protocol {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *CDS) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*CDS)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.DigestType != r2.DigestType {\n\t\treturn false\n\t}\n\tif r1.Digest != r2.Digest {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *CERT) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*CERT)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Type != r2.Type {\n\t\treturn false\n\t}\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.Certificate != r2.Certificate {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *CNAME) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*CNAME)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Target, r2.Target) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *CSYNC) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*CSYNC)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Serial != r2.Serial {\n\t\treturn false\n\t}\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif len(r1.TypeBitMap) != len(r2.TypeBitMap) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.TypeBitMap); i++ {\n\t\tif r1.TypeBitMap[i] != r2.TypeBitMap[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *DHCID) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*DHCID)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Digest != r2.Digest {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *DLV) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*DLV)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.DigestType != r2.DigestType {\n\t\treturn false\n\t}\n\tif r1.Digest != r2.Digest {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *DNAME) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*DNAME)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Target, r2.Target) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *DNSKEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*DNSKEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Protocol != r2.Protocol {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *DS) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*DS)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.DigestType != r2.DigestType {\n\t\treturn false\n\t}\n\tif r1.Digest != r2.Digest {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *EID) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*EID)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Endpoint != r2.Endpoint {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *EUI48) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*EUI48)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Address != r2.Address {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *EUI64) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*EUI64)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Address != r2.Address {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *GID) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*GID)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Gid != r2.Gid {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *GPOS) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*GPOS)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Longitude != r2.Longitude {\n\t\treturn false\n\t}\n\tif r1.Latitude != r2.Latitude {\n\t\treturn false\n\t}\n\tif r1.Altitude != r2.Altitude {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *HINFO) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*HINFO)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Cpu != r2.Cpu {\n\t\treturn false\n\t}\n\tif r1.Os != r2.Os {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *HIP) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*HIP)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.HitLength != r2.HitLength {\n\t\treturn false\n\t}\n\tif r1.PublicKeyAlgorithm != r2.PublicKeyAlgorithm {\n\t\treturn false\n\t}\n\tif r1.PublicKeyLength != r2.PublicKeyLength {\n\t\treturn false\n\t}\n\tif r1.Hit != r2.Hit {\n\t\treturn false\n\t}\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\tif len(r1.RendezvousServers) != len(r2.RendezvousServers) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.RendezvousServers); i++ {\n\t\tif !isDuplicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *HTTPS) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*HTTPS)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Priority != r2.Priority {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Target, r2.Target) {\n\t\treturn false\n\t}\n\tif len(r1.Value) != len(r2.Value) {\n\t\treturn false\n\t}\n\tif !areSVCBPairArraysEqual(r1.Value, r2.Value) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *IPSECKEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*IPSECKEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Precedence != r2.Precedence {\n\t\treturn false\n\t}\n\tif r1.GatewayType != r2.GatewayType {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tswitch r1.GatewayType {\n\tcase IPSECGatewayIPv4, IPSECGatewayIPv6:\n\t\tif !r1.GatewayAddr.Equal(r2.GatewayAddr) {\n\t\t\treturn false\n\t\t}\n\tcase IPSECGatewayHost:\n\t\tif !isDuplicateName(r1.GatewayHost, r2.GatewayHost) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *ISDN) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*ISDN)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Address != r2.Address {\n\t\treturn false\n\t}\n\tif r1.SubAddress != r2.SubAddress {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *KEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*KEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Protocol != r2.Protocol {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *KX) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*KX)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Exchanger, r2.Exchanger) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *L32) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*L32)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif !r1.Locator32.Equal(r2.Locator32) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *L64) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*L64)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif r1.Locator64 != r2.Locator64 {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *LOC) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*LOC)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Version != r2.Version {\n\t\treturn false\n\t}\n\tif r1.Size != r2.Size {\n\t\treturn false\n\t}\n\tif r1.HorizPre != r2.HorizPre {\n\t\treturn false\n\t}\n\tif r1.VertPre != r2.VertPre {\n\t\treturn false\n\t}\n\tif r1.Latitude != r2.Latitude {\n\t\treturn false\n\t}\n\tif r1.Longitude != r2.Longitude {\n\t\treturn false\n\t}\n\tif r1.Altitude != r2.Altitude {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *LP) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*LP)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Fqdn, r2.Fqdn) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MB) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MB)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Mb, r2.Mb) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MD) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MD)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Md, r2.Md) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MF) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MF)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Mf, r2.Mf) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MG) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MG)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Mg, r2.Mg) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MINFO) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MINFO)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Rmail, r2.Rmail) {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Email, r2.Email) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MR) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MR)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Mr, r2.Mr) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *MX) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*MX)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Mx, r2.Mx) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NAPTR) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NAPTR)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Order != r2.Order {\n\t\treturn false\n\t}\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Service != r2.Service {\n\t\treturn false\n\t}\n\tif r1.Regexp != r2.Regexp {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Replacement, r2.Replacement) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NID) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NID)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif r1.NodeID != r2.NodeID {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NIMLOC) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NIMLOC)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Locator != r2.Locator {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NINFO) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NINFO)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif len(r1.ZSData) != len(r2.ZSData) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.ZSData); i++ {\n\t\tif r1.ZSData[i] != r2.ZSData[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *NS) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NS)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Ns, r2.Ns) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NSAPPTR) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NSAPPTR)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Ptr, r2.Ptr) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NSEC) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NSEC)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.NextDomain, r2.NextDomain) {\n\t\treturn false\n\t}\n\tif len(r1.TypeBitMap) != len(r2.TypeBitMap) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.TypeBitMap); i++ {\n\t\tif r1.TypeBitMap[i] != r2.TypeBitMap[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *NSEC3) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NSEC3)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Hash != r2.Hash {\n\t\treturn false\n\t}\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Iterations != r2.Iterations {\n\t\treturn false\n\t}\n\tif r1.SaltLength != r2.SaltLength {\n\t\treturn false\n\t}\n\tif r1.Salt != r2.Salt {\n\t\treturn false\n\t}\n\tif r1.HashLength != r2.HashLength {\n\t\treturn false\n\t}\n\tif r1.NextDomain != r2.NextDomain {\n\t\treturn false\n\t}\n\tif len(r1.TypeBitMap) != len(r2.TypeBitMap) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.TypeBitMap); i++ {\n\t\tif r1.TypeBitMap[i] != r2.TypeBitMap[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *NSEC3PARAM) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NSEC3PARAM)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Hash != r2.Hash {\n\t\treturn false\n\t}\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Iterations != r2.Iterations {\n\t\treturn false\n\t}\n\tif r1.SaltLength != r2.SaltLength {\n\t\treturn false\n\t}\n\tif r1.Salt != r2.Salt {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NULL) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NULL)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Data != r2.Data {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *NXNAME) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NXNAME)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\treturn true\n}\n\nfunc (r1 *NXT) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*NXT)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.NextDomain, r2.NextDomain) {\n\t\treturn false\n\t}\n\tif len(r1.TypeBitMap) != len(r2.TypeBitMap) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.TypeBitMap); i++ {\n\t\tif r1.TypeBitMap[i] != r2.TypeBitMap[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *OPENPGPKEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*OPENPGPKEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *PTR) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*PTR)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Ptr, r2.Ptr) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *PX) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*PX)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Map822, r2.Map822) {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Mapx400, r2.Mapx400) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *RESINFO) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RESINFO)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif len(r1.Txt) != len(r2.Txt) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.Txt); i++ {\n\t\tif r1.Txt[i] != r2.Txt[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *RFC3597) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RFC3597)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Rdata != r2.Rdata {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *RKEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RKEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Flags != r2.Flags {\n\t\treturn false\n\t}\n\tif r1.Protocol != r2.Protocol {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.PublicKey != r2.PublicKey {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *RP) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RP)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Mbox, r2.Mbox) {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Txt, r2.Txt) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *RRSIG) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RRSIG)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.TypeCovered != r2.TypeCovered {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.Labels != r2.Labels {\n\t\treturn false\n\t}\n\tif r1.OrigTtl != r2.OrigTtl {\n\t\treturn false\n\t}\n\tif r1.Expiration != r2.Expiration {\n\t\treturn false\n\t}\n\tif r1.Inception != r2.Inception {\n\t\treturn false\n\t}\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.SignerName, r2.SignerName) {\n\t\treturn false\n\t}\n\tif r1.Signature != r2.Signature {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *RT) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*RT)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Preference != r2.Preference {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Host, r2.Host) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *SIG) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SIG)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.TypeCovered != r2.TypeCovered {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.Labels != r2.Labels {\n\t\treturn false\n\t}\n\tif r1.OrigTtl != r2.OrigTtl {\n\t\treturn false\n\t}\n\tif r1.Expiration != r2.Expiration {\n\t\treturn false\n\t}\n\tif r1.Inception != r2.Inception {\n\t\treturn false\n\t}\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.SignerName, r2.SignerName) {\n\t\treturn false\n\t}\n\tif r1.Signature != r2.Signature {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *SMIMEA) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SMIMEA)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Usage != r2.Usage {\n\t\treturn false\n\t}\n\tif r1.Selector != r2.Selector {\n\t\treturn false\n\t}\n\tif r1.MatchingType != r2.MatchingType {\n\t\treturn false\n\t}\n\tif r1.Certificate != r2.Certificate {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *SOA) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SOA)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Ns, r2.Ns) {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Mbox, r2.Mbox) {\n\t\treturn false\n\t}\n\tif r1.Serial != r2.Serial {\n\t\treturn false\n\t}\n\tif r1.Refresh != r2.Refresh {\n\t\treturn false\n\t}\n\tif r1.Retry != r2.Retry {\n\t\treturn false\n\t}\n\tif r1.Expire != r2.Expire {\n\t\treturn false\n\t}\n\tif r1.Minttl != r2.Minttl {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *SPF) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SPF)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif len(r1.Txt) != len(r2.Txt) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.Txt); i++ {\n\t\tif r1.Txt[i] != r2.Txt[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *SRV) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SRV)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Priority != r2.Priority {\n\t\treturn false\n\t}\n\tif r1.Weight != r2.Weight {\n\t\treturn false\n\t}\n\tif r1.Port != r2.Port {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Target, r2.Target) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *SSHFP) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SSHFP)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.Type != r2.Type {\n\t\treturn false\n\t}\n\tif r1.FingerPrint != r2.FingerPrint {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *SVCB) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*SVCB)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Priority != r2.Priority {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.Target, r2.Target) {\n\t\treturn false\n\t}\n\tif len(r1.Value) != len(r2.Value) {\n\t\treturn false\n\t}\n\tif !areSVCBPairArraysEqual(r1.Value, r2.Value) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *TA) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*TA)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.KeyTag != r2.KeyTag {\n\t\treturn false\n\t}\n\tif r1.Algorithm != r2.Algorithm {\n\t\treturn false\n\t}\n\tif r1.DigestType != r2.DigestType {\n\t\treturn false\n\t}\n\tif r1.Digest != r2.Digest {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *TALINK) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*TALINK)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.PreviousName, r2.PreviousName) {\n\t\treturn false\n\t}\n\tif !isDuplicateName(r1.NextName, r2.NextName) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *TKEY) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*TKEY)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Algorithm, r2.Algorithm) {\n\t\treturn false\n\t}\n\tif r1.Inception != r2.Inception {\n\t\treturn false\n\t}\n\tif r1.Expiration != r2.Expiration {\n\t\treturn false\n\t}\n\tif r1.Mode != r2.Mode {\n\t\treturn false\n\t}\n\tif r1.Error != r2.Error {\n\t\treturn false\n\t}\n\tif r1.KeySize != r2.KeySize {\n\t\treturn false\n\t}\n\tif r1.Key != r2.Key {\n\t\treturn false\n\t}\n\tif r1.OtherLen != r2.OtherLen {\n\t\treturn false\n\t}\n\tif r1.OtherData != r2.OtherData {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *TLSA) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*TLSA)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Usage != r2.Usage {\n\t\treturn false\n\t}\n\tif r1.Selector != r2.Selector {\n\t\treturn false\n\t}\n\tif r1.MatchingType != r2.MatchingType {\n\t\treturn false\n\t}\n\tif r1.Certificate != r2.Certificate {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *TSIG) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*TSIG)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif !isDuplicateName(r1.Algorithm, r2.Algorithm) {\n\t\treturn false\n\t}\n\tif r1.TimeSigned != r2.TimeSigned {\n\t\treturn false\n\t}\n\tif r1.Fudge != r2.Fudge {\n\t\treturn false\n\t}\n\tif r1.MACSize != r2.MACSize {\n\t\treturn false\n\t}\n\tif r1.MAC != r2.MAC {\n\t\treturn false\n\t}\n\tif r1.OrigId != r2.OrigId {\n\t\treturn false\n\t}\n\tif r1.Error != r2.Error {\n\t\treturn false\n\t}\n\tif r1.OtherLen != r2.OtherLen {\n\t\treturn false\n\t}\n\tif r1.OtherData != r2.OtherData {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *TXT) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*TXT)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif len(r1.Txt) != len(r2.Txt) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r1.Txt); i++ {\n\t\tif r1.Txt[i] != r2.Txt[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (r1 *UID) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*UID)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Uid != r2.Uid {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *UINFO) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*UINFO)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Uinfo != r2.Uinfo {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *URI) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*URI)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Priority != r2.Priority {\n\t\treturn false\n\t}\n\tif r1.Weight != r2.Weight {\n\t\treturn false\n\t}\n\tif r1.Target != r2.Target {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *X25) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*X25)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.PSDNAddress != r2.PSDNAddress {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 *ZONEMD) isDuplicate(_r2 RR) bool {\n\tr2, ok := _r2.(*ZONEMD)\n\tif !ok {\n\t\treturn false\n\t}\n\t_ = r2\n\tif r1.Serial != r2.Serial {\n\t\treturn false\n\t}\n\tif r1.Scheme != r2.Scheme {\n\t\treturn false\n\t}\n\tif r1.Hash != r2.Hash {\n\t\treturn false\n\t}\n\tif r1.Digest != r2.Digest {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "zmsg.go",
    "content": "// Code generated by \"go run msg_generate.go\"; DO NOT EDIT.\n\npackage dns\n\nimport \"fmt\"\n\n// pack*() functions\n\nfunc (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDataA(rr.A, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AAAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDataAAAA(rr.AAAA, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Subtype, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Hostname, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AMTRELAY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Precedence, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.GatewayType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\treturn off, nil\n}\n\nfunc (rr *APL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDataApl(rr.Prefixes, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringTxt(rr.Txt, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Flag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Tag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringOctet(rr.Value, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CDNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Protocol, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CDS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.DigestType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Digest, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CERT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Type, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.Certificate, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Target, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CSYNC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint32(rr.Serial, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataNsec(rr.TypeBitMap, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DHCID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringBase64(rr.Digest, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DLV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.DigestType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Digest, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Target, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Protocol, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.DigestType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Digest, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *EID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringHex(rr.Endpoint, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *EUI48) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint48(rr.Address, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *EUI64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint64(rr.Address, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *GID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint32(rr.Gid, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *GPOS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packString(rr.Longitude, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Latitude, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Altitude, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *HINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packString(rr.Cpu, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Os, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.HitLength, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.PublicKeyAlgorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.PublicKeyLength, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Hit, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *HTTPS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Priority, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Target, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataSVCB(rr.Value, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *IPSECKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Precedence, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.GatewayType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *ISDN) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packString(rr.Address, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.SubAddress, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Protocol, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *KX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Exchanger, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *L32) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataA(rr.Locator32, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *L64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint64(rr.Locator64, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *LOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Version, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Size, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.HorizPre, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.VertPre, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Latitude, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Longitude, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Altitude, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *LP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Fqdn, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Mb, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Md, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Mf, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Mg, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Rmail, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Email, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Mr, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Mx, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NAPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Order, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Service, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packString(rr.Regexp, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Replacement, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint64(rr.NodeID, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NIMLOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringHex(rr.Locator, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringTxt(rr.ZSData, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Ns, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSAPPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Ptr, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSEC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.NextDomain, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataNsec(rr.TypeBitMap, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSEC3) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Hash, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Iterations, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.SaltLength, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\t// Only pack salt if value is not \"-\", i.e. empty\n\tif rr.Salt != \"-\" {\n\t\toff, err = packStringHex(rr.Salt, msg, off)\n\t\tif err != nil {\n\t\t\treturn off, err\n\t\t}\n\t}\n\toff, err = packUint8(rr.HashLength, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase32(rr.NextDomain, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataNsec(rr.TypeBitMap, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Hash, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Iterations, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.SaltLength, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\t// Only pack salt if value is not \"-\", i.e. empty\n\tif rr.Salt != \"-\" {\n\t\toff, err = packStringHex(rr.Salt, msg, off)\n\t\tif err != nil {\n\t\t\treturn off, err\n\t\t}\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NULL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringAny(rr.Data, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NXNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\treturn off, nil\n}\n\nfunc (rr *NXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.NextDomain, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataNsec(rr.TypeBitMap, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *OPT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDataOpt(rr.Option, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *PTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Ptr, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *PX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Map822, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Mapx400, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RESINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringTxt(rr.Txt, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RFC3597) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringHex(rr.Rdata, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Flags, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Protocol, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.PublicKey, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Mbox, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Txt, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RRSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.TypeCovered, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Labels, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.OrigTtl, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Expiration, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Inception, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.SignerName, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.Signature, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Preference, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Host, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.TypeCovered, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Labels, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.OrigTtl, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Expiration, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Inception, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.SignerName, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringBase64(rr.Signature, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SMIMEA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Usage, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Selector, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.MatchingType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Certificate, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SOA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Ns, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Mbox, msg, off, compression, compress)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Serial, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Refresh, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Retry, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Expire, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Minttl, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SPF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringTxt(rr.Txt, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SRV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Priority, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Weight, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Port, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Target, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Type, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.FingerPrint, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SVCB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Priority, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.Target, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDataSVCB(rr.Value, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.KeyTag, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Algorithm, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.DigestType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Digest, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TALINK) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.PreviousName, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packDomainName(rr.NextName, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Algorithm, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Inception, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint32(rr.Expiration, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Mode, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Error, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.KeySize, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Key, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.OtherLen, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.OtherData, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TLSA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint8(rr.Usage, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Selector, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.MatchingType, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Certificate, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packDomainName(rr.Algorithm, msg, off, compression, false)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint48(rr.TimeSigned, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Fudge, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.MACSize, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.MAC, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.OrigId, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Error, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.OtherLen, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.OtherData, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packStringTxt(rr.Txt, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *UID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint32(rr.Uid, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *UINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packString(rr.Uinfo, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *URI) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint16(rr.Priority, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint16(rr.Weight, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringOctet(rr.Target, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packString(rr.PSDNAddress, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *ZONEMD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n\toff, err = packUint32(rr.Serial, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Scheme, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packUint8(rr.Hash, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\toff, err = packStringHex(rr.Digest, msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\n// unpack*() functions\n\nfunc (rr *A) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.A, off, err = unpackDataA(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"A: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AAAA) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.AAAA, off, err = unpackDataAAAA(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AAAA: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Subtype, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AFSDB.Subtype: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Hostname, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AFSDB.Hostname: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AMTRELAY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Precedence, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AMTRELAY.Precedence: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.GatewayType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AMTRELAY.GatewayType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AMTRELAY.GatewayHost: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\treturn off, nil\n}\n\nfunc (rr *APL) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Prefixes, off, err = unpackDataApl(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"APL.Prefixes: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Txt, off, err = unpackStringTxt(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"AVC.Txt: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CAA) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Flag, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CAA.Flag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Tag, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CAA.Tag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Value, off, err = unpackStringOctet(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CAA.Value: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CDNSKEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Flags, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDNSKEY.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Protocol, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDNSKEY.Protocol: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDNSKEY.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDNSKEY.PublicKey: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CDS) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDS.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDS.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.DigestType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDS.DigestType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CDS.Digest: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CERT) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Type, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CERT.Type: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CERT.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CERT.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Certificate, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CERT.Certificate: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CNAME) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Target, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CNAME.Target: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *CSYNC) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Serial, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CSYNC.Serial: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Flags, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CSYNC.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.TypeBitMap, off, err = unpackDataNsec(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"CSYNC.TypeBitMap: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DHCID) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Digest, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DHCID.Digest: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DLV) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DLV.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DLV.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.DigestType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DLV.DigestType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DLV.Digest: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DNAME) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Target, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DNAME.Target: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DNSKEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Flags, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DNSKEY.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Protocol, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DNSKEY.Protocol: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DNSKEY.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DNSKEY.PublicKey: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *DS) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DS.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DS.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.DigestType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DS.DigestType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"DS.Digest: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *EID) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Endpoint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"EID.Endpoint: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *EUI48) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Address, off, err = unpackUint48(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"EUI48.Address: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *EUI64) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Address, off, err = unpackUint64(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"EUI64.Address: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *GID) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Gid, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"GID.Gid: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *GPOS) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Longitude, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"GPOS.Longitude: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Latitude, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"GPOS.Latitude: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Altitude, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"GPOS.Altitude: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *HINFO) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Cpu, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HINFO.Cpu: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Os, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HINFO.Os: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.HitLength, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HIP.HitLength: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKeyAlgorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HIP.PublicKeyAlgorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKeyLength, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HIP.PublicKeyLength: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Hit, off, err = unpackStringHex(msg, off, off+int(rr.HitLength))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, off+int(rr.PublicKeyLength))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\trr.RendezvousServers, off, err = unpackDataDomainNames(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HIP.RendezvousServers: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *HTTPS) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Priority, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HTTPS.Priority: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Target, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HTTPS.Target: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Value, off, err = unpackDataSVCB(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"HTTPS.Value: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *IPSECKEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Precedence, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"IPSECKEY.Precedence: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.GatewayType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"IPSECKEY.GatewayType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"IPSECKEY.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"IPSECKEY.GatewayHost: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"IPSECKEY.PublicKey: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *ISDN) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Address, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"ISDN.Address: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.SubAddress, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"ISDN.SubAddress: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Flags, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"KEY.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Protocol, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"KEY.Protocol: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"KEY.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"KEY.PublicKey: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *KX) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"KX.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Exchanger, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"KX.Exchanger: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *L32) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"L32.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Locator32, off, err = unpackDataA(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"L32.Locator32: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *L64) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"L64.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Locator64, off, err = unpackUint64(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"L64.Locator64: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *LOC) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Version, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.Version: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Size, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.Size: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.HorizPre, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.HorizPre: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.VertPre, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.VertPre: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Latitude, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.Latitude: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Longitude, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.Longitude: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Altitude, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LOC.Altitude: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *LP) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LP.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Fqdn, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"LP.Fqdn: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MB) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Mb, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MB.Mb: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MD) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Md, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MD.Md: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MF) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Mf, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MF.Mf: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MG) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Mg, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MG.Mg: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MINFO) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Rmail, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MINFO.Rmail: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Email, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MINFO.Email: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MR) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Mr, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MR.Mr: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *MX) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MX.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Mx, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"MX.Mx: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NAPTR) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Order, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NAPTR.Order: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NAPTR.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Flags, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NAPTR.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Service, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NAPTR.Service: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Regexp, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NAPTR.Regexp: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Replacement, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NAPTR.Replacement: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NID) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NID.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.NodeID, off, err = unpackUint64(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NID.NodeID: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NIMLOC) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Locator, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NIMLOC.Locator: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NINFO) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.ZSData, off, err = unpackStringTxt(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NINFO.ZSData: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NS) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Ns, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NS.Ns: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSAPPTR) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Ptr, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSAPPTR.Ptr: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSEC) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.NextDomain, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC.NextDomain: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.TypeBitMap, off, err = unpackDataNsec(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC.TypeBitMap: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Hash, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3.Hash: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Flags, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Iterations, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3.Iterations: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.SaltLength, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3.SaltLength: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\trr.HashLength, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3.HashLength: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.NextDomain, off, err = unpackStringBase32(msg, off, off+int(rr.HashLength))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\trr.TypeBitMap, off, err = unpackDataNsec(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3.TypeBitMap: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NSEC3PARAM) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Hash, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3PARAM.Hash: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Flags, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3PARAM.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Iterations, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3PARAM.Iterations: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.SaltLength, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NSEC3PARAM.SaltLength: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NULL) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Data, off, err = unpackStringAny(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NULL.Data: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *NXNAME) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\treturn off, nil\n}\n\nfunc (rr *NXT) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.NextDomain, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NXT.NextDomain: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.TypeBitMap, off, err = unpackDataNsec(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"NXT.TypeBitMap: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *OPENPGPKEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"OPENPGPKEY.PublicKey: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *OPT) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Option, off, err = unpackDataOpt(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"OPT.Option: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *PTR) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Ptr, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"PTR.Ptr: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *PX) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"PX.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Map822, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"PX.Map822: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Mapx400, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"PX.Mapx400: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RESINFO) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Txt, off, err = unpackStringTxt(msg, off)\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RFC3597) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Rdata, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RFC3597.Rdata: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RKEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Flags, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RKEY.Flags: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Protocol, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RKEY.Protocol: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RKEY.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RKEY.PublicKey: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RP) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Mbox, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RP.Mbox: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Txt, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RP.Txt: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RRSIG) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.TypeCovered, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.TypeCovered: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Labels, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.Labels: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.OrigTtl, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.OrigTtl: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Expiration, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.Expiration: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Inception, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.Inception: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.SignerName, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.SignerName: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RRSIG.Signature: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *RT) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Preference, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RT.Preference: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Host, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"RT.Host: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SIG) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.TypeCovered, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.TypeCovered: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Labels, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.Labels: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.OrigTtl, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.OrigTtl: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Expiration, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.Expiration: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Inception, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.Inception: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.SignerName, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.SignerName: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SIG.Signature: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SMIMEA) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Usage, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SMIMEA.Usage: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Selector, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SMIMEA.Selector: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.MatchingType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SMIMEA.MatchingType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SMIMEA.Certificate: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SOA) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Ns, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Ns: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Mbox, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Mbox: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Serial, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Serial: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Refresh, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Refresh: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Retry, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Retry: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Expire, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Expire: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Minttl, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SOA.Minttl: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SPF) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Txt, off, err = unpackStringTxt(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SPF.Txt: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SRV) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Priority, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SRV.Priority: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Weight, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SRV.Weight: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Port, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SRV.Port: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Target, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SRV.Target: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SSHFP) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SSHFP.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Type, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SSHFP.Type: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.FingerPrint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SSHFP.FingerPrint: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *SVCB) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Priority, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SVCB.Priority: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Target, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SVCB.Target: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Value, off, err = unpackDataSVCB(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"SVCB.Value: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TA) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.KeyTag, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TA.KeyTag: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Algorithm, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TA.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.DigestType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TA.DigestType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TA.Digest: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TALINK) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.PreviousName, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TALINK.PreviousName: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.NextName, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TALINK.NextName: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TKEY) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Algorithm, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Inception, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.Inception: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Expiration, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.Expiration: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Mode, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.Mode: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Error, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.Error: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.KeySize, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.KeySize: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Key, off, err = unpackStringHex(msg, off, off+int(rr.KeySize))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\trr.OtherLen, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TKEY.OtherLen: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TLSA) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Usage, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TLSA.Usage: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Selector, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TLSA.Selector: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.MatchingType, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TLSA.MatchingType: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TLSA.Certificate: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TSIG) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Algorithm, off, err = UnpackDomainName(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.Algorithm: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.TimeSigned, off, err = unpackUint48(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.TimeSigned: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Fudge, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.Fudge: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.MACSize, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.MACSize: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.MAC, off, err = unpackStringHex(msg, off, off+int(rr.MACSize))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\trr.OrigId, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.OrigId: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Error, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.Error: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.OtherLen, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TSIG.OtherLen: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen))\n\tif err != nil {\n\t\treturn off, err\n\t}\n\treturn off, nil\n}\n\nfunc (rr *TXT) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Txt, off, err = unpackStringTxt(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"TXT.Txt: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *UID) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Uid, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"UID.Uid: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *UINFO) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Uinfo, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"UINFO.Uinfo: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *URI) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Priority, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"URI.Priority: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Weight, off, err = unpackUint16(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"URI.Weight: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Target, off, err = unpackStringOctet(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"URI.Target: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *X25) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.PSDNAddress, off, err = unpackString(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"X25.PSDNAddress: %w\", err)\n\t}\n\treturn off, nil\n}\n\nfunc (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) {\n\trdStart := off\n\t_ = rdStart\n\n\trr.Serial, off, err = unpackUint32(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"ZONEMD.Serial: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Scheme, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"ZONEMD.Scheme: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Hash, off, err = unpackUint8(msg, off)\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"ZONEMD.Hash: %w\", err)\n\t}\n\tif off == len(msg) {\n\t\treturn off, nil\n\t}\n\trr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))\n\tif err != nil {\n\t\treturn off, fmt.Errorf(\"ZONEMD.Digest: %w\", err)\n\t}\n\treturn off, nil\n}\n"
  },
  {
    "path": "ztypes.go",
    "content": "// Code generated by \"go run types_generate.go\"; DO NOT EDIT.\n\npackage dns\n\nimport (\n\t\"encoding/base64\"\n\t\"net\"\n)\n\n// TypeToRR is a map of constructors for each RR type.\nvar TypeToRR = map[uint16]func() RR{\n\tTypeA:          func() RR { return new(A) },\n\tTypeAAAA:       func() RR { return new(AAAA) },\n\tTypeAFSDB:      func() RR { return new(AFSDB) },\n\tTypeAMTRELAY:   func() RR { return new(AMTRELAY) },\n\tTypeANY:        func() RR { return new(ANY) },\n\tTypeAPL:        func() RR { return new(APL) },\n\tTypeAVC:        func() RR { return new(AVC) },\n\tTypeCAA:        func() RR { return new(CAA) },\n\tTypeCDNSKEY:    func() RR { return new(CDNSKEY) },\n\tTypeCDS:        func() RR { return new(CDS) },\n\tTypeCERT:       func() RR { return new(CERT) },\n\tTypeCNAME:      func() RR { return new(CNAME) },\n\tTypeCSYNC:      func() RR { return new(CSYNC) },\n\tTypeDHCID:      func() RR { return new(DHCID) },\n\tTypeDLV:        func() RR { return new(DLV) },\n\tTypeDNAME:      func() RR { return new(DNAME) },\n\tTypeDNSKEY:     func() RR { return new(DNSKEY) },\n\tTypeDS:         func() RR { return new(DS) },\n\tTypeEID:        func() RR { return new(EID) },\n\tTypeEUI48:      func() RR { return new(EUI48) },\n\tTypeEUI64:      func() RR { return new(EUI64) },\n\tTypeGID:        func() RR { return new(GID) },\n\tTypeGPOS:       func() RR { return new(GPOS) },\n\tTypeHINFO:      func() RR { return new(HINFO) },\n\tTypeHIP:        func() RR { return new(HIP) },\n\tTypeHTTPS:      func() RR { return new(HTTPS) },\n\tTypeIPSECKEY:   func() RR { return new(IPSECKEY) },\n\tTypeISDN:       func() RR { return new(ISDN) },\n\tTypeKEY:        func() RR { return new(KEY) },\n\tTypeKX:         func() RR { return new(KX) },\n\tTypeL32:        func() RR { return new(L32) },\n\tTypeL64:        func() RR { return new(L64) },\n\tTypeLOC:        func() RR { return new(LOC) },\n\tTypeLP:         func() RR { return new(LP) },\n\tTypeMB:         func() RR { return new(MB) },\n\tTypeMD:         func() RR { return new(MD) },\n\tTypeMF:         func() RR { return new(MF) },\n\tTypeMG:         func() RR { return new(MG) },\n\tTypeMINFO:      func() RR { return new(MINFO) },\n\tTypeMR:         func() RR { return new(MR) },\n\tTypeMX:         func() RR { return new(MX) },\n\tTypeNAPTR:      func() RR { return new(NAPTR) },\n\tTypeNID:        func() RR { return new(NID) },\n\tTypeNIMLOC:     func() RR { return new(NIMLOC) },\n\tTypeNINFO:      func() RR { return new(NINFO) },\n\tTypeNS:         func() RR { return new(NS) },\n\tTypeNSAPPTR:    func() RR { return new(NSAPPTR) },\n\tTypeNSEC:       func() RR { return new(NSEC) },\n\tTypeNSEC3:      func() RR { return new(NSEC3) },\n\tTypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },\n\tTypeNULL:       func() RR { return new(NULL) },\n\tTypeNXNAME:     func() RR { return new(NXNAME) },\n\tTypeNXT:        func() RR { return new(NXT) },\n\tTypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },\n\tTypeOPT:        func() RR { return new(OPT) },\n\tTypePTR:        func() RR { return new(PTR) },\n\tTypePX:         func() RR { return new(PX) },\n\tTypeRESINFO:    func() RR { return new(RESINFO) },\n\tTypeRKEY:       func() RR { return new(RKEY) },\n\tTypeRP:         func() RR { return new(RP) },\n\tTypeRRSIG:      func() RR { return new(RRSIG) },\n\tTypeRT:         func() RR { return new(RT) },\n\tTypeSIG:        func() RR { return new(SIG) },\n\tTypeSMIMEA:     func() RR { return new(SMIMEA) },\n\tTypeSOA:        func() RR { return new(SOA) },\n\tTypeSPF:        func() RR { return new(SPF) },\n\tTypeSRV:        func() RR { return new(SRV) },\n\tTypeSSHFP:      func() RR { return new(SSHFP) },\n\tTypeSVCB:       func() RR { return new(SVCB) },\n\tTypeTA:         func() RR { return new(TA) },\n\tTypeTALINK:     func() RR { return new(TALINK) },\n\tTypeTKEY:       func() RR { return new(TKEY) },\n\tTypeTLSA:       func() RR { return new(TLSA) },\n\tTypeTSIG:       func() RR { return new(TSIG) },\n\tTypeTXT:        func() RR { return new(TXT) },\n\tTypeUID:        func() RR { return new(UID) },\n\tTypeUINFO:      func() RR { return new(UINFO) },\n\tTypeURI:        func() RR { return new(URI) },\n\tTypeX25:        func() RR { return new(X25) },\n\tTypeZONEMD:     func() RR { return new(ZONEMD) },\n}\n\n// TypeToString is a map of strings for each RR type.\nvar TypeToString = map[uint16]string{\n\tTypeA:          \"A\",\n\tTypeAAAA:       \"AAAA\",\n\tTypeAFSDB:      \"AFSDB\",\n\tTypeAMTRELAY:   \"AMTRELAY\",\n\tTypeANY:        \"ANY\",\n\tTypeAPL:        \"APL\",\n\tTypeATMA:       \"ATMA\",\n\tTypeAVC:        \"AVC\",\n\tTypeAXFR:       \"AXFR\",\n\tTypeCAA:        \"CAA\",\n\tTypeCDNSKEY:    \"CDNSKEY\",\n\tTypeCDS:        \"CDS\",\n\tTypeCERT:       \"CERT\",\n\tTypeCNAME:      \"CNAME\",\n\tTypeCSYNC:      \"CSYNC\",\n\tTypeDHCID:      \"DHCID\",\n\tTypeDLV:        \"DLV\",\n\tTypeDNAME:      \"DNAME\",\n\tTypeDNSKEY:     \"DNSKEY\",\n\tTypeDS:         \"DS\",\n\tTypeEID:        \"EID\",\n\tTypeEUI48:      \"EUI48\",\n\tTypeEUI64:      \"EUI64\",\n\tTypeGID:        \"GID\",\n\tTypeGPOS:       \"GPOS\",\n\tTypeHINFO:      \"HINFO\",\n\tTypeHIP:        \"HIP\",\n\tTypeHTTPS:      \"HTTPS\",\n\tTypeIPSECKEY:   \"IPSECKEY\",\n\tTypeISDN:       \"ISDN\",\n\tTypeIXFR:       \"IXFR\",\n\tTypeKEY:        \"KEY\",\n\tTypeKX:         \"KX\",\n\tTypeL32:        \"L32\",\n\tTypeL64:        \"L64\",\n\tTypeLOC:        \"LOC\",\n\tTypeLP:         \"LP\",\n\tTypeMAILA:      \"MAILA\",\n\tTypeMAILB:      \"MAILB\",\n\tTypeMB:         \"MB\",\n\tTypeMD:         \"MD\",\n\tTypeMF:         \"MF\",\n\tTypeMG:         \"MG\",\n\tTypeMINFO:      \"MINFO\",\n\tTypeMR:         \"MR\",\n\tTypeMX:         \"MX\",\n\tTypeNAPTR:      \"NAPTR\",\n\tTypeNID:        \"NID\",\n\tTypeNIMLOC:     \"NIMLOC\",\n\tTypeNINFO:      \"NINFO\",\n\tTypeNS:         \"NS\",\n\tTypeNSEC:       \"NSEC\",\n\tTypeNSEC3:      \"NSEC3\",\n\tTypeNSEC3PARAM: \"NSEC3PARAM\",\n\tTypeNULL:       \"NULL\",\n\tTypeNXNAME:     \"NXNAME\",\n\tTypeNXT:        \"NXT\",\n\tTypeNone:       \"None\",\n\tTypeOPENPGPKEY: \"OPENPGPKEY\",\n\tTypeOPT:        \"OPT\",\n\tTypePTR:        \"PTR\",\n\tTypePX:         \"PX\",\n\tTypeRESINFO:    \"RESINFO\",\n\tTypeRKEY:       \"RKEY\",\n\tTypeRP:         \"RP\",\n\tTypeRRSIG:      \"RRSIG\",\n\tTypeRT:         \"RT\",\n\tTypeReserved:   \"Reserved\",\n\tTypeSIG:        \"SIG\",\n\tTypeSMIMEA:     \"SMIMEA\",\n\tTypeSOA:        \"SOA\",\n\tTypeSPF:        \"SPF\",\n\tTypeSRV:        \"SRV\",\n\tTypeSSHFP:      \"SSHFP\",\n\tTypeSVCB:       \"SVCB\",\n\tTypeTA:         \"TA\",\n\tTypeTALINK:     \"TALINK\",\n\tTypeTKEY:       \"TKEY\",\n\tTypeTLSA:       \"TLSA\",\n\tTypeTSIG:       \"TSIG\",\n\tTypeTXT:        \"TXT\",\n\tTypeUID:        \"UID\",\n\tTypeUINFO:      \"UINFO\",\n\tTypeUNSPEC:     \"UNSPEC\",\n\tTypeURI:        \"URI\",\n\tTypeX25:        \"X25\",\n\tTypeZONEMD:     \"ZONEMD\",\n\tTypeNSAPPTR:    \"NSAP-PTR\",\n}\n\nfunc (rr *A) Header() *RR_Header          { return &rr.Hdr }\nfunc (rr *AAAA) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *AFSDB) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *AMTRELAY) Header() *RR_Header   { return &rr.Hdr }\nfunc (rr *ANY) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *APL) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *AVC) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *CAA) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *CDNSKEY) Header() *RR_Header    { return &rr.Hdr }\nfunc (rr *CDS) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *CERT) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *CNAME) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *CSYNC) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *DHCID) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *DLV) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *DNAME) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *DNSKEY) Header() *RR_Header     { return &rr.Hdr }\nfunc (rr *DS) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *EID) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *EUI48) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *EUI64) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *GID) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *GPOS) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *HINFO) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *HIP) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *HTTPS) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *IPSECKEY) Header() *RR_Header   { return &rr.Hdr }\nfunc (rr *ISDN) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *KEY) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *KX) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *L32) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *L64) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *LOC) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *LP) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *MB) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *MD) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *MF) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *MG) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *MINFO) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *MR) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *MX) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *NAPTR) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *NID) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *NIMLOC) Header() *RR_Header     { return &rr.Hdr }\nfunc (rr *NINFO) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *NS) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *NSAPPTR) Header() *RR_Header    { return &rr.Hdr }\nfunc (rr *NSEC) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *NSEC3) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }\nfunc (rr *NULL) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *NXNAME) Header() *RR_Header     { return &rr.Hdr }\nfunc (rr *NXT) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }\nfunc (rr *OPT) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *PTR) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *PX) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *RESINFO) Header() *RR_Header    { return &rr.Hdr }\nfunc (rr *RFC3597) Header() *RR_Header    { return &rr.Hdr }\nfunc (rr *RKEY) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *RP) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *RRSIG) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *RT) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *SIG) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *SMIMEA) Header() *RR_Header     { return &rr.Hdr }\nfunc (rr *SOA) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *SPF) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *SRV) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *SSHFP) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *SVCB) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *TA) Header() *RR_Header         { return &rr.Hdr }\nfunc (rr *TALINK) Header() *RR_Header     { return &rr.Hdr }\nfunc (rr *TKEY) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *TLSA) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *TSIG) Header() *RR_Header       { return &rr.Hdr }\nfunc (rr *TXT) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *UID) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *UINFO) Header() *RR_Header      { return &rr.Hdr }\nfunc (rr *URI) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *X25) Header() *RR_Header        { return &rr.Hdr }\nfunc (rr *ZONEMD) Header() *RR_Header     { return &rr.Hdr }\n\n// len() functions\nfunc (rr *A) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tif len(rr.A) != 0 {\n\t\tl += net.IPv4len\n\t}\n\treturn l\n}\n\nfunc (rr *AAAA) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tif len(rr.AAAA) != 0 {\n\t\tl += net.IPv6len\n\t}\n\treturn l\n}\n\nfunc (rr *AFSDB) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Subtype\n\tl += domainNameLen(rr.Hostname, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *AMTRELAY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++ // Precedence\n\tl++ // GatewayType\n\tswitch rr.GatewayType {\n\tcase AMTRELAYIPv4:\n\t\tl += net.IPv4len\n\tcase AMTRELAYIPv6:\n\t\tl += net.IPv6len\n\tcase AMTRELAYHost:\n\t\tl += len(rr.GatewayHost) + 1\n\t}\n\treturn l\n}\n\nfunc (rr *ANY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\treturn l\n}\n\nfunc (rr *APL) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, x := range rr.Prefixes {\n\t\tl += x.len()\n\t}\n\treturn l\n}\n\nfunc (rr *AVC) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, x := range rr.Txt {\n\t\tl += len(x) + 1\n\t}\n\treturn l\n}\n\nfunc (rr *CAA) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++ // Flag\n\tl += len(rr.Tag) + 1\n\tl += len(rr.Value)\n\treturn l\n}\n\nfunc (rr *CERT) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Type\n\tl += 2 // KeyTag\n\tl++    // Algorithm\n\tl += base64.StdEncoding.DecodedLen(len(rr.Certificate))\n\treturn l\n}\n\nfunc (rr *CNAME) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Target, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *DHCID) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += base64.StdEncoding.DecodedLen(len(rr.Digest))\n\treturn l\n}\n\nfunc (rr *DNAME) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Target, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *DNSKEY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Flags\n\tl++    // Protocol\n\tl++    // Algorithm\n\tl += base64.StdEncoding.DecodedLen(len(rr.PublicKey))\n\treturn l\n}\n\nfunc (rr *DS) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // KeyTag\n\tl++    // Algorithm\n\tl++    // DigestType\n\tl += len(rr.Digest) / 2\n\treturn l\n}\n\nfunc (rr *EID) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Endpoint) / 2\n\treturn l\n}\n\nfunc (rr *EUI48) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 6 // Address\n\treturn l\n}\n\nfunc (rr *EUI64) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 8 // Address\n\treturn l\n}\n\nfunc (rr *GID) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 4 // Gid\n\treturn l\n}\n\nfunc (rr *GPOS) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Longitude) + 1\n\tl += len(rr.Latitude) + 1\n\tl += len(rr.Altitude) + 1\n\treturn l\n}\n\nfunc (rr *HINFO) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Cpu) + 1\n\tl += len(rr.Os) + 1\n\treturn l\n}\n\nfunc (rr *HIP) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++    // HitLength\n\tl++    // PublicKeyAlgorithm\n\tl += 2 // PublicKeyLength\n\tl += len(rr.Hit) / 2\n\tl += base64.StdEncoding.DecodedLen(len(rr.PublicKey))\n\tfor _, x := range rr.RendezvousServers {\n\t\tl += domainNameLen(x, off+l, compression, false)\n\t}\n\treturn l\n}\n\nfunc (rr *IPSECKEY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++ // Precedence\n\tl++ // GatewayType\n\tl++ // Algorithm\n\tswitch rr.GatewayType {\n\tcase IPSECGatewayIPv4:\n\t\tl += net.IPv4len\n\tcase IPSECGatewayIPv6:\n\t\tl += net.IPv6len\n\tcase IPSECGatewayHost:\n\t\tl += len(rr.GatewayHost) + 1\n\t}\n\tl += base64.StdEncoding.DecodedLen(len(rr.PublicKey))\n\treturn l\n}\n\nfunc (rr *ISDN) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Address) + 1\n\tl += len(rr.SubAddress) + 1\n\treturn l\n}\n\nfunc (rr *KX) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += domainNameLen(rr.Exchanger, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *L32) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tif len(rr.Locator32) != 0 {\n\t\tl += net.IPv4len\n\t}\n\treturn l\n}\n\nfunc (rr *L64) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += 8 // Locator64\n\treturn l\n}\n\nfunc (rr *LOC) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++    // Version\n\tl++    // Size\n\tl++    // HorizPre\n\tl++    // VertPre\n\tl += 4 // Latitude\n\tl += 4 // Longitude\n\tl += 4 // Altitude\n\treturn l\n}\n\nfunc (rr *LP) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += domainNameLen(rr.Fqdn, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *MB) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Mb, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *MD) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Md, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *MF) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Mf, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *MG) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Mg, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *MINFO) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Rmail, off+l, compression, true)\n\tl += domainNameLen(rr.Email, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *MR) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Mr, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *MX) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += domainNameLen(rr.Mx, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *NAPTR) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Order\n\tl += 2 // Preference\n\tl += len(rr.Flags) + 1\n\tl += len(rr.Service) + 1\n\tl += len(rr.Regexp) + 1\n\tl += domainNameLen(rr.Replacement, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *NID) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += 8 // NodeID\n\treturn l\n}\n\nfunc (rr *NIMLOC) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Locator) / 2\n\treturn l\n}\n\nfunc (rr *NINFO) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, x := range rr.ZSData {\n\t\tl += len(x) + 1\n\t}\n\treturn l\n}\n\nfunc (rr *NS) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Ns, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *NSAPPTR) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Ptr, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++    // Hash\n\tl++    // Flags\n\tl += 2 // Iterations\n\tl++    // SaltLength\n\tl += len(rr.Salt) / 2\n\treturn l\n}\n\nfunc (rr *NULL) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Data)\n\treturn l\n}\n\nfunc (rr *NXNAME) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\treturn l\n}\n\nfunc (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += base64.StdEncoding.DecodedLen(len(rr.PublicKey))\n\treturn l\n}\n\nfunc (rr *PTR) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Ptr, off+l, compression, true)\n\treturn l\n}\n\nfunc (rr *PX) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += domainNameLen(rr.Map822, off+l, compression, false)\n\tl += domainNameLen(rr.Mapx400, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *RESINFO) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, x := range rr.Txt {\n\t\tl += len(x) + 1\n\t}\n\treturn l\n}\n\nfunc (rr *RFC3597) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Rdata) / 2\n\treturn l\n}\n\nfunc (rr *RKEY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Flags\n\tl++    // Protocol\n\tl++    // Algorithm\n\tl += base64.StdEncoding.DecodedLen(len(rr.PublicKey))\n\treturn l\n}\n\nfunc (rr *RP) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Mbox, off+l, compression, false)\n\tl += domainNameLen(rr.Txt, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *RRSIG) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // TypeCovered\n\tl++    // Algorithm\n\tl++    // Labels\n\tl += 4 // OrigTtl\n\tl += 4 // Expiration\n\tl += 4 // Inception\n\tl += 2 // KeyTag\n\tl += domainNameLen(rr.SignerName, off+l, compression, false)\n\tl += base64.StdEncoding.DecodedLen(len(rr.Signature))\n\treturn l\n}\n\nfunc (rr *RT) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Preference\n\tl += domainNameLen(rr.Host, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *SMIMEA) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++ // Usage\n\tl++ // Selector\n\tl++ // MatchingType\n\tl += len(rr.Certificate) / 2\n\treturn l\n}\n\nfunc (rr *SOA) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Ns, off+l, compression, true)\n\tl += domainNameLen(rr.Mbox, off+l, compression, true)\n\tl += 4 // Serial\n\tl += 4 // Refresh\n\tl += 4 // Retry\n\tl += 4 // Expire\n\tl += 4 // Minttl\n\treturn l\n}\n\nfunc (rr *SPF) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, x := range rr.Txt {\n\t\tl += len(x) + 1\n\t}\n\treturn l\n}\n\nfunc (rr *SRV) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Priority\n\tl += 2 // Weight\n\tl += 2 // Port\n\tl += domainNameLen(rr.Target, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *SSHFP) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++ // Algorithm\n\tl++ // Type\n\tl += len(rr.FingerPrint) / 2\n\treturn l\n}\n\nfunc (rr *SVCB) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Priority\n\tl += domainNameLen(rr.Target, off+l, compression, false)\n\tfor _, x := range rr.Value {\n\t\tl += 4 + int(x.len())\n\t}\n\treturn l\n}\n\nfunc (rr *TA) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // KeyTag\n\tl++    // Algorithm\n\tl++    // DigestType\n\tl += len(rr.Digest) / 2\n\treturn l\n}\n\nfunc (rr *TALINK) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.PreviousName, off+l, compression, false)\n\tl += domainNameLen(rr.NextName, off+l, compression, false)\n\treturn l\n}\n\nfunc (rr *TKEY) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Algorithm, off+l, compression, false)\n\tl += 4 // Inception\n\tl += 4 // Expiration\n\tl += 2 // Mode\n\tl += 2 // Error\n\tl += 2 // KeySize\n\tl += len(rr.Key) / 2\n\tl += 2 // OtherLen\n\tl += len(rr.OtherData) / 2\n\treturn l\n}\n\nfunc (rr *TLSA) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl++ // Usage\n\tl++ // Selector\n\tl++ // MatchingType\n\tl += len(rr.Certificate) / 2\n\treturn l\n}\n\nfunc (rr *TSIG) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += domainNameLen(rr.Algorithm, off+l, compression, false)\n\tl += 6 // TimeSigned\n\tl += 2 // Fudge\n\tl += 2 // MACSize\n\tl += len(rr.MAC) / 2\n\tl += 2 // OrigId\n\tl += 2 // Error\n\tl += 2 // OtherLen\n\tl += len(rr.OtherData) / 2\n\treturn l\n}\n\nfunc (rr *TXT) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tfor _, x := range rr.Txt {\n\t\tl += len(x) + 1\n\t}\n\treturn l\n}\n\nfunc (rr *UID) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 4 // Uid\n\treturn l\n}\n\nfunc (rr *UINFO) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.Uinfo) + 1\n\treturn l\n}\n\nfunc (rr *URI) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 2 // Priority\n\tl += 2 // Weight\n\tl += len(rr.Target)\n\treturn l\n}\n\nfunc (rr *X25) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += len(rr.PSDNAddress) + 1\n\treturn l\n}\n\nfunc (rr *ZONEMD) len(off int, compression map[string]struct{}) int {\n\tl := rr.Hdr.len(off, compression)\n\tl += 4 // Serial\n\tl++    // Scheme\n\tl++    // Hash\n\tl += len(rr.Digest) / 2\n\treturn l\n}\n\n// copy() functions\nfunc (rr *A) copy() RR {\n\treturn &A{rr.Hdr, cloneSlice(rr.A)}\n}\n\nfunc (rr *AAAA) copy() RR {\n\treturn &AAAA{rr.Hdr, cloneSlice(rr.AAAA)}\n}\n\nfunc (rr *AFSDB) copy() RR {\n\treturn &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname}\n}\n\nfunc (rr *AMTRELAY) copy() RR {\n\treturn &AMTRELAY{\n\t\trr.Hdr,\n\t\trr.Precedence,\n\t\trr.GatewayType,\n\t\tcloneSlice(rr.GatewayAddr),\n\t\trr.GatewayHost,\n\t}\n}\n\nfunc (rr *ANY) copy() RR {\n\treturn &ANY{rr.Hdr}\n}\n\nfunc (rr *APL) copy() RR {\n\tPrefixes := make([]APLPrefix, len(rr.Prefixes))\n\tfor i, e := range rr.Prefixes {\n\t\tPrefixes[i] = e.copy()\n\t}\n\treturn &APL{rr.Hdr, Prefixes}\n}\n\nfunc (rr *AVC) copy() RR {\n\treturn &AVC{rr.Hdr, cloneSlice(rr.Txt)}\n}\n\nfunc (rr *CAA) copy() RR {\n\treturn &CAA{\n\t\trr.Hdr,\n\t\trr.Flag,\n\t\trr.Tag,\n\t\trr.Value,\n\t}\n}\n\nfunc (rr *CDNSKEY) copy() RR {\n\treturn &CDNSKEY{*rr.DNSKEY.copy().(*DNSKEY)}\n}\n\nfunc (rr *CDS) copy() RR {\n\treturn &CDS{*rr.DS.copy().(*DS)}\n}\n\nfunc (rr *CERT) copy() RR {\n\treturn &CERT{\n\t\trr.Hdr,\n\t\trr.Type,\n\t\trr.KeyTag,\n\t\trr.Algorithm,\n\t\trr.Certificate,\n\t}\n}\n\nfunc (rr *CNAME) copy() RR {\n\treturn &CNAME{rr.Hdr, rr.Target}\n}\n\nfunc (rr *CSYNC) copy() RR {\n\treturn &CSYNC{\n\t\trr.Hdr,\n\t\trr.Serial,\n\t\trr.Flags,\n\t\tcloneSlice(rr.TypeBitMap),\n\t}\n}\n\nfunc (rr *DHCID) copy() RR {\n\treturn &DHCID{rr.Hdr, rr.Digest}\n}\n\nfunc (rr *DLV) copy() RR {\n\treturn &DLV{*rr.DS.copy().(*DS)}\n}\n\nfunc (rr *DNAME) copy() RR {\n\treturn &DNAME{rr.Hdr, rr.Target}\n}\n\nfunc (rr *DNSKEY) copy() RR {\n\treturn &DNSKEY{\n\t\trr.Hdr,\n\t\trr.Flags,\n\t\trr.Protocol,\n\t\trr.Algorithm,\n\t\trr.PublicKey,\n\t}\n}\n\nfunc (rr *DS) copy() RR {\n\treturn &DS{\n\t\trr.Hdr,\n\t\trr.KeyTag,\n\t\trr.Algorithm,\n\t\trr.DigestType,\n\t\trr.Digest,\n\t}\n}\n\nfunc (rr *EID) copy() RR {\n\treturn &EID{rr.Hdr, rr.Endpoint}\n}\n\nfunc (rr *EUI48) copy() RR {\n\treturn &EUI48{rr.Hdr, rr.Address}\n}\n\nfunc (rr *EUI64) copy() RR {\n\treturn &EUI64{rr.Hdr, rr.Address}\n}\n\nfunc (rr *GID) copy() RR {\n\treturn &GID{rr.Hdr, rr.Gid}\n}\n\nfunc (rr *GPOS) copy() RR {\n\treturn &GPOS{\n\t\trr.Hdr,\n\t\trr.Longitude,\n\t\trr.Latitude,\n\t\trr.Altitude,\n\t}\n}\n\nfunc (rr *HINFO) copy() RR {\n\treturn &HINFO{rr.Hdr, rr.Cpu, rr.Os}\n}\n\nfunc (rr *HIP) copy() RR {\n\treturn &HIP{\n\t\trr.Hdr,\n\t\trr.HitLength,\n\t\trr.PublicKeyAlgorithm,\n\t\trr.PublicKeyLength,\n\t\trr.Hit,\n\t\trr.PublicKey,\n\t\tcloneSlice(rr.RendezvousServers),\n\t}\n}\n\nfunc (rr *HTTPS) copy() RR {\n\treturn &HTTPS{*rr.SVCB.copy().(*SVCB)}\n}\n\nfunc (rr *IPSECKEY) copy() RR {\n\treturn &IPSECKEY{\n\t\trr.Hdr,\n\t\trr.Precedence,\n\t\trr.GatewayType,\n\t\trr.Algorithm,\n\t\tcloneSlice(rr.GatewayAddr),\n\t\trr.GatewayHost,\n\t\trr.PublicKey,\n\t}\n}\n\nfunc (rr *ISDN) copy() RR {\n\treturn &ISDN{rr.Hdr, rr.Address, rr.SubAddress}\n}\n\nfunc (rr *KEY) copy() RR {\n\treturn &KEY{*rr.DNSKEY.copy().(*DNSKEY)}\n}\n\nfunc (rr *KX) copy() RR {\n\treturn &KX{rr.Hdr, rr.Preference, rr.Exchanger}\n}\n\nfunc (rr *L32) copy() RR {\n\treturn &L32{rr.Hdr, rr.Preference, cloneSlice(rr.Locator32)}\n}\n\nfunc (rr *L64) copy() RR {\n\treturn &L64{rr.Hdr, rr.Preference, rr.Locator64}\n}\n\nfunc (rr *LOC) copy() RR {\n\treturn &LOC{\n\t\trr.Hdr,\n\t\trr.Version,\n\t\trr.Size,\n\t\trr.HorizPre,\n\t\trr.VertPre,\n\t\trr.Latitude,\n\t\trr.Longitude,\n\t\trr.Altitude,\n\t}\n}\n\nfunc (rr *LP) copy() RR {\n\treturn &LP{rr.Hdr, rr.Preference, rr.Fqdn}\n}\n\nfunc (rr *MB) copy() RR {\n\treturn &MB{rr.Hdr, rr.Mb}\n}\n\nfunc (rr *MD) copy() RR {\n\treturn &MD{rr.Hdr, rr.Md}\n}\n\nfunc (rr *MF) copy() RR {\n\treturn &MF{rr.Hdr, rr.Mf}\n}\n\nfunc (rr *MG) copy() RR {\n\treturn &MG{rr.Hdr, rr.Mg}\n}\n\nfunc (rr *MINFO) copy() RR {\n\treturn &MINFO{rr.Hdr, rr.Rmail, rr.Email}\n}\n\nfunc (rr *MR) copy() RR {\n\treturn &MR{rr.Hdr, rr.Mr}\n}\n\nfunc (rr *MX) copy() RR {\n\treturn &MX{rr.Hdr, rr.Preference, rr.Mx}\n}\n\nfunc (rr *NAPTR) copy() RR {\n\treturn &NAPTR{\n\t\trr.Hdr,\n\t\trr.Order,\n\t\trr.Preference,\n\t\trr.Flags,\n\t\trr.Service,\n\t\trr.Regexp,\n\t\trr.Replacement,\n\t}\n}\n\nfunc (rr *NID) copy() RR {\n\treturn &NID{rr.Hdr, rr.Preference, rr.NodeID}\n}\n\nfunc (rr *NIMLOC) copy() RR {\n\treturn &NIMLOC{rr.Hdr, rr.Locator}\n}\n\nfunc (rr *NINFO) copy() RR {\n\treturn &NINFO{rr.Hdr, cloneSlice(rr.ZSData)}\n}\n\nfunc (rr *NS) copy() RR {\n\treturn &NS{rr.Hdr, rr.Ns}\n}\n\nfunc (rr *NSAPPTR) copy() RR {\n\treturn &NSAPPTR{rr.Hdr, rr.Ptr}\n}\n\nfunc (rr *NSEC) copy() RR {\n\treturn &NSEC{rr.Hdr, rr.NextDomain, cloneSlice(rr.TypeBitMap)}\n}\n\nfunc (rr *NSEC3) copy() RR {\n\treturn &NSEC3{\n\t\trr.Hdr,\n\t\trr.Hash,\n\t\trr.Flags,\n\t\trr.Iterations,\n\t\trr.SaltLength,\n\t\trr.Salt,\n\t\trr.HashLength,\n\t\trr.NextDomain,\n\t\tcloneSlice(rr.TypeBitMap),\n\t}\n}\n\nfunc (rr *NSEC3PARAM) copy() RR {\n\treturn &NSEC3PARAM{\n\t\trr.Hdr,\n\t\trr.Hash,\n\t\trr.Flags,\n\t\trr.Iterations,\n\t\trr.SaltLength,\n\t\trr.Salt,\n\t}\n}\n\nfunc (rr *NULL) copy() RR {\n\treturn &NULL{rr.Hdr, rr.Data}\n}\n\nfunc (rr *NXNAME) copy() RR {\n\treturn &NXNAME{rr.Hdr}\n}\n\nfunc (rr *NXT) copy() RR {\n\treturn &NXT{*rr.NSEC.copy().(*NSEC)}\n}\n\nfunc (rr *OPENPGPKEY) copy() RR {\n\treturn &OPENPGPKEY{rr.Hdr, rr.PublicKey}\n}\n\nfunc (rr *OPT) copy() RR {\n\tOption := make([]EDNS0, len(rr.Option))\n\tfor i, e := range rr.Option {\n\t\tOption[i] = e.copy()\n\t}\n\treturn &OPT{rr.Hdr, Option}\n}\n\nfunc (rr *PTR) copy() RR {\n\treturn &PTR{rr.Hdr, rr.Ptr}\n}\n\nfunc (rr *PX) copy() RR {\n\treturn &PX{\n\t\trr.Hdr,\n\t\trr.Preference,\n\t\trr.Map822,\n\t\trr.Mapx400,\n\t}\n}\n\nfunc (rr *RESINFO) copy() RR {\n\treturn &RESINFO{rr.Hdr, cloneSlice(rr.Txt)}\n}\n\nfunc (rr *RFC3597) copy() RR {\n\treturn &RFC3597{rr.Hdr, rr.Rdata}\n}\n\nfunc (rr *RKEY) copy() RR {\n\treturn &RKEY{\n\t\trr.Hdr,\n\t\trr.Flags,\n\t\trr.Protocol,\n\t\trr.Algorithm,\n\t\trr.PublicKey,\n\t}\n}\n\nfunc (rr *RP) copy() RR {\n\treturn &RP{rr.Hdr, rr.Mbox, rr.Txt}\n}\n\nfunc (rr *RRSIG) copy() RR {\n\treturn &RRSIG{\n\t\trr.Hdr,\n\t\trr.TypeCovered,\n\t\trr.Algorithm,\n\t\trr.Labels,\n\t\trr.OrigTtl,\n\t\trr.Expiration,\n\t\trr.Inception,\n\t\trr.KeyTag,\n\t\trr.SignerName,\n\t\trr.Signature,\n\t}\n}\n\nfunc (rr *RT) copy() RR {\n\treturn &RT{rr.Hdr, rr.Preference, rr.Host}\n}\n\nfunc (rr *SIG) copy() RR {\n\treturn &SIG{*rr.RRSIG.copy().(*RRSIG)}\n}\n\nfunc (rr *SMIMEA) copy() RR {\n\treturn &SMIMEA{\n\t\trr.Hdr,\n\t\trr.Usage,\n\t\trr.Selector,\n\t\trr.MatchingType,\n\t\trr.Certificate,\n\t}\n}\n\nfunc (rr *SOA) copy() RR {\n\treturn &SOA{\n\t\trr.Hdr,\n\t\trr.Ns,\n\t\trr.Mbox,\n\t\trr.Serial,\n\t\trr.Refresh,\n\t\trr.Retry,\n\t\trr.Expire,\n\t\trr.Minttl,\n\t}\n}\n\nfunc (rr *SPF) copy() RR {\n\treturn &SPF{rr.Hdr, cloneSlice(rr.Txt)}\n}\n\nfunc (rr *SRV) copy() RR {\n\treturn &SRV{\n\t\trr.Hdr,\n\t\trr.Priority,\n\t\trr.Weight,\n\t\trr.Port,\n\t\trr.Target,\n\t}\n}\n\nfunc (rr *SSHFP) copy() RR {\n\treturn &SSHFP{\n\t\trr.Hdr,\n\t\trr.Algorithm,\n\t\trr.Type,\n\t\trr.FingerPrint,\n\t}\n}\n\nfunc (rr *SVCB) copy() RR {\n\tValue := make([]SVCBKeyValue, len(rr.Value))\n\tfor i, e := range rr.Value {\n\t\tValue[i] = e.copy()\n\t}\n\treturn &SVCB{\n\t\trr.Hdr,\n\t\trr.Priority,\n\t\trr.Target,\n\t\tValue,\n\t}\n}\n\nfunc (rr *TA) copy() RR {\n\treturn &TA{\n\t\trr.Hdr,\n\t\trr.KeyTag,\n\t\trr.Algorithm,\n\t\trr.DigestType,\n\t\trr.Digest,\n\t}\n}\n\nfunc (rr *TALINK) copy() RR {\n\treturn &TALINK{rr.Hdr, rr.PreviousName, rr.NextName}\n}\n\nfunc (rr *TKEY) copy() RR {\n\treturn &TKEY{\n\t\trr.Hdr,\n\t\trr.Algorithm,\n\t\trr.Inception,\n\t\trr.Expiration,\n\t\trr.Mode,\n\t\trr.Error,\n\t\trr.KeySize,\n\t\trr.Key,\n\t\trr.OtherLen,\n\t\trr.OtherData,\n\t}\n}\n\nfunc (rr *TLSA) copy() RR {\n\treturn &TLSA{\n\t\trr.Hdr,\n\t\trr.Usage,\n\t\trr.Selector,\n\t\trr.MatchingType,\n\t\trr.Certificate,\n\t}\n}\n\nfunc (rr *TSIG) copy() RR {\n\treturn &TSIG{\n\t\trr.Hdr,\n\t\trr.Algorithm,\n\t\trr.TimeSigned,\n\t\trr.Fudge,\n\t\trr.MACSize,\n\t\trr.MAC,\n\t\trr.OrigId,\n\t\trr.Error,\n\t\trr.OtherLen,\n\t\trr.OtherData,\n\t}\n}\n\nfunc (rr *TXT) copy() RR {\n\treturn &TXT{rr.Hdr, cloneSlice(rr.Txt)}\n}\n\nfunc (rr *UID) copy() RR {\n\treturn &UID{rr.Hdr, rr.Uid}\n}\n\nfunc (rr *UINFO) copy() RR {\n\treturn &UINFO{rr.Hdr, rr.Uinfo}\n}\n\nfunc (rr *URI) copy() RR {\n\treturn &URI{\n\t\trr.Hdr,\n\t\trr.Priority,\n\t\trr.Weight,\n\t\trr.Target,\n\t}\n}\n\nfunc (rr *X25) copy() RR {\n\treturn &X25{rr.Hdr, rr.PSDNAddress}\n}\n\nfunc (rr *ZONEMD) copy() RR {\n\treturn &ZONEMD{\n\t\trr.Hdr,\n\t\trr.Serial,\n\t\trr.Scheme,\n\t\trr.Hash,\n\t\trr.Digest,\n\t}\n}\n"
  }
]