pax_global_header00006660000000000000000000000064147674267170014537gustar00rootroot0000000000000052 comment=0b2473868eb4eefbd0eb9bec0e69cd737977d824 golang-github-miekg-dns-1.1.64/000077500000000000000000000000001476742671700162335ustar00rootroot00000000000000golang-github-miekg-dns-1.1.64/.codecov.yml000066400000000000000000000002001476742671700204460ustar00rootroot00000000000000coverage: status: project: default: target: 40% threshold: null patch: false changes: false golang-github-miekg-dns-1.1.64/.github/000077500000000000000000000000001476742671700175735ustar00rootroot00000000000000golang-github-miekg-dns-1.1.64/.github/dependabot.yml000066400000000000000000000002471476742671700224260ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "monthly" groups: all: patterns: - "*" golang-github-miekg-dns-1.1.64/.github/pull_request_template.md000066400000000000000000000004131476742671700245320ustar00rootroot00000000000000Thanks for you pull request, do note the following: * If your PR introduces backward incompatible changes it will very likely not be merged. * We support the last two major Go versions, if your PR uses features from a too new Go version, it will not be merged. golang-github-miekg-dns-1.1.64/.github/workflows/000077500000000000000000000000001476742671700216305ustar00rootroot00000000000000golang-github-miekg-dns-1.1.64/.github/workflows/codeql-analysis.yml000066400000000000000000000011461476742671700254450ustar00rootroot00000000000000name: "Code scanning - action" on: push: branches: [master, ] pull_request: branches: [master] schedule: - cron: '0 23 * * 5' jobs: CodeQL-Build: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 with: fetch-depth: 2 - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} - name: Initialize CodeQL uses: github/codeql-action/init@v2 - name: Autobuild uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 golang-github-miekg-dns-1.1.64/.github/workflows/go.yml000066400000000000000000000006561476742671700227670ustar00rootroot00000000000000name: Go on: [push, pull_request] jobs: build: name: Build and Test runs-on: ubuntu-latest strategy: matrix: go: [ 1.22.x, 1.23.x ] steps: - name: Set up Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - name: Check out code uses: actions/checkout@v3 - name: Build run: go build -v ./... - name: Test run: go test -v ./... golang-github-miekg-dns-1.1.64/.gitignore000066400000000000000000000000301476742671700202140ustar00rootroot00000000000000*.6 tags test.out a.out golang-github-miekg-dns-1.1.64/AUTHORS000066400000000000000000000000331476742671700172770ustar00rootroot00000000000000Miek Gieben golang-github-miekg-dns-1.1.64/CODEOWNERS000066400000000000000000000000221476742671700176200ustar00rootroot00000000000000* @miekg @tmthrgd golang-github-miekg-dns-1.1.64/CONTRIBUTORS000066400000000000000000000002261476742671700201130ustar00rootroot00000000000000Alex A. Skinner Andrew Tunnell-Jones Ask Bjørn Hansen Dave Cheney Dusty Wilson Marek Majkowski Peter van Dijk Omri Bahumi Alex Sergeyev James Hartig golang-github-miekg-dns-1.1.64/COPYRIGHT000066400000000000000000000010001476742671700175150ustar00rootroot00000000000000Copyright 2009 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. Extensions of the original work are copyright (c) 2011 Miek Gieben Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. Copyright 2014 CloudFlare. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. golang-github-miekg-dns-1.1.64/LICENSE000066400000000000000000000030401476742671700172350ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-github-miekg-dns-1.1.64/Makefile.fuzz000066400000000000000000000012371476742671700206730ustar00rootroot00000000000000# Makefile for fuzzing # # Use go-fuzz and needs the tools installed. # See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/ # # Installing go-fuzz: # $ make -f Makefile.fuzz get # Installs: # * github.com/dvyukov/go-fuzz/go-fuzz # * get github.com/dvyukov/go-fuzz/go-fuzz-build all: build .PHONY: build build: go-fuzz-build -tags fuzz github.com/miekg/dns .PHONY: build-newrr build-newrr: go-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns .PHONY: fuzz fuzz: go-fuzz -bin=dns-fuzz.zip -workdir=fuzz .PHONY: get get: go get github.com/dvyukov/go-fuzz/go-fuzz go get github.com/dvyukov/go-fuzz/go-fuzz-build .PHONY: clean clean: rm *-fuzz.zip golang-github-miekg-dns-1.1.64/Makefile.release000066400000000000000000000017401476742671700213140ustar00rootroot00000000000000# Makefile for releasing. # # The release is controlled from version.go. The version found there is # used to tag the git repo, we're not building any artifacts so there is nothing # to upload to github. # # * Up the version in version.go # * Run: make -f Makefile.release release # * will *commit* your change with 'Release $VERSION' # * push to github # define GO //+build ignore package main import ( "fmt" "github.com/miekg/dns" ) func main() { fmt.Println(dns.Version.String()) } endef $(file > version_release.go,$(GO)) VERSION:=$(shell go run version_release.go) TAG="v$(VERSION)" all: @echo Use the \'release\' target to start a release $(VERSION) rm -f version_release.go .PHONY: release release: commit push @echo Released $(VERSION) rm -f version_release.go .PHONY: commit commit: @echo Committing release $(VERSION) git commit -am"Release $(VERSION)" git tag $(TAG) .PHONY: push push: @echo Pushing release $(VERSION) to master git push --tags git push golang-github-miekg-dns-1.1.64/README.md000066400000000000000000000154241476742671700175200ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns) [![Code Coverage](https://img.shields.io/codecov/c/github/miekg/dns/master.svg)](https://codecov.io/github/miekg/dns?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns) [![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns) # Alternative (more granular) approach to a DNS library > Less is more. Complete and usable DNS library. All Resource Records are supported, including the DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there isn't a convenience function for it. Server side and client side programming is supported, i.e. you can build servers and resolvers with it. We try to keep the "master" branch as sane as possible and at the bleeding edge of standards, avoiding breaking changes wherever reasonable. We support the last two versions of Go. # Goals * KISS; * Fast; * Small API. If it's easy to code in Go, don't make a function for it. # Users A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/coredns/coredns * https://github.com/abh/geodns * https://github.com/baidu/bfe * http://www.statdns.com/ * http://www.dnsinspect.com/ * https://github.com/chuangbo/jianbing-dictionary-dns * http://www.dns-lg.com/ * https://github.com/fcambus/rrda * https://github.com/kenshinx/godns * https://github.com/skynetservices/skydns * https://github.com/hashicorp/consul * https://github.com/DevelopersPL/godnsagent * https://github.com/duedil-ltd/discodns * https://github.com/StalkR/dns-reverse-proxy * https://github.com/tianon/rawdns * https://mesosphere.github.io/mesos-dns/ * https://github.com/fcambus/statzone * https://github.com/benschw/dns-clb-go * https://github.com/corny/dnscheck for * https://github.com/miekg/unbound * https://github.com/miekg/exdns * https://dnslookup.org * https://github.com/looterz/grimd * https://github.com/phamhongviet/serf-dns * https://github.com/mehrdadrad/mylg * https://github.com/bamarni/dockness * https://github.com/fffaraz/microdns * https://github.com/ipdcode/hades * https://github.com/StackExchange/dnscontrol/ * https://www.dnsperf.com/ * https://dnssectest.net/ * https://github.com/oif/apex * https://github.com/jedisct1/dnscrypt-proxy * https://github.com/jedisct1/rpdns * https://github.com/xor-gate/sshfp * https://github.com/rs/dnstrace * https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss)) * https://render.com * https://github.com/peterzen/goresolver * https://github.com/folbricht/routedns * https://domainr.com/ * https://zonedb.org/ * https://router7.org/ * https://github.com/fortio/dnsping * https://github.com/Luzilla/dnsbl_exporter * https://github.com/bodgit/tsig * https://github.com/v2fly/v2ray-core (test only) * https://kuma.io/ * https://www.misaka.io/services/dns * https://ping.sx/dig * https://fleetdeck.io/ * https://github.com/markdingo/autoreverse * https://github.com/slackhq/nebula * https://addr.tools/ * https://dnscheck.tools/ * https://github.com/egbakou/domainverifier * https://github.com/semihalev/sdns * https://github.com/wintbiit/NineDNS * https://linuxcontainers.org/incus/ * https://ifconfig.es * https://github.com/zmap/zdns * https://framagit.org/bortzmeyer/check-soa Send pull request if you want to be listed here. # Features * UDP/TCP queries, IPv4 and IPv6 * RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported * Fast * Server side programming (mimicking the net/http package) * Client side programming * DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519 * EDNS0, NSID, Cookies * AXFR/IXFR * TSIG, SIG(0) * DNS over TLS (DoT): encrypted connection between client and server over TCP * DNS name compression Have fun! Miek Gieben - 2010-2012 - DNS Authors 2012- # Building This library uses Go modules and uses semantic versioning. Building is done with the `go` tool, so the following should work: go get github.com/miekg/dns go build github.com/miekg/dns ## Examples A short "how to use the API" is at the beginning of doc.go (this also will show when you call `godoc github.com/miekg/dns`). Example programs can be found in the `github.com/miekg/exdns` repository. ## Supported RFCs *all of them* * 103{4,5} - DNS standard * 1183 - ISDN, X25 and other deprecated records * 1348 - NSAP record (removed the record) * 1982 - Serial Arithmetic * 1876 - LOC record * 1995 - IXFR * 1996 - DNS notify * 2136 - DNS Update (dynamic updates) * 2181 - RRset definition - there is no RRset type though, just []RR * 2537 - RSAMD5 DNS keys * 2065 - DNSSEC (updated in later RFCs) * 2671 - EDNS record * 2782 - SRV record * 2845 - TSIG record * 2915 - NAPTR record * 2929 - DNS IANA Considerations * 3110 - RSASHA1 DNS keys * 3123 - APL record * 3225 - DO bit (DNSSEC OK) * 340{1,2,3} - NAPTR record * 3445 - Limiting the scope of (DNS)KEY * 3596 - AAAA record * 3597 - Unknown RRs * 4025 - A Method for Storing IPsec Keying Material in DNS * 403{3,4,5} - DNSSEC + validation functions * 4255 - SSHFP record * 4343 - Case insensitivity * 4408 - SPF record * 4509 - SHA256 Hash in DS * 4592 - Wildcards in the DNS * 4635 - HMAC SHA TSIG * 4701 - DHCID * 4892 - id.server * 5001 - NSID * 5155 - NSEC3 record * 5205 - HIP record * 5702 - SHA2 in the DNS * 5936 - AXFR * 5966 - TCP implementation recommendations * 6605 - ECDSA * 6725 - IANA Registry Update * 6742 - ILNP DNS * 6840 - Clarifications and Implementation Notes for DNS Security * 6844 - CAA record * 6891 - EDNS0 update * 6895 - DNS IANA considerations * 6944 - DNSSEC DNSKEY Algorithm Status * 6975 - Algorithm Understanding in DNSSEC * 7043 - EUI48/EUI64 records * 7314 - DNS (EDNS) EXPIRE Option * 7477 - CSYNC RR * 7828 - edns-tcp-keepalive EDNS0 Option * 7553 - URI record * 7858 - DNS over TLS: Initiation and Performance Considerations * 7871 - EDNS0 Client Subnet * 7873 - Domain Name System (DNS) Cookies * 8080 - EdDSA for DNSSEC * 8499 - DNS Terminology * 8659 - DNS Certification Authority Authorization (CAA) Resource Record * 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery * 8914 - Extended DNS Errors * 8976 - Message Digest for DNS Zones (ZONEMD RR) * 9460 - Service Binding and Parameter Specification via the DNS * 9461 - Service Binding Mapping for DNS Servers * 9462 - Discovery of Designated Resolvers * 9460 - SVCB and HTTPS Records * 9606 - DNS Resolver Information * Draft - Compact Denial of Existence in DNSSEC ## Loosely Based Upon * ldns - * NSD - * Net::DNS - * GRONG - golang-github-miekg-dns-1.1.64/acceptfunc.go000066400000000000000000000035601476742671700207010ustar00rootroot00000000000000package dns // MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError. // It returns a MsgAcceptAction to indicate what should happen with the message. type MsgAcceptFunc func(dh Header) MsgAcceptAction // DefaultMsgAcceptFunc checks the request and will reject if: // // * isn't a request (don't respond in that case) // // * opcode isn't OpcodeQuery or OpcodeNotify // // * does not have exactly 1 question in the question section // // * has more than 1 RR in the Answer section // // * has more than 0 RRs in the Authority section // // * has more than 2 RRs in the Additional section var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc // MsgAcceptAction represents the action to be taken. type MsgAcceptAction int // Allowed returned values from a MsgAcceptFunc. const ( MsgAccept MsgAcceptAction = iota // Accept the message MsgReject // Reject the message with a RcodeFormatError MsgIgnore // Ignore the error and send nothing back. MsgRejectNotImplemented // Reject the message with a RcodeNotImplemented ) func defaultMsgAcceptFunc(dh Header) MsgAcceptAction { if isResponse := dh.Bits&_QR != 0; isResponse { return MsgIgnore } // Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs. opcode := int(dh.Bits>>11) & 0xF if opcode != OpcodeQuery && opcode != OpcodeNotify { return MsgRejectNotImplemented } if dh.Qdcount != 1 { return MsgReject } // NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11. if dh.Ancount > 1 { return MsgReject } // IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3. if dh.Nscount > 1 { return MsgReject } if dh.Arcount > 2 { return MsgReject } return MsgAccept } golang-github-miekg-dns-1.1.64/acceptfunc_test.go000066400000000000000000000055241476742671700217420ustar00rootroot00000000000000package dns import ( "encoding/binary" "net" "testing" ) func TestAcceptNotify(t *testing.T) { HandleFunc("example.org.", handleNotify) s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetNotify("example.org.") // Set a SOA hint in the answer section, this is allowed according to RFC 1996. soa, _ := NewRR("example.org. IN SOA sns.dns.icann.org. noc.dns.icann.org. 2018112827 7200 3600 1209600 3600") m.Answer = []RR{soa} c := new(Client) resp, _, err := c.Exchange(m, addrstr) if err != nil { t.Errorf("failed to exchange: %v", err) } if resp.Rcode != RcodeSuccess { t.Errorf("expected %s, got %s", RcodeToString[RcodeSuccess], RcodeToString[resp.Rcode]) } } func handleNotify(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) w.WriteMsg(m) } func TestInvalidMsg(t *testing.T) { HandleFunc("example.org.", func(ResponseWriter, *Msg) { t.Fatal("the handler must not be called in any of these tests") }) s, addrstr, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() s.MsgAcceptFunc = func(dh Header) MsgAcceptAction { switch dh.Id { case 0x0001: return MsgAccept case 0x0002: return MsgReject case 0x0003: return MsgIgnore case 0x0004: return MsgRejectNotImplemented default: t.Errorf("unexpected ID %x", dh.Id) return -1 } } invalidErrors := make(chan error) s.MsgInvalidFunc = func(m []byte, err error) { invalidErrors <- err } c, err := net.Dial("tcp", addrstr) if err != nil { t.Fatalf("cannot connect to test server: %v", err) } write := func(m []byte) { var length [2]byte binary.BigEndian.PutUint16(length[:], uint16(len(m))) _, err := c.Write(length[:]) if err != nil { t.Fatalf("length write failed: %v", err) } _, err = c.Write(m) if err != nil { t.Fatalf("content write failed: %v", err) } } /* Message is too short, so there is no header to accept or reject. */ tooShortMessage := make([]byte, 11) tooShortMessage[1] = 0x3 // ID = 3, would be ignored if it were parsable. write(tooShortMessage) // Expect an error to be reported. <-invalidErrors /* Message is accepted but is actually invalid. */ badMessage := make([]byte, 13) badMessage[1] = 0x1 // ID = 1, Accept. badMessage[5] = 1 // QDCOUNT = 1 badMessage[12] = 99 // Bad question section. Invalid! write(badMessage) // Expect an error to be reported. <-invalidErrors /* Message is rejected before it can be determined to be invalid. */ close(invalidErrors) // A call to InvalidMsgFunc would panic due to the closed chan. badMessage[1] = 0x2 // ID = 2, Reject write(badMessage) badMessage[1] = 0x3 // ID = 3, Ignore write(badMessage) badMessage[1] = 0x4 // ID = 4, RejectNotImplemented write(badMessage) } golang-github-miekg-dns-1.1.64/client.go000066400000000000000000000343331476742671700200460ustar00rootroot00000000000000package dns // A client implementation. import ( "context" "crypto/tls" "encoding/binary" "io" "net" "strings" "time" ) const ( dnsTimeout time.Duration = 2 * time.Second tcpIdleTimeout time.Duration = 8 * time.Second ) func isPacketConn(c net.Conn) bool { if _, ok := c.(net.PacketConn); !ok { return false } if ua, ok := c.LocalAddr().(*net.UnixAddr); ok { return ua.Net == "unixgram" || ua.Net == "unixpacket" } return true } // A Conn represents a connection to a DNS server. type Conn struct { net.Conn // a net.Conn holding the connection UDPSize uint16 // minimum receive buffer for UDP messages TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. tsigRequestMAC string } func (co *Conn) tsigProvider() TsigProvider { if co.TsigProvider != nil { return co.TsigProvider } // tsigSecretProvider will return ErrSecret if co.TsigSecret is nil. return tsigSecretProvider(co.TsigSecret) } // A Client defines parameters for a DNS client. type Client struct { Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) UDPSize uint16 // minimum receive buffer for UDP messages TLSConfig *tls.Config // TLS connection configuration Dialer *net.Dialer // a net.Dialer used to set local address, timeouts and more // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout, // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and // Client.Dialer) or context.Context.Deadline (see ExchangeContext) Timeout time.Duration DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. // SingleInflight previously serialised multiple concurrent queries for the // same Qname, Qtype and Qclass to ensure only one would be in flight at a // time. // // Deprecated: This is a no-op. Callers should implement their own in flight // query caching if needed. See github.com/miekg/dns/issues/1449. SingleInflight bool } // Exchange performs a synchronous UDP query. It sends the message m to the address // contained in a and waits for a reply. Exchange does not retry a failed query, nor // will it fall back to TCP in case of truncation. // See client.Exchange for more information on setting larger buffer sizes. func Exchange(m *Msg, a string) (r *Msg, err error) { client := Client{Net: "udp"} r, _, err = client.Exchange(m, a) return r, err } func (c *Client) dialTimeout() time.Duration { if c.Timeout != 0 { return c.Timeout } if c.DialTimeout != 0 { return c.DialTimeout } return dnsTimeout } func (c *Client) readTimeout() time.Duration { if c.ReadTimeout != 0 { return c.ReadTimeout } return dnsTimeout } func (c *Client) writeTimeout() time.Duration { if c.WriteTimeout != 0 { return c.WriteTimeout } return dnsTimeout } // Dial connects to the address on the named network. func (c *Client) Dial(address string) (conn *Conn, err error) { return c.DialContext(context.Background(), address) } // DialContext connects to the address on the named network, with a context.Context. func (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, err error) { // create a new dialer with the appropriate timeout var d net.Dialer if c.Dialer == nil { d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())} } else { d = *c.Dialer } network := c.Net if network == "" { network = "udp" } useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls") conn = new(Conn) if useTLS { network = strings.TrimSuffix(network, "-tls") tlsDialer := tls.Dialer{ NetDialer: &d, Config: c.TLSConfig, } conn.Conn, err = tlsDialer.DialContext(ctx, network, address) } else { conn.Conn, err = d.DialContext(ctx, network, address) } if err != nil { return nil, err } conn.UDPSize = c.UDPSize return conn, nil } // Exchange performs a synchronous query. It sends the message m to the address // contained in a and waits for a reply. Basic use pattern with a *dns.Client: // // c := new(dns.Client) // in, rtt, err := c.Exchange(message, "127.0.0.1:53") // // Exchange does not retry a failed query, nor will it fall back to TCP in // case of truncation. // It is up to the caller to create a message that allows for larger responses to be // returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger // buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit // of 512 bytes // To specify a local address or a timeout, the caller has to set the `Client.Dialer` // attribute appropriately func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) { co, err := c.Dial(address) if err != nil { return nil, 0, err } defer co.Close() return c.ExchangeWithConn(m, co) } // ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection // that will be used instead of creating a new one. // Usage pattern with a *dns.Client: // // c := new(dns.Client) // // connection management logic goes here // // conn := c.Dial(address) // in, rtt, err := c.ExchangeWithConn(message, conn) // // This allows users of the library to implement their own connection management, // as opposed to Exchange, which will always use new connections and incur the added overhead // that entails when using "tcp" and especially "tcp-tls" clients. func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { return c.ExchangeWithConnContext(context.Background(), m, conn) } // ExchangeWithConnContext has the same behaviour as ExchangeWithConn and // additionally obeys deadlines from the passed Context. func (c *Client) ExchangeWithConnContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { opt := m.IsEdns0() // If EDNS0 is used use that for size. if opt != nil && opt.UDPSize() >= MinMsgSize { co.UDPSize = opt.UDPSize() } // Otherwise use the client's configured UDP size. if opt == nil && c.UDPSize >= MinMsgSize { co.UDPSize = c.UDPSize } // write with the appropriate write timeout t := time.Now() writeDeadline := t.Add(c.getTimeoutForRequest(c.writeTimeout())) readDeadline := t.Add(c.getTimeoutForRequest(c.readTimeout())) if deadline, ok := ctx.Deadline(); ok { if deadline.Before(writeDeadline) { writeDeadline = deadline } if deadline.Before(readDeadline) { readDeadline = deadline } } co.SetWriteDeadline(writeDeadline) co.SetReadDeadline(readDeadline) co.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider if err = co.WriteMsg(m); err != nil { return nil, 0, err } if isPacketConn(co.Conn) { for { r, err = co.ReadMsg() // Ignore replies with mismatched IDs because they might be // responses to earlier queries that timed out. if err != nil || r.Id == m.Id { break } } } else { r, err = co.ReadMsg() if err == nil && r.Id != m.Id { err = ErrId } } rtt = time.Since(t) return r, rtt, err } // ReadMsg reads a message from the connection co. // If the received message contains a TSIG record the transaction signature // is verified. This method always tries to return the message, however if an // error is returned there are no guarantees that the returned message is a // valid representation of the packet read. func (co *Conn) ReadMsg() (*Msg, error) { p, err := co.ReadMsgHeader(nil) if err != nil { return nil, err } m := new(Msg) if err := m.Unpack(p); err != nil { // If an error was returned, we still want to allow the user to use // the message, but naively they can just check err if they don't want // to use an erroneous message return m, err } if t := m.IsTsig(); t != nil { // Need to work on the original message p, as that was used to calculate the tsig. err = TsigVerifyWithProvider(p, co.tsigProvider(), co.tsigRequestMAC, false) } return m, err } // ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil). // Returns message as a byte slice to be parsed with Msg.Unpack later on. // Note that error handling on the message body is not possible as only the header is parsed. func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { var ( p []byte n int err error ) if isPacketConn(co.Conn) { if co.UDPSize > MinMsgSize { p = make([]byte, co.UDPSize) } else { p = make([]byte, MinMsgSize) } n, err = co.Read(p) } else { var length uint16 if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil { return nil, err } p = make([]byte, length) n, err = io.ReadFull(co.Conn, p) } if err != nil { return nil, err } else if n < headerSize { return nil, ErrShortRead } p = p[:n] if hdr != nil { dh, _, err := unpackMsgHdr(p, 0) if err != nil { return nil, err } *hdr = dh } return p, err } // Read implements the net.Conn read method. func (co *Conn) Read(p []byte) (n int, err error) { if co.Conn == nil { return 0, ErrConnEmpty } if isPacketConn(co.Conn) { // UDP connection return co.Conn.Read(p) } var length uint16 if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil { return 0, err } if int(length) > len(p) { return 0, io.ErrShortBuffer } return io.ReadFull(co.Conn, p[:length]) } // WriteMsg sends a message through the connection co. // If the message m contains a TSIG record the transaction // signature is calculated. func (co *Conn) WriteMsg(m *Msg) (err error) { var out []byte if t := m.IsTsig(); t != nil { // Set tsigRequestMAC for the next read, although only used in zone transfers. out, co.tsigRequestMAC, err = TsigGenerateWithProvider(m, co.tsigProvider(), co.tsigRequestMAC, false) } else { out, err = m.Pack() } if err != nil { return err } _, err = co.Write(out) return err } // Write implements the net.Conn Write method. func (co *Conn) Write(p []byte) (int, error) { if len(p) > MaxMsgSize { return 0, &Error{err: "message too large"} } if isPacketConn(co.Conn) { return co.Conn.Write(p) } msg := make([]byte, 2+len(p)) binary.BigEndian.PutUint16(msg, uint16(len(p))) copy(msg[2:], p) return co.Conn.Write(msg) } // Return the appropriate timeout for a specific request func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration { var requestTimeout time.Duration if c.Timeout != 0 { requestTimeout = c.Timeout } else { requestTimeout = timeout } // net.Dialer.Timeout has priority if smaller than the timeouts computed so // far if c.Dialer != nil && c.Dialer.Timeout != 0 { if c.Dialer.Timeout < requestTimeout { requestTimeout = c.Dialer.Timeout } } return requestTimeout } // Dial connects to the address on the named network. func Dial(network, address string) (conn *Conn, err error) { conn = new(Conn) conn.Conn, err = net.Dial(network, address) if err != nil { return nil, err } return conn, nil } // ExchangeContext performs a synchronous UDP query, like Exchange. It // additionally obeys deadlines from the passed Context. func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) { client := Client{Net: "udp"} r, _, err = client.ExchangeContext(ctx, m, a) // ignoring rtt to leave the original ExchangeContext API unchanged, but // this function will go away return r, err } // ExchangeConn performs a synchronous query. It sends the message m via the connection // c and waits for a reply. The connection c is not closed by ExchangeConn. // Deprecated: This function is going away, but can easily be mimicked: // // co := &dns.Conn{Conn: c} // c is your net.Conn // co.WriteMsg(m) // in, _ := co.ReadMsg() // co.Close() func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { println("dns: ExchangeConn: this function is deprecated") co := new(Conn) co.Conn = c if err = co.WriteMsg(m); err != nil { return nil, err } r, err = co.ReadMsg() if err == nil && r.Id != m.Id { err = ErrId } return r, err } // DialTimeout acts like Dial but takes a timeout. func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) { client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}} return client.Dial(address) } // DialWithTLS connects to the address on the named network with TLS. func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) { if !strings.HasSuffix(network, "-tls") { network += "-tls" } client := Client{Net: network, TLSConfig: tlsConfig} return client.Dial(address) } // DialTimeoutWithTLS acts like DialWithTLS but takes a timeout. func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) { if !strings.HasSuffix(network, "-tls") { network += "-tls" } client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig} return client.Dial(address) } // ExchangeContext acts like Exchange, but honors the deadline on the provided // context, if present. If there is both a context deadline and a configured // timeout on the client, the earliest of the two takes effect. func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) { conn, err := c.DialContext(ctx, a) if err != nil { return nil, 0, err } defer conn.Close() return c.ExchangeWithConnContext(ctx, m, conn) } golang-github-miekg-dns-1.1.64/client_test.go000066400000000000000000000433021476742671700211010ustar00rootroot00000000000000package dns import ( "context" "crypto/tls" "errors" "fmt" "net" "strconv" "strings" "syscall" "testing" "time" ) func TestIsPacketConn(t *testing.T) { t.Run("UDP", func(t *testing.T) { s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c, err := net.Dial("udp", addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } defer c.Close() if !isPacketConn(c) { t.Error("UDP connection should be a packet conn") } if !isPacketConn(struct{ *net.UDPConn }{c.(*net.UDPConn)}) { t.Error("UDP connection (wrapped type) should be a packet conn") } }) t.Run("TCP", func(t *testing.T) { s, addrstr, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c, err := net.Dial("tcp", addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } defer c.Close() if isPacketConn(c) { t.Error("TCP connection should not be a packet conn") } if isPacketConn(struct{ *net.TCPConn }{c.(*net.TCPConn)}) { t.Error("TCP connection (wrapped type) should not be a packet conn") } }) t.Run("Unix datagram", func(t *testing.T) { s, addrstr, _, err := RunLocalUnixGramServer(tempFile(t, "unixgram.sock")) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c, err := net.Dial("unixgram", addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } defer c.Close() if !isPacketConn(c) { t.Error("Unix datagram connection should be a packet conn") } if !isPacketConn(struct{ *net.UnixConn }{c.(*net.UnixConn)}) { t.Error("Unix datagram connection (wrapped type) should be a packet conn") } }) t.Run("Unix Seqpacket", func(t *testing.T) { shutChan, addrstr, err := RunLocalUnixSeqPacketServer(tempFile(t, "unixpacket.sock")) if err != nil { if errors.Is(err, syscall.EPROTONOSUPPORT) { t.Skip("unix seqpacket not supported on this OS") } t.Fatalf("unable to run test server: %v", err) } defer func() { shutChan <- &struct{}{} }() c, err := net.Dial("unixpacket", addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } defer c.Close() if !isPacketConn(c) { t.Error("Unix datagram connection should be a packet conn") } if !isPacketConn(struct{ *net.UnixConn }{c.(*net.UnixConn)}) { t.Error("Unix datagram connection (wrapped type) should be a packet conn") } }) t.Run("Unix stream", func(t *testing.T) { s, addrstr, _, err := RunLocalUnixServer(tempFile(t, "unixstream.sock")) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c, err := net.Dial("unix", addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } defer c.Close() if isPacketConn(c) { t.Error("Unix stream connection should not be a packet conn") } if isPacketConn(struct{ *net.UnixConn }{c.(*net.UnixConn)}) { t.Error("Unix stream connection (wrapped type) should not be a packet conn") } }) } func TestDialUDP(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) conn, err := c.Dial(addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } if conn == nil { t.Fatalf("conn is nil") } } func TestClientSync(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatalf("failed to exchange: %v", err) } if r == nil { t.Fatal("response is nil") } if r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } // And now with plain Exchange(). r, err = Exchange(m, addrstr) if err != nil { t.Errorf("failed to exchange: %v", err) } if r == nil || r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } } func TestClientLocalAddress(t *testing.T) { HandleFunc("miek.nl.", HelloServerEchoAddrPort) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) laddr := net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 12345, Zone: ""} c.Dialer = &net.Dialer{LocalAddr: &laddr} r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatalf("failed to exchange: %v", err) } if r != nil && r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } if len(r.Extra) != 1 { t.Fatalf("failed to get additional answers\n%v", r) } txt := r.Extra[0].(*TXT) if txt == nil { t.Errorf("invalid TXT response\n%v", txt) } if len(txt.Txt) != 1 || !strings.Contains(txt.Txt[0], ":12345") { t.Errorf("invalid TXT response\n%v", txt.Txt) } } func TestClientTLSSyncV4(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } config := tls.Config{ Certificates: []tls.Certificate{cert}, } s, addrstr, _, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) // test tcp-tls c.Net = "tcp-tls" c.TLSConfig = &tls.Config{ InsecureSkipVerify: true, } r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatalf("failed to exchange: %v", err) } if r == nil { t.Fatal("response is nil") } if r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } // test tcp4-tls c.Net = "tcp4-tls" c.TLSConfig = &tls.Config{ InsecureSkipVerify: true, } r, _, err = c.Exchange(m, addrstr) if err != nil { t.Fatalf("failed to exchange: %v", err) } if r == nil { t.Fatal("response is nil") } if r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } } func isNetworkTimeout(err error) bool { // TODO: when Go 1.14 support is dropped, do this: https://golang.org/doc/go1.15#net var netError net.Error return errors.As(err, &netError) && netError.Timeout() } func TestClientSyncBadID(t *testing.T) { HandleFunc("miek.nl.", HelloServerBadID) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) // Test with client.Exchange, the plain Exchange function is just a wrapper, so // we don't need to test that separately. c := &Client{ Timeout: 10 * time.Millisecond, } if _, _, err := c.Exchange(m, addrstr); err == nil || !isNetworkTimeout(err) { t.Errorf("query did not time out") } } func TestClientSyncBadThenGoodID(t *testing.T) { HandleFunc("miek.nl.", HelloServerBadThenGoodID) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) r, _, err := c.Exchange(m, addrstr) if err != nil { t.Errorf("failed to exchange: %v", err) } if r.Id != m.Id { t.Errorf("failed to get response with expected Id") } } func TestClientSyncTCPBadID(t *testing.T) { HandleFunc("miek.nl.", HelloServerBadID) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := &Client{ Net: "tcp", } if _, _, err := c.Exchange(m, addrstr); err != ErrId { t.Errorf("did not find a bad Id") } } func TestClientEDNS0(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeDNSKEY) m.SetEdns0(2048, true) c := new(Client) r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatalf("failed to exchange: %v", err) } if r != nil && r.Rcode != RcodeSuccess { t.Errorf("failed to get a valid answer\n%v", r) } } // Validates the transmission and parsing of local EDNS0 options. func TestClientEDNS0Local(t *testing.T) { optStr1 := "1979:0x0707" optStr2 := strconv.Itoa(EDNS0LOCALSTART) + ":0x0601" handler := func(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1, 2) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello local edns"}} // If the local options are what we expect, then reflect them back. ec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String() ec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String() if ec1 == optStr1 && ec2 == optStr2 { m.Extra = append(m.Extra, req.Extra[0]) } w.WriteMsg(m) } HandleFunc("miek.nl.", handler) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %s", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) // Add two local edns options to the query. ec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}} ec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}} o := &OPT{Hdr: RR_Header{Name: ".", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}} m.Extra = append(m.Extra, o) c := new(Client) r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatalf("failed to exchange: %s", err) } if r == nil { t.Fatal("response is nil") } if r.Rcode != RcodeSuccess { t.Fatal("failed to get a valid answer") } txt := r.Extra[0].(*TXT).Txt[0] if txt != "Hello local edns" { t.Error("Unexpected result for miek.nl", txt, "!= Hello local edns") } // Validate the local options in the reply. got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String() if got != optStr1 { t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr1) } got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String() if got != optStr2 { t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr2) } } func TestClientConn(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") // This uses TCP just to make it slightly different than TestClientSync s, addrstr, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) cn, err := Dial("tcp", addrstr) if err != nil { t.Errorf("failed to dial %s: %v", addrstr, err) } err = cn.WriteMsg(m) if err != nil { t.Errorf("failed to exchange: %v", err) } r, err := cn.ReadMsg() if err != nil { t.Errorf("failed to get a valid answer: %v", err) } if r == nil || r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } err = cn.WriteMsg(m) if err != nil { t.Errorf("failed to exchange: %v", err) } h := new(Header) buf, err := cn.ReadMsgHeader(h) if buf == nil { t.Errorf("failed to get an valid answer\n%v", r) } if err != nil { t.Errorf("failed to get a valid answer: %v", err) } if int(h.Bits&0xF) != RcodeSuccess { t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r) } if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 { t.Errorf("expected to have question and additional in response; got something else: %+v", h) } if err = r.Unpack(buf); err != nil { t.Errorf("unable to unpack message fully: %v", err) } } func TestClientConnWriteSinglePacket(t *testing.T) { c := &countingConn{} conn := Conn{ Conn: c, } m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) err := conn.WriteMsg(m) if err != nil { t.Fatalf("failed to write: %v", err) } if c.writes != 1 { t.Fatalf("incorrect number of Write calls") } } func TestTruncatedMsg(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeSRV) cnt := 10 for i := 0; i < cnt; i++ { r := &SRV{ Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0}, Port: uint16(i + 8000), Target: "target.miek.nl.", } m.Answer = append(m.Answer, r) re := &A{ Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0}, A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i)).To4(), } m.Extra = append(m.Extra, re) } buf, err := m.Pack() if err != nil { t.Errorf("failed to pack: %v", err) } r := new(Msg) if err = r.Unpack(buf); err != nil { t.Errorf("unable to unpack message: %v", err) } if len(r.Answer) != cnt { t.Errorf("answer count after regular unpack doesn't match: %d", len(r.Answer)) } if len(r.Extra) != cnt { t.Errorf("extra count after regular unpack doesn't match: %d", len(r.Extra)) } m.Truncated = true buf, err = m.Pack() if err != nil { t.Errorf("failed to pack truncated message: %v", err) } r = new(Msg) if err = r.Unpack(buf); err != nil { t.Errorf("failed to unpack truncated message: %v", err) } if !r.Truncated { t.Errorf("truncated message wasn't unpacked as truncated") } if len(r.Answer) != cnt { t.Errorf("answer count after truncated unpack doesn't match: %d", len(r.Answer)) } if len(r.Extra) != cnt { t.Errorf("extra count after truncated unpack doesn't match: %d", len(r.Extra)) } // Now we want to remove almost all of the extra records // We're going to loop over the extra to get the count of the size of all // of them off := 0 buf1 := make([]byte, m.Len()) for i := 0; i < len(m.Extra); i++ { off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress) if err != nil { t.Errorf("failed to pack extra: %v", err) } } // Remove all of the extra bytes but 10 bytes from the end of buf off -= 10 buf1 = buf[:len(buf)-off] r = new(Msg) if err = r.Unpack(buf1); err == nil { t.Error("cutoff message should have failed to unpack") } // r's header might be still usable. if !r.Truncated { t.Error("truncated cutoff message wasn't unpacked as truncated") } if len(r.Answer) != cnt { t.Errorf("answer count after cutoff unpack doesn't match: %d", len(r.Answer)) } if len(r.Extra) != 0 { t.Errorf("extra count after cutoff unpack is not zero: %d", len(r.Extra)) } // Now we want to remove almost all of the answer records too buf1 = make([]byte, m.Len()) as := 0 for i := 0; i < len(m.Extra); i++ { off1 := off off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress) as = off - off1 if err != nil { t.Errorf("failed to pack extra: %v", err) } } // Keep exactly one answer left // This should still cause Answer to be nil off -= as buf1 = buf[:len(buf)-off] r = new(Msg) if err = r.Unpack(buf1); err == nil { t.Error("cutoff message should have failed to unpack") } if !r.Truncated { t.Error("truncated cutoff message wasn't unpacked as truncated") } if len(r.Answer) != 0 { t.Errorf("answer count after second cutoff unpack is not zero: %d", len(r.Answer)) } // Now leave only 1 byte of the question // Since the header is always 12 bytes, we just need to keep 13 buf1 = buf[:13] r = new(Msg) err = r.Unpack(buf1) if err == nil { t.Errorf("error should be nil after question cutoff unpack: %v", err) } // Finally, if we only have the header, we don't return an error. buf1 = buf[:12] r = new(Msg) if err = r.Unpack(buf1); err != nil { t.Errorf("from header-only unpack should not return an error: %v", err) } } func TestTimeout(t *testing.T) { // Set up a dummy UDP server that won't respond addr, err := net.ResolveUDPAddr("udp", ":0") if err != nil { t.Fatalf("unable to resolve local udp address: %v", err) } conn, err := net.ListenUDP("udp", addr) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer conn.Close() addrstr := conn.LocalAddr().String() // Message to send m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) runTest := func(name string, exchange func(m *Msg, addr string, timeout time.Duration) (*Msg, time.Duration, error)) { t.Run(name, func(t *testing.T) { start := time.Now() timeout := time.Millisecond allowable := timeout + 10*time.Millisecond _, _, err := exchange(m, addrstr, timeout) if err == nil { t.Errorf("no timeout using Client.%s", name) } length := time.Since(start) if length > allowable { t.Errorf("exchange took longer %v than specified Timeout %v", length, allowable) } }) } runTest("Exchange", func(m *Msg, addr string, timeout time.Duration) (*Msg, time.Duration, error) { c := &Client{Timeout: timeout} return c.Exchange(m, addr) }) runTest("ExchangeContext", func(m *Msg, addr string, timeout time.Duration) (*Msg, time.Duration, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() return new(Client).ExchangeContext(ctx, m, addrstr) }) } func TestExchangeWithConn(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) conn, err := c.Dial(addrstr) if err != nil { t.Fatalf("failed to dial: %v", err) } r, _, err := c.ExchangeWithConn(m, conn) if err != nil { t.Fatalf("failed to exchange: %v", err) } if r == nil { t.Fatal("response is nil") } if r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) } } golang-github-miekg-dns-1.1.64/clientconfig.go000066400000000000000000000064451476742671700212370ustar00rootroot00000000000000package dns import ( "bufio" "io" "os" "strconv" "strings" ) // ClientConfig wraps the contents of the /etc/resolv.conf file. type ClientConfig struct { Servers []string // servers to use Search []string // suffixes to append to local name Port string // what port to use Ndots int // number of dots in name to trigger absolute lookup Timeout int // seconds before giving up on packet Attempts int // lost packets before giving up on server, not used in the package dns } // ClientConfigFromFile parses a resolv.conf(5) like file and returns // a *ClientConfig. func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) { file, err := os.Open(resolvconf) if err != nil { return nil, err } defer file.Close() return ClientConfigFromReader(file) } // ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) { c := new(ClientConfig) scanner := bufio.NewScanner(resolvconf) c.Servers = make([]string, 0) c.Search = make([]string, 0) c.Port = "53" c.Ndots = 1 c.Timeout = 5 c.Attempts = 2 for scanner.Scan() { if err := scanner.Err(); err != nil { return nil, err } line := scanner.Text() f := strings.Fields(line) if len(f) < 1 { continue } switch f[0] { case "nameserver": // add one name server if len(f) > 1 { // One more check: make sure server name is // just an IP address. Otherwise we need DNS // to look it up. name := f[1] c.Servers = append(c.Servers, name) } case "domain": // set search path to just this domain if len(f) > 1 { c.Search = make([]string, 1) c.Search[0] = f[1] } else { c.Search = make([]string, 0) } case "search": // set search path to given servers c.Search = cloneSlice(f[1:]) case "options": // magic options for _, s := range f[1:] { switch { case len(s) >= 6 && s[:6] == "ndots:": n, _ := strconv.Atoi(s[6:]) if n < 0 { n = 0 } else if n > 15 { n = 15 } c.Ndots = n case len(s) >= 8 && s[:8] == "timeout:": n, _ := strconv.Atoi(s[8:]) if n < 1 { n = 1 } c.Timeout = n case len(s) >= 9 && s[:9] == "attempts:": n, _ := strconv.Atoi(s[9:]) if n < 1 { n = 1 } c.Attempts = n case s == "rotate": /* not imp */ } } } } return c, nil } // NameList returns all of the names that should be queried based on the // config. It is based off of go's net/dns name building, but it does not // check the length of the resulting names. func (c *ClientConfig) NameList(name string) []string { // if this domain is already fully qualified, no append needed. if IsFqdn(name) { return []string{name} } // Check to see if the name has more labels than Ndots. Do this before making // the domain fully qualified. hasNdots := CountLabel(name) > c.Ndots // Make the domain fully qualified. name = Fqdn(name) // Make a list of names based off search. names := []string{} // If name has enough dots, try that first. if hasNdots { names = append(names, name) } for _, s := range c.Search { names = append(names, Fqdn(name+s)) } // If we didn't have enough dots, try after suffixes. if !hasNdots { names = append(names, name) } return names } golang-github-miekg-dns-1.1.64/clientconfig_test.go000066400000000000000000000110271476742671700222660ustar00rootroot00000000000000package dns import ( "os" "path/filepath" "strings" "testing" ) const normal string = ` # Comment domain somedomain.com nameserver 10.28.10.2 nameserver 11.28.10.1 ` const missingNewline string = ` domain somedomain.com nameserver 10.28.10.2 nameserver 11.28.10.1` // <- NOTE: NO newline. func testConfig(t *testing.T, data string) { cc, err := ClientConfigFromReader(strings.NewReader(data)) if err != nil { t.Errorf("error parsing resolv.conf: %v", err) } if l := len(cc.Servers); l != 2 { t.Errorf("incorrect number of nameservers detected: %d", l) } if l := len(cc.Search); l != 1 { t.Errorf("domain directive not parsed correctly: %v", cc.Search) } else { if cc.Search[0] != "somedomain.com" { t.Errorf("domain is unexpected: %v", cc.Search[0]) } } } func TestNameserver(t *testing.T) { testConfig(t, normal) } func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) } func TestNdots(t *testing.T) { ndotsVariants := map[string]int{ "options ndots:0": 0, "options ndots:1": 1, "options ndots:15": 15, "options ndots:16": 15, "options ndots:-1": 0, "": 1, } for data := range ndotsVariants { cc, err := ClientConfigFromReader(strings.NewReader(data)) if err != nil { t.Errorf("error parsing resolv.conf: %v", err) } if cc.Ndots != ndotsVariants[data] { t.Errorf("Ndots not properly parsed: (Expected: %d / Was: %d)", ndotsVariants[data], cc.Ndots) } } } func TestClientConfigFromReaderAttempts(t *testing.T) { testCases := []struct { data string expected int }{ {data: "options attempts:0", expected: 1}, {data: "options attempts:1", expected: 1}, {data: "options attempts:15", expected: 15}, {data: "options attempts:16", expected: 16}, {data: "options attempts:-1", expected: 1}, {data: "options attempt:", expected: 2}, } for _, test := range testCases { test := test t.Run(strings.Replace(test.data, ":", " ", -1), func(t *testing.T) { t.Parallel() cc, err := ClientConfigFromReader(strings.NewReader(test.data)) if err != nil { t.Errorf("error parsing resolv.conf: %v", err) } if cc.Attempts != test.expected { t.Errorf("A attempts not properly parsed: (Expected: %d / Was: %d)", test.expected, cc.Attempts) } }) } } func TestReadFromFile(t *testing.T) { tempDir := t.TempDir() path := filepath.Join(tempDir, "resolv.conf") if err := os.WriteFile(path, []byte(normal), 0o644); err != nil { t.Fatalf("writeFile: %v", err) } cc, err := ClientConfigFromFile(path) if err != nil { t.Errorf("error parsing resolv.conf: %v", err) } if l := len(cc.Servers); l != 2 { t.Errorf("incorrect number of nameservers detected: %d", l) } if l := len(cc.Search); l != 1 { t.Errorf("domain directive not parsed correctly: %v", cc.Search) } else { if cc.Search[0] != "somedomain.com" { t.Errorf("domain is unexpected: %v", cc.Search[0]) } } } func TestNameListNdots1(t *testing.T) { cfg := ClientConfig{ Ndots: 1, } // fqdn should be only result returned names := cfg.NameList("miek.nl.") if len(names) != 1 { t.Errorf("NameList returned != 1 names: %v", names) } else if names[0] != "miek.nl." { t.Errorf("NameList didn't return sent fqdn domain: %v", names[0]) } cfg.Search = []string{ "test", } // Sent domain has NDots and search names = cfg.NameList("miek.nl") if len(names) != 2 { t.Errorf("NameList returned != 2 names: %v", names) } else if names[0] != "miek.nl." { t.Errorf("NameList didn't return sent domain first: %v", names[0]) } else if names[1] != "miek.nl.test." { t.Errorf("NameList didn't return search last: %v", names[1]) } } func TestNameListNdots2(t *testing.T) { cfg := ClientConfig{ Ndots: 2, } // Sent domain has less than NDots and search cfg.Search = []string{ "test", } names := cfg.NameList("miek.nl") if len(names) != 2 { t.Errorf("NameList returned != 2 names: %v", names) } else if names[0] != "miek.nl.test." { t.Errorf("NameList didn't return search first: %v", names[0]) } else if names[1] != "miek.nl." { t.Errorf("NameList didn't return sent domain last: %v", names[1]) } } func TestNameListNdots0(t *testing.T) { cfg := ClientConfig{ Ndots: 0, } cfg.Search = []string{ "test", } // Sent domain has less than NDots and search names := cfg.NameList("miek") if len(names) != 2 { t.Errorf("NameList returned != 2 names: %v", names) } else if names[0] != "miek." { t.Errorf("NameList didn't return search first: %v", names[0]) } else if names[1] != "miek.test." { t.Errorf("NameList didn't return sent domain last: %v", names[1]) } } golang-github-miekg-dns-1.1.64/dane.go000066400000000000000000000017451476742671700175000ustar00rootroot00000000000000package dns import ( "crypto/sha256" "crypto/sha512" "crypto/x509" "encoding/hex" "errors" ) // CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records. func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) { switch matchingType { case 0: switch selector { case 0: return hex.EncodeToString(cert.Raw), nil case 1: return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil } case 1: h := sha256.New() switch selector { case 0: h.Write(cert.Raw) return hex.EncodeToString(h.Sum(nil)), nil case 1: h.Write(cert.RawSubjectPublicKeyInfo) return hex.EncodeToString(h.Sum(nil)), nil } case 2: h := sha512.New() switch selector { case 0: h.Write(cert.Raw) return hex.EncodeToString(h.Sum(nil)), nil case 1: h.Write(cert.RawSubjectPublicKeyInfo) return hex.EncodeToString(h.Sum(nil)), nil } } return "", errors.New("dns: bad MatchingType or Selector") } golang-github-miekg-dns-1.1.64/defaults.go000066400000000000000000000252601476742671700203760ustar00rootroot00000000000000package dns import ( "errors" "net" "strconv" "strings" ) const hexDigit = "0123456789abcdef" // Everything is assumed in ClassINET. // SetReply creates a reply message from a request message. func (dns *Msg) SetReply(request *Msg) *Msg { dns.Id = request.Id dns.Response = true dns.Opcode = request.Opcode if dns.Opcode == OpcodeQuery { dns.RecursionDesired = request.RecursionDesired // Copy rd bit dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit } dns.Rcode = RcodeSuccess if len(request.Question) > 0 { dns.Question = []Question{request.Question[0]} } return dns } // SetQuestion creates a question message, it sets the Question // section, generates an Id and sets the RecursionDesired (RD) // bit to true. func (dns *Msg) SetQuestion(z string, t uint16) *Msg { dns.Id = Id() dns.RecursionDesired = true dns.Question = make([]Question, 1) dns.Question[0] = Question{z, t, ClassINET} return dns } // SetNotify creates a notify message, it sets the Question // section, generates an Id and sets the Authoritative (AA) // bit to true. func (dns *Msg) SetNotify(z string) *Msg { dns.Opcode = OpcodeNotify dns.Authoritative = true dns.Id = Id() dns.Question = make([]Question, 1) dns.Question[0] = Question{z, TypeSOA, ClassINET} return dns } // SetRcode creates an error message suitable for the request. func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg { dns.SetReply(request) dns.Rcode = rcode return dns } // SetRcodeFormatError creates a message with FormError set. func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg { dns.Rcode = RcodeFormatError dns.Opcode = OpcodeQuery dns.Response = true dns.Authoritative = false dns.Id = request.Id return dns } // SetUpdate makes the message a dynamic update message. It // sets the ZONE section to: z, TypeSOA, ClassINET. func (dns *Msg) SetUpdate(z string) *Msg { dns.Id = Id() dns.Response = false dns.Opcode = OpcodeUpdate dns.Compress = false // BIND9 cannot handle compression dns.Question = make([]Question, 1) dns.Question[0] = Question{z, TypeSOA, ClassINET} return dns } // SetIxfr creates message for requesting an IXFR. func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg { dns.Id = Id() dns.Question = make([]Question, 1) dns.Ns = make([]RR, 1) s := new(SOA) s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0} s.Serial = serial s.Ns = ns s.Mbox = mbox dns.Question[0] = Question{z, TypeIXFR, ClassINET} dns.Ns[0] = s return dns } // SetAxfr creates message for requesting an AXFR. func (dns *Msg) SetAxfr(z string) *Msg { dns.Id = Id() dns.Question = make([]Question, 1) dns.Question[0] = Question{z, TypeAXFR, ClassINET} return dns } // SetTsig appends a TSIG RR to the message. // This is only a skeleton TSIG RR that is added as the last RR in the // additional section. The TSIG is calculated when the message is being send. func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg { t := new(TSIG) t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} t.Algorithm = algo t.Fudge = fudge t.TimeSigned = uint64(timesigned) t.OrigId = dns.Id dns.Extra = append(dns.Extra, t) return dns } // SetEdns0 appends a EDNS0 OPT RR to the message. // TSIG should always the last RR in a message. func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg { e := new(OPT) e.Hdr.Name = "." e.Hdr.Rrtype = TypeOPT e.SetUDPSize(udpsize) if do { e.SetDo() } dns.Extra = append(dns.Extra, e) return dns } // IsTsig checks if the message has a TSIG record as the last record // in the additional section. It returns the TSIG record found or nil. func (dns *Msg) IsTsig() *TSIG { if len(dns.Extra) > 0 { if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG { return dns.Extra[len(dns.Extra)-1].(*TSIG) } } return nil } // IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0 // record in the additional section will do. It returns the OPT record // found or nil. func (dns *Msg) IsEdns0() *OPT { // RFC 6891, Section 6.1.1 allows the OPT record to appear // anywhere in the additional record section, but it's usually at // the end so start there. for i := len(dns.Extra) - 1; i >= 0; i-- { if dns.Extra[i].Header().Rrtype == TypeOPT { return dns.Extra[i].(*OPT) } } return nil } // popEdns0 is like IsEdns0, but it removes the record from the message. func (dns *Msg) popEdns0() *OPT { // RFC 6891, Section 6.1.1 allows the OPT record to appear // anywhere in the additional record section, but it's usually at // the end so start there. for i := len(dns.Extra) - 1; i >= 0; i-- { if dns.Extra[i].Header().Rrtype == TypeOPT { opt := dns.Extra[i].(*OPT) dns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...) return opt } } return nil } // IsDomainName checks if s is a valid domain name, it returns the number of // labels and true, when a domain name is valid. Note that non fully qualified // domain name is considered valid, in this case the last label is counted in // the number of labels. When false is returned the number of labels is not // defined. Also note that this function is extremely liberal; almost any // string is a valid domain name as the DNS is 8 bit protocol. It checks if each // label fits in 63 characters and that the entire name will fit into the 255 // octet wire format limit. func IsDomainName(s string) (labels int, ok bool) { // XXX: The logic in this function was copied from packDomainName and // should be kept in sync with that function. const lenmsg = 256 if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata. return 0, false } s = Fqdn(s) // Each dot ends a segment of the name. Except for escaped dots (\.), which // are normal dots. var ( off int begin int wasDot bool escape bool ) for i := 0; i < len(s); i++ { switch s[i] { case '\\': escape = !escape if off+1 > lenmsg { return labels, false } // check for \DDD if isDDD(s[i+1:]) { i += 3 begin += 3 } else { i++ begin++ } wasDot = false case '.': escape = false if i == 0 && len(s) > 1 { // leading dots are not legal except for the root zone return labels, false } if wasDot { // two dots back to back is not legal return labels, false } wasDot = true labelLen := i - begin if labelLen >= 1<<6 { // top two bits of length must be clear return labels, false } // off can already (we're in a loop) be bigger than lenmsg // this happens when a name isn't fully qualified off += 1 + labelLen if off > lenmsg { return labels, false } labels++ begin = i + 1 default: escape = false wasDot = false } } if escape { return labels, false } return labels, true } // IsSubDomain checks if child is indeed a child of the parent. If child and parent // are the same domain true is returned as well. func IsSubDomain(parent, child string) bool { // Entire child is contained in parent return CompareDomainName(parent, child) == CountLabel(parent) } // IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet. // The checking is performed on the binary payload. func IsMsg(buf []byte) error { // Header if len(buf) < headerSize { return errors.New("dns: bad message header") } // Header: Opcode // TODO(miek): more checks here, e.g. check all header bits. return nil } // IsFqdn checks if a domain name is fully qualified. func IsFqdn(s string) bool { // Check for (and remove) a trailing dot, returning if there isn't one. if s == "" || s[len(s)-1] != '.' { return false } s = s[:len(s)-1] // If we don't have an escape sequence before the final dot, we know it's // fully qualified and can return here. if s == "" || s[len(s)-1] != '\\' { return true } // Otherwise we have to check if the dot is escaped or not by checking if // there are an odd or even number of escape sequences before the dot. i := strings.LastIndexFunc(s, func(r rune) bool { return r != '\\' }) return (len(s)-i)%2 != 0 } // IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181. // This means the RRs need to have the same type, name, and class. func IsRRset(rrset []RR) bool { if len(rrset) == 0 { return false } baseH := rrset[0].Header() for _, rr := range rrset[1:] { curH := rr.Header() if curH.Rrtype != baseH.Rrtype || curH.Class != baseH.Class || curH.Name != baseH.Name { // Mismatch between the records, so this is not a valid rrset for // signing/verifying return false } } return true } // Fqdn return the fully qualified domain name from s. // If s is already fully qualified, it behaves as the identity function. func Fqdn(s string) string { if IsFqdn(s) { return s } return s + "." } // CanonicalName returns the domain name in canonical form. A name in canonical // form is lowercase and fully qualified. Only US-ASCII letters are affected. See // Section 6.2 in RFC 4034. func CanonicalName(s string) string { return strings.Map(func(r rune) rune { if r >= 'A' && r <= 'Z' { r += 'a' - 'A' } return r }, Fqdn(s)) } // Copied from the official Go code. // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP // address suitable for reverse DNS (PTR) record lookups or an error if it fails // to parse the IP address. func ReverseAddr(addr string) (arpa string, err error) { ip := net.ParseIP(addr) if ip == nil { return "", &Error{err: "unrecognized address: " + addr} } if v4 := ip.To4(); v4 != nil { buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa.")) // Add it, in reverse, to the buffer for i := len(v4) - 1; i >= 0; i-- { buf = strconv.AppendInt(buf, int64(v4[i]), 10) buf = append(buf, '.') } // Append "in-addr.arpa." and return (buf already has the final .) buf = append(buf, "in-addr.arpa."...) return string(buf), nil } // Must be IPv6 buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa.")) // Add it, in reverse, to the buffer for i := len(ip) - 1; i >= 0; i-- { v := ip[i] buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.') } // Append "ip6.arpa." and return (buf already has the final .) buf = append(buf, "ip6.arpa."...) return string(buf), nil } // String returns the string representation for the type t. func (t Type) String() string { if t1, ok := TypeToString[uint16(t)]; ok { return t1 } return "TYPE" + strconv.Itoa(int(t)) } // String returns the string representation for the class c. func (c Class) String() string { if s, ok := ClassToString[uint16(c)]; ok { // Only emit mnemonics when they are unambiguous, specially ANY is in both. if _, ok := StringToType[s]; !ok { return s } } return "CLASS" + strconv.Itoa(int(c)) } // String returns the string representation for the name n. func (n Name) String() string { return sprintName(string(n)) } golang-github-miekg-dns-1.1.64/dns.go000066400000000000000000000105411476742671700173470ustar00rootroot00000000000000package dns import ( "encoding/hex" "strconv" ) const ( year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. defaultTtl = 3600 // Default internal TTL. // DefaultMsgSize is the standard default for messages larger than 512 bytes. DefaultMsgSize = 4096 // MinMsgSize is the minimal size of a DNS packet. MinMsgSize = 512 // MaxMsgSize is the largest possible DNS packet. MaxMsgSize = 65535 ) // Error represents a DNS error. type Error struct{ err string } func (e *Error) Error() string { if e == nil { return "dns: " } return "dns: " + e.err } // An RR represents a resource record. type RR interface { // Header returns the header of an resource record. The header contains // everything up to the rdata. Header() *RR_Header // String returns the text representation of the resource record. String() string // copy returns a copy of the RR copy() RR // len returns the length (in octets) of the compressed or uncompressed RR in wire format. // // If compression is nil, the uncompressed size will be returned, otherwise the compressed // size will be returned and domain names will be added to the map for future compression. len(off int, compression map[string]struct{}) int // pack packs the records RDATA into wire format. The header will // already have been packed into msg. pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) // unpack unpacks an RR from wire format. // // This will only be called on a new and empty RR type with only the header populated. It // will only be called if the record's RDATA is non-empty. unpack(msg []byte, off int) (off1 int, err error) // parse parses an RR from zone file format. // // This will only be called on a new and empty RR type with only the header populated. parse(c *zlexer, origin string) *ParseError // isDuplicate returns whether the two RRs are duplicates. isDuplicate(r2 RR) bool } // RR_Header is the header all DNS resource records share. type RR_Header struct { Name string `dns:"cdomain-name"` Rrtype uint16 Class uint16 Ttl uint32 Rdlength uint16 // Length of data after header. } // Header returns itself. This is here to make RR_Header implements the RR interface. func (h *RR_Header) Header() *RR_Header { return h } // Just to implement the RR interface. func (h *RR_Header) copy() RR { return nil } func (h *RR_Header) String() string { var s string if h.Rrtype == TypeOPT { s = ";" // and maybe other things } s += sprintName(h.Name) + "\t" s += strconv.FormatInt(int64(h.Ttl), 10) + "\t" s += Class(h.Class).String() + "\t" s += Type(h.Rrtype).String() + "\t" return s } func (h *RR_Header) len(off int, compression map[string]struct{}) int { l := domainNameLen(h.Name, off, compression, true) l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2) return l } func (h *RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { // RR_Header has no RDATA to pack. return off, nil } func (h *RR_Header) unpack(msg []byte, off int) (int, error) { panic("dns: internal error: unpack should never be called on RR_Header") } func (h *RR_Header) parse(c *zlexer, origin string) *ParseError { panic("dns: internal error: parse should never be called on RR_Header") } // ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597. func (rr *RFC3597) ToRFC3597(r RR) error { buf := make([]byte, Len(r)) headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false) if err != nil { return err } buf = buf[:off] *rr = RFC3597{Hdr: *r.Header()} rr.Hdr.Rdlength = uint16(off - headerEnd) if noRdata(rr.Hdr) { return nil } _, err = rr.unpack(buf, headerEnd) return err } // fromRFC3597 converts an unknown RR representation from RFC 3597 to the known RR type. func (rr *RFC3597) fromRFC3597(r RR) error { hdr := r.Header() *hdr = rr.Hdr // Can't overflow uint16 as the length of Rdata is validated in (*RFC3597).parse. // We can only get here when rr was constructed with that method. hdr.Rdlength = uint16(hex.DecodedLen(len(rr.Rdata))) if noRdata(*hdr) { // Dynamic update. return nil } // rr.pack requires an extra allocation and a copy so we just decode Rdata // manually, it's simpler anyway. msg, err := hex.DecodeString(rr.Rdata) if err != nil { return err } _, err = r.unpack(msg, 0) return err } golang-github-miekg-dns-1.1.64/dns_bench_test.go000066400000000000000000000226571476742671700215600ustar00rootroot00000000000000package dns import ( "fmt" "net" "testing" ) func BenchmarkMsgLength(b *testing.B) { b.StopTimer() makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) b.StartTimer() for i := 0; i < b.N; i++ { msg.Len() } } func BenchmarkMsgLengthNoCompression(b *testing.B) { b.StopTimer() makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) b.StartTimer() for i := 0; i < b.N; i++ { msg.Len() } } func BenchmarkMsgLengthPack(b *testing.B) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = msg.Pack() } } func BenchmarkMsgLengthMassive(b *testing.B) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } const name1 = "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) answer := []RR{rrMx, rrMx} for i := 0; i < 128; i++ { rrA := testRR(fmt.Sprintf("example%03d.something%03delse.org. 2311 IN A 127.0.0.1", i/32, i%32)) answer = append(answer, rrA) } answer = append(answer, rrMx, rrMx) msg := makeMsg(name1, answer, nil, nil) b.ResetTimer() for i := 0; i < b.N; i++ { msg.Len() } } func BenchmarkMsgLengthOnlyQuestion(b *testing.B) { msg := new(Msg) msg.SetQuestion(Fqdn("12345678901234567890123456789012345.12345678.123."), TypeANY) msg.Compress = true b.ResetTimer() for i := 0; i < b.N; i++ { msg.Len() } } func BenchmarkMsgLengthEscapedName(b *testing.B) { msg := new(Msg) msg.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) b.ResetTimer() for i := 0; i < b.N; i++ { msg.Len() } } func BenchmarkPackDomainName(b *testing.B) { name1 := "12345678901234567890123456789012345.12345678.123." buf := make([]byte, len(name1)+1) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = PackDomainName(name1, buf, 0, nil, false) } } func BenchmarkUnpackDomainName(b *testing.B) { name1 := "12345678901234567890123456789012345.12345678.123." buf := make([]byte, len(name1)+1) _, _ = PackDomainName(name1, buf, 0, nil, false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackDomainName(buf, 0) } } func BenchmarkUnpackDomainNameUnprintable(b *testing.B) { name1 := "\x02\x02\x02\x025\x02\x02\x02\x02.12345678.123." buf := make([]byte, len(name1)+1) _, _ = PackDomainName(name1, buf, 0, nil, false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackDomainName(buf, 0) } } func BenchmarkUnpackDomainNameLongest(b *testing.B) { buf := make([]byte, len(longestDomain)+1) n, err := PackDomainName(longestDomain, buf, 0, nil, false) if err != nil { b.Fatal(err) } if n != maxDomainNameWireOctets { b.Fatalf("name wrong size in wire format, expected %d, got %d", maxDomainNameWireOctets, n) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackDomainName(buf, 0) } } func BenchmarkUnpackDomainNameLongestUnprintable(b *testing.B) { buf := make([]byte, len(longestUnprintableDomain)+1) n, err := PackDomainName(longestUnprintableDomain, buf, 0, nil, false) if err != nil { b.Fatal(err) } if n != maxDomainNameWireOctets { b.Fatalf("name wrong size in wire format, expected %d, got %d", maxDomainNameWireOctets, n) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackDomainName(buf, 0) } } func BenchmarkCopy(b *testing.B) { b.ReportAllocs() m := new(Msg) m.SetQuestion("miek.nl.", TypeA) rr := testRR("miek.nl. 2311 IN A 127.0.0.1") m.Answer = []RR{rr} rr = testRR("miek.nl. 2311 IN NS 127.0.0.1") m.Ns = []RR{rr} rr = testRR("miek.nl. 2311 IN A 127.0.0.1") m.Extra = []RR{rr} b.ResetTimer() for i := 0; i < b.N; i++ { m.Copy() } } func BenchmarkPackA(b *testing.B) { a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)} buf := make([]byte, Len(a)) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = PackRR(a, buf, 0, nil, false) } } func BenchmarkUnpackA(b *testing.B) { a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)} buf := make([]byte, Len(a)) PackRR(a, buf, 0, nil, false) a = nil b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackRR(buf, 0) } } func BenchmarkPackMX(b *testing.B) { m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."} buf := make([]byte, Len(m)) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = PackRR(m, buf, 0, nil, false) } } func BenchmarkUnpackMX(b *testing.B) { m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."} buf := make([]byte, Len(m)) PackRR(m, buf, 0, nil, false) m = nil b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackRR(buf, 0) } } func BenchmarkPackAAAAA(b *testing.B) { aaaa := testRR(". IN AAAA ::1") buf := make([]byte, Len(aaaa)) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = PackRR(aaaa, buf, 0, nil, false) } } func BenchmarkUnpackAAAA(b *testing.B) { aaaa := testRR(". IN AAAA ::1") buf := make([]byte, Len(aaaa)) PackRR(aaaa, buf, 0, nil, false) aaaa = nil b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = UnpackRR(buf, 0) } } func BenchmarkPackMsg(b *testing.B) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) buf := make([]byte, 512) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = msg.PackBuffer(buf) } } func BenchmarkPackMsgMassive(b *testing.B) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } const name1 = "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) answer := []RR{rrMx, rrMx} for i := 0; i < 128; i++ { rrA := testRR(fmt.Sprintf("example%03d.something%03delse.org. 2311 IN A 127.0.0.1", i/32, i%32)) answer = append(answer, rrA) } answer = append(answer, rrMx, rrMx) msg := makeMsg(name1, answer, nil, nil) buf := make([]byte, 512) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = msg.PackBuffer(buf) } } func BenchmarkPackMsgOnlyQuestion(b *testing.B) { msg := new(Msg) msg.SetQuestion(Fqdn("12345678901234567890123456789012345.12345678.123."), TypeANY) msg.Compress = true buf := make([]byte, 512) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = msg.PackBuffer(buf) } } func BenchmarkUnpackMsg(b *testing.B) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) msgBuf, _ := msg.Pack() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _ = msg.Unpack(msgBuf) } } func BenchmarkIdGeneration(b *testing.B) { for i := 0; i < b.N; i++ { _ = id() } } func BenchmarkReverseAddr(b *testing.B) { b.Run("IP4", func(b *testing.B) { for n := 0; n < b.N; n++ { addr, err := ReverseAddr("192.0.2.1") if err != nil { b.Fatal(err) } if expect := "1.2.0.192.in-addr.arpa."; addr != expect { b.Fatalf("invalid reverse address, expected %q, got %q", expect, addr) } } }) b.Run("IP6", func(b *testing.B) { for n := 0; n < b.N; n++ { addr, err := ReverseAddr("2001:db8::68") if err != nil { b.Fatal(err) } if 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 { b.Fatalf("invalid reverse address, expected %q, got %q", expect, addr) } } }) } golang-github-miekg-dns-1.1.64/dns_test.go000066400000000000000000000253761476742671700204220ustar00rootroot00000000000000package dns import ( "bytes" "encoding/hex" "net" "testing" ) func TestPackUnpack(t *testing.T) { out := new(Msg) out.Answer = make([]RR, 1) key := &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1} key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600} key.PublicKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" out.Answer[0] = key msg, err := out.Pack() if err != nil { t.Error("failed to pack msg with DNSKEY") } in := new(Msg) if in.Unpack(msg) != nil { t.Error("failed to unpack msg with DNSKEY") } sig := &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2, OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.", Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"} sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600} out.Answer[0] = sig msg, err = out.Pack() if err != nil { t.Error("failed to pack msg with RRSIG") } if in.Unpack(msg) != nil { t.Error("failed to unpack msg with RRSIG") } } func TestPackUnpack2(t *testing.T) { m := new(Msg) m.Extra = make([]RR, 1) m.Answer = make([]RR, 1) dom := "miek.nl." rr := new(A) rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0} rr.A = net.IPv4(127, 0, 0, 1) x := new(TXT) x.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0} x.Txt = []string{"heelalaollo"} m.Extra[0] = x m.Answer[0] = rr _, err := m.Pack() if err != nil { t.Error("Packing failed: ", err) return } } func TestPackUnpack3(t *testing.T) { m := new(Msg) m.Extra = make([]RR, 2) m.Answer = make([]RR, 1) dom := "miek.nl." rr := new(A) rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0} rr.A = net.IPv4(127, 0, 0, 1) x1 := new(TXT) x1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0} x1.Txt = []string{} x2 := new(TXT) x2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0} x2.Txt = []string{"heelalaollo"} m.Extra[0] = x1 m.Extra[1] = x2 m.Answer[0] = rr b, err := m.Pack() if err != nil { t.Error("packing failed: ", err) return } var unpackMsg Msg err = unpackMsg.Unpack(b) if err != nil { t.Error("unpacking failed") return } } func TestBailiwick(t *testing.T) { yes := map[string]string{ "miek1.nl": "miek1.nl", "miek.nl": "ns.miek.nl", ".": "miek.nl", } for parent, child := range yes { if !IsSubDomain(parent, child) { t.Errorf("%s should be child of %s", child, parent) t.Errorf("comparelabels %d", CompareDomainName(parent, child)) t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child)) } } no := map[string]string{ "www.miek.nl": "ns.miek.nl", "m\\.iek.nl": "ns.miek.nl", "w\\.iek.nl": "w.iek.nl", "p\\\\.iek.nl": "ns.p.iek.nl", // p\\.iek.nl , literal \ in domain name "miek.nl": ".", } for parent, child := range no { if IsSubDomain(parent, child) { t.Errorf("%s should not be child of %s", child, parent) t.Errorf("comparelabels %d", CompareDomainName(parent, child)) t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child)) } } } func TestPackNAPTR(t *testing.T) { for _, n := range []string{ `apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`, `apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`, `apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`, } { rr := testRR(n) msg := make([]byte, Len(rr)) if off, err := PackRR(rr, msg, 0, nil, false); err != nil { t.Errorf("packing failed: %v", err) t.Errorf("length %d, need more than %d", Len(rr), off) } } } func TestToRFC3597(t *testing.T) { a := testRR("miek.nl. IN A 10.0.1.1") x := new(RFC3597) x.ToRFC3597(a) if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` { t.Errorf("string mismatch, got: %s", x) } b := testRR("miek.nl. IN MX 10 mx.miek.nl.") x.ToRFC3597(b) if x.String() != `miek.nl. 3600 CLASS1 TYPE15 \# 14 000a026d78046d69656b026e6c00` { t.Errorf("string mismatch, got: %s", x) } } func TestNoRdataPack(t *testing.T) { data := make([]byte, 1024) for typ, fn := range TypeToRR { r := fn() *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16} _, err := PackRR(r, data, 0, nil, false) if err != nil { t.Errorf("failed to pack RR with zero rdata: %s: %v", TypeToString[typ], err) } } } func TestNoRdataUnpack(t *testing.T) { data := make([]byte, 1024) for typ, fn := range TypeToRR { if typ == TypeSOA || typ == TypeTSIG || typ == TypeTKEY { // SOA, TSIG will not be seen (like this) in dyn. updates? // TKEY requires length fields to be present for the Key and OtherData fields continue } r := fn() *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16} off, err := PackRR(r, data, 0, nil, false) if err != nil { // Should always works, TestNoDataPack should have caught this t.Errorf("failed to pack RR: %v", err) continue } if _, _, err := UnpackRR(data[:off], 0); err != nil { t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err) } } } func TestRdataOverflow(t *testing.T) { rr := new(RFC3597) rr.Hdr.Name = "." rr.Hdr.Class = ClassINET rr.Hdr.Rrtype = 65280 rr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF)) buf := make([]byte, 0xFFFF*2) if _, err := PackRR(rr, buf, 0, nil, false); err != nil { t.Fatalf("maximum size rrdata pack failed: %v", err) } rr.Rdata += "00" if _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata { t.Fatalf("oversize rrdata pack didn't return ErrRdata - instead: %v", err) } } func TestCopy(t *testing.T) { rr := testRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL rr1 := Copy(rr) if rr.String() != rr1.String() { t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String()) } } func TestMsgCopy(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeA) rr := testRR("miek.nl. 2311 IN A 127.0.0.1") m.Answer = []RR{rr} rr = testRR("miek.nl. 2311 IN NS 127.0.0.1") m.Ns = []RR{rr} m1 := m.Copy() if m.String() != m1.String() { t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String()) } m1.Answer[0] = testRR("somethingelse.nl. 2311 IN A 127.0.0.1") if m.String() == m1.String() { t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String()) } rr = testRR("miek.nl. 2311 IN A 127.0.0.2") m1.Answer = append(m1.Answer, rr) if m1.Ns[0].String() == m1.Answer[1].String() { t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String()) } } func TestMsgPackBuffer(t *testing.T) { var testMessages = []string{ // news.ycombinator.com.in.escapemg.com. IN A, response "586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10", // news.ycombinator.com.in.escapemg.com. IN A, question "586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001", "398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001", } for i, hexData := range testMessages { // we won't fail the decoding of the hex input, _ := hex.DecodeString(hexData) m := new(Msg) if err := m.Unpack(input); err != nil { t.Errorf("packet %d failed to unpack", i) continue } } } // Make sure we can decode a TKEY packet from the string, modify the RR, and then pack it again. func TestTKEY(t *testing.T) { // An example TKEY RR captured. There is no known accepted standard text format for a TKEY // record so we do this from a hex string instead of from a text readable string. tkeyStr := "0737362d6d732d370932322d3332633233332463303439663961662d633065612d313165372d363839362d6463333937396666656666640000f900ff0000000000d2086773732d747369670059fd01f359fe53730003000000b8a181b53081b2a0030a0100a10b06092a864882f712010202a2819d04819a60819706092a864886f71201020202006f8187308184a003020105a10302010fa2783076a003020112a26f046db29b1b1d2625da3b20b49dafef930dd1e9aad335e1c5f45dcd95e0005d67a1100f3e573d70506659dbed064553f1ab890f68f65ae10def0dad5b423b39f240ebe666f2886c5fe03819692d29182bbed87b83e1f9d16b7334ec16a3c4fc5ad4a990088e0be43f0c6957916f5fe60000" tkeyBytes, err := hex.DecodeString(tkeyStr) if err != nil { t.Fatal("unable to decode TKEY string ", err) } // Decode the RR rr, tkeyLen, unPackErr := UnpackRR(tkeyBytes, 0) if unPackErr != nil { t.Fatal("unable to decode TKEY RR", unPackErr) } // Make sure it's a TKEY record if rr.Header().Rrtype != TypeTKEY { t.Fatal("Unable to decode TKEY") } // Make sure we get back the same length if Len(rr) != len(tkeyBytes) { t.Fatalf("Lengths don't match %d != %d", Len(rr), len(tkeyBytes)) } // make space for it with some fudge room msg := make([]byte, tkeyLen+1000) offset, packErr := PackRR(rr, msg, 0, nil, false) if packErr != nil { t.Fatal("unable to pack TKEY RR", packErr) } if offset != len(tkeyBytes) { t.Fatalf("mismatched TKEY RR size %d != %d", len(tkeyBytes), offset) } if !bytes.Equal(tkeyBytes, msg[0:offset]) { t.Fatal("mismatched TKEY data after rewriting bytes") } // Now add some bytes to this and make sure we can encode OtherData properly tkey := rr.(*TKEY) tkey.OtherData = "abcd" tkey.OtherLen = 2 offset, packErr = PackRR(tkey, msg, 0, nil, false) if packErr != nil { t.Fatal("unable to pack TKEY RR after modification", packErr) } if offset != len(tkeyBytes)+2 { t.Fatalf("mismatched TKEY RR size %d != %d", offset, len(tkeyBytes)+2) } // Make sure we can parse our string output tkey.Hdr.Class = ClassINET // https://github.com/miekg/dns/issues/577 _, newError := NewRR(tkey.String()) if newError != nil { t.Fatalf("unable to parse TKEY string: %s", newError) } } var ( sinkBool bool sinkString string ) func BenchmarkIsFQDN(b *testing.B) { b.Run("no_dot", func(b *testing.B) { var r bool for n := 0; n < b.N; n++ { r = IsFqdn("www.google.com") } sinkBool = r }) b.Run("unescaped", func(b *testing.B) { var r bool for n := 0; n < b.N; n++ { r = IsFqdn("www.google.com.") } sinkBool = r }) b.Run("escaped", func(b *testing.B) { var r bool for n := 0; n < b.N; n++ { r = IsFqdn(`www.google.com\\\\\\\\.`) } sinkBool = r }) } func BenchmarkFQDN(b *testing.B) { b.Run("is_fqdn", func(b *testing.B) { var r string for n := 0; n < b.N; n++ { r = Fqdn("www.google.com.") } sinkString = r }) b.Run("not_fqdn", func(b *testing.B) { var r string for n := 0; n < b.N; n++ { r = Fqdn("www.google.com") } sinkString = r }) } golang-github-miekg-dns-1.1.64/dnssec.go000066400000000000000000000456151476742671700200540ustar00rootroot00000000000000package dns import ( "bytes" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" _ "crypto/sha1" // need its init function _ "crypto/sha256" // need its init function _ "crypto/sha512" // need its init function "encoding/asn1" "encoding/binary" "encoding/hex" "math/big" "sort" "strings" "time" ) // DNSSEC encryption algorithm codes. const ( _ uint8 = iota RSAMD5 DH DSA _ // Skip 4, RFC 6725, section 2.1 RSASHA1 DSANSEC3SHA1 RSASHA1NSEC3SHA1 RSASHA256 _ // Skip 9, RFC 6725, section 2.1 RSASHA512 _ // Skip 11, RFC 6725, section 2.1 ECCGOST ECDSAP256SHA256 ECDSAP384SHA384 ED25519 ED448 INDIRECT uint8 = 252 PRIVATEDNS uint8 = 253 // Private (experimental keys) PRIVATEOID uint8 = 254 ) // AlgorithmToString is a map of algorithm IDs to algorithm names. var AlgorithmToString = map[uint8]string{ RSAMD5: "RSAMD5", DH: "DH", DSA: "DSA", RSASHA1: "RSASHA1", DSANSEC3SHA1: "DSA-NSEC3-SHA1", RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1", RSASHA256: "RSASHA256", RSASHA512: "RSASHA512", ECCGOST: "ECC-GOST", ECDSAP256SHA256: "ECDSAP256SHA256", ECDSAP384SHA384: "ECDSAP384SHA384", ED25519: "ED25519", ED448: "ED448", INDIRECT: "INDIRECT", PRIVATEDNS: "PRIVATEDNS", PRIVATEOID: "PRIVATEOID", } // AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's. // For newer algorithm that do their own hashing (i.e. ED25519) the returned value // is 0, implying no (external) hashing should occur. The non-exported identityHash is then // used. var AlgorithmToHash = map[uint8]crypto.Hash{ RSAMD5: crypto.MD5, // Deprecated in RFC 6725 DSA: crypto.SHA1, RSASHA1: crypto.SHA1, RSASHA1NSEC3SHA1: crypto.SHA1, RSASHA256: crypto.SHA256, ECDSAP256SHA256: crypto.SHA256, ECDSAP384SHA384: crypto.SHA384, RSASHA512: crypto.SHA512, ED25519: 0, } // DNSSEC hashing algorithm codes. const ( _ uint8 = iota SHA1 // RFC 4034 SHA256 // RFC 4509 GOST94 // RFC 5933 SHA384 // Experimental SHA512 // Experimental ) // HashToString is a map of hash IDs to names. var HashToString = map[uint8]string{ SHA1: "SHA1", SHA256: "SHA256", GOST94: "GOST94", SHA384: "SHA384", SHA512: "SHA512", } // DNSKEY flag values. const ( SEP = 1 REVOKE = 1 << 7 ZONE = 1 << 8 ) // The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing. type rrsigWireFmt struct { TypeCovered uint16 Algorithm uint8 Labels uint8 OrigTtl uint32 Expiration uint32 Inception uint32 KeyTag uint16 SignerName string `dns:"domain-name"` /* No Signature */ } // Used for converting DNSKEY's rdata to wirefmt. type dnskeyWireFmt struct { Flags uint16 Protocol uint8 Algorithm uint8 PublicKey string `dns:"base64"` /* Nothing is left out */ } // KeyTag calculates the keytag (or key-id) of the DNSKEY. func (k *DNSKEY) KeyTag() uint16 { if k == nil { return 0 } var keytag int switch k.Algorithm { case RSAMD5: // This algorithm has been deprecated, but keep this key-tag calculation. // Look at the bottom two bytes of the modules, which the last item in the pubkey. // See https://www.rfc-editor.org/errata/eid193 . modulus, _ := fromBase64([]byte(k.PublicKey)) if len(modulus) > 1 { x := binary.BigEndian.Uint16(modulus[len(modulus)-3:]) keytag = int(x) } default: keywire := new(dnskeyWireFmt) keywire.Flags = k.Flags keywire.Protocol = k.Protocol keywire.Algorithm = k.Algorithm keywire.PublicKey = k.PublicKey wire := make([]byte, DefaultMsgSize) n, err := packKeyWire(keywire, wire) if err != nil { return 0 } wire = wire[:n] for i, v := range wire { if i&1 != 0 { keytag += int(v) // must be larger than uint32 } else { keytag += int(v) << 8 } } keytag += keytag >> 16 & 0xFFFF keytag &= 0xFFFF } return uint16(keytag) } // ToDS converts a DNSKEY record to a DS record. func (k *DNSKEY) ToDS(h uint8) *DS { if k == nil { return nil } ds := new(DS) ds.Hdr.Name = k.Hdr.Name ds.Hdr.Class = k.Hdr.Class ds.Hdr.Rrtype = TypeDS ds.Hdr.Ttl = k.Hdr.Ttl ds.Algorithm = k.Algorithm ds.DigestType = h ds.KeyTag = k.KeyTag() keywire := new(dnskeyWireFmt) keywire.Flags = k.Flags keywire.Protocol = k.Protocol keywire.Algorithm = k.Algorithm keywire.PublicKey = k.PublicKey wire := make([]byte, DefaultMsgSize) n, err := packKeyWire(keywire, wire) if err != nil { return nil } wire = wire[:n] owner := make([]byte, 255) off, err1 := PackDomainName(CanonicalName(k.Hdr.Name), owner, 0, nil, false) if err1 != nil { return nil } owner = owner[:off] // RFC4034: // digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA); // "|" denotes concatenation // DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. var hash crypto.Hash switch h { case SHA1: hash = crypto.SHA1 case SHA256: hash = crypto.SHA256 case SHA384: hash = crypto.SHA384 case SHA512: hash = crypto.SHA512 default: return nil } s := hash.New() s.Write(owner) s.Write(wire) ds.Digest = hex.EncodeToString(s.Sum(nil)) return ds } // ToCDNSKEY converts a DNSKEY record to a CDNSKEY record. func (k *DNSKEY) ToCDNSKEY() *CDNSKEY { c := &CDNSKEY{DNSKEY: *k} c.Hdr = k.Hdr c.Hdr.Rrtype = TypeCDNSKEY return c } // ToCDS converts a DS record to a CDS record. func (d *DS) ToCDS() *CDS { c := &CDS{DS: *d} c.Hdr = d.Hdr c.Hdr.Rrtype = TypeCDS return c } // Sign signs an RRSet. The signature needs to be filled in with the values: // Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied // from the RRset. Sign returns a non-nill error when the signing went OK. // There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non // zero, it is used as-is, otherwise the TTL of the RRset is used as the // OrigTTL. func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { h0 := rrset[0].Header() rr.Hdr.Rrtype = TypeRRSIG rr.Hdr.Name = h0.Name rr.Hdr.Class = h0.Class if rr.OrigTtl == 0 { // If set don't override rr.OrigTtl = h0.Ttl } rr.TypeCovered = h0.Rrtype rr.Labels = uint8(CountLabel(h0.Name)) if strings.HasPrefix(h0.Name, "*") { rr.Labels-- // wildcard, remove from label count } return rr.signAsIs(k, rrset) } func (rr *RRSIG) signAsIs(k crypto.Signer, rrset []RR) error { if k == nil { return ErrPrivKey } // s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { return ErrKey } sigwire := new(rrsigWireFmt) sigwire.TypeCovered = rr.TypeCovered sigwire.Algorithm = rr.Algorithm sigwire.Labels = rr.Labels sigwire.OrigTtl = rr.OrigTtl sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag // For signing, lowercase this name sigwire.SignerName = CanonicalName(rr.SignerName) // Create the desired binary blob signdata := make([]byte, DefaultMsgSize) n, err := packSigWire(sigwire, signdata) if err != nil { return err } signdata = signdata[:n] wire, err := rawSignatureData(rrset, rr) if err != nil { return err } h, cryptohash, err := hashFromAlgorithm(rr.Algorithm) if err != nil { return err } switch rr.Algorithm { case RSAMD5, DSA, DSANSEC3SHA1: // See RFC 6944. return ErrAlg default: h.Write(signdata) h.Write(wire) signature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm) if err != nil { return err } rr.Signature = toBase64(signature) return nil } } func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) { signature, err := k.Sign(rand.Reader, hashed, hash) if err != nil { return nil, err } switch alg { case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, ED25519: return signature, nil case ECDSAP256SHA256, ECDSAP384SHA384: ecdsaSignature := &struct { R, S *big.Int }{} if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil { return nil, err } var intlen int switch alg { case ECDSAP256SHA256: intlen = 32 case ECDSAP384SHA384: intlen = 48 } signature := intToBytes(ecdsaSignature.R, intlen) signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...) return signature, nil default: return nil, ErrAlg } } // Verify validates an RRSet with the signature and key. This is only the // cryptographic test, the signature validity period must be checked separately. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. // It also checks that the Zone Key bit (RFC 4034 2.1.1) is set on the DNSKEY // and that the Protocol field is set to 3 (RFC 4034 2.1.2). func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { // First the easy checks if !IsRRset(rrset) { return ErrRRset } if rr.KeyTag != k.KeyTag() { return ErrKey } if rr.Hdr.Class != k.Hdr.Class { return ErrKey } if rr.Algorithm != k.Algorithm { return ErrKey } signerName := CanonicalName(rr.SignerName) if !equal(signerName, k.Hdr.Name) { return ErrKey } if k.Protocol != 3 { return ErrKey } // RFC 4034 2.1.1 If bit 7 has value 0, then the DNSKEY record holds some // other type of DNS public key and MUST NOT be used to verify RRSIGs that // cover RRsets. if k.Flags&ZONE == 0 { return ErrKey } // IsRRset checked that we have at least one RR and that the RRs in // the set have consistent type, class, and name. Also check that type, // class and name matches the RRSIG record. // Also checks RFC 4035 5.3.1 the number of labels in the RRset owner // name MUST be greater than or equal to the value in the RRSIG RR's Labels field. // RFC 4035 5.3.1 Signer's Name MUST be the name of the zone that [contains the RRset]. // Since we don't have SOA info, checking suffix may be the best we can do...? if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered || uint8(CountLabel(h0.Name)) < rr.Labels || !equal(h0.Name, rr.Hdr.Name) || !strings.HasSuffix(CanonicalName(h0.Name), signerName) { return ErrRRset } // RFC 4035 5.3.2. Reconstructing the Signed Data // Copy the sig, except the rrsig data sigwire := new(rrsigWireFmt) sigwire.TypeCovered = rr.TypeCovered sigwire.Algorithm = rr.Algorithm sigwire.Labels = rr.Labels sigwire.OrigTtl = rr.OrigTtl sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag sigwire.SignerName = signerName // Create the desired binary blob signeddata := make([]byte, DefaultMsgSize) n, err := packSigWire(sigwire, signeddata) if err != nil { return err } signeddata = signeddata[:n] wire, err := rawSignatureData(rrset, rr) if err != nil { return err } sigbuf := rr.sigBuf() // Get the binary signature data // TODO(miek) // remove the domain name and assume its ours? // if rr.Algorithm == PRIVATEDNS { // PRIVATEOID // } h, cryptohash, err := hashFromAlgorithm(rr.Algorithm) if err != nil { return err } switch rr.Algorithm { case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512: // TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere?? pubkey := k.publicKeyRSA() // Get the key if pubkey == nil { return ErrKey } h.Write(signeddata) h.Write(wire) return rsa.VerifyPKCS1v15(pubkey, cryptohash, h.Sum(nil), sigbuf) case ECDSAP256SHA256, ECDSAP384SHA384: pubkey := k.publicKeyECDSA() if pubkey == nil { return ErrKey } // Split sigbuf into the r and s coordinates r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2]) s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:]) h.Write(signeddata) h.Write(wire) if ecdsa.Verify(pubkey, h.Sum(nil), r, s) { return nil } return ErrSig case ED25519: pubkey := k.publicKeyED25519() if pubkey == nil { return ErrKey } if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) { return nil } return ErrSig default: return ErrAlg } } // ValidityPeriod uses RFC1982 serial arithmetic to calculate // if a signature period is valid. If t is the zero time, the // current time is taken other t is. Returns true if the signature // is valid at the given time, otherwise returns false. func (rr *RRSIG) ValidityPeriod(t time.Time) bool { var utc int64 if t.IsZero() { utc = time.Now().UTC().Unix() } else { utc = t.UTC().Unix() } modi := (int64(rr.Inception) - utc) / year68 mode := (int64(rr.Expiration) - utc) / year68 ti := int64(rr.Inception) + modi*year68 te := int64(rr.Expiration) + mode*year68 return ti <= utc && utc <= te } // Return the signatures base64 encoding sigdata as a byte slice. func (rr *RRSIG) sigBuf() []byte { sigbuf, err := fromBase64([]byte(rr.Signature)) if err != nil { return nil } return sigbuf } // publicKeyRSA returns the RSA public key from a DNSKEY record. func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey { keybuf, err := fromBase64([]byte(k.PublicKey)) if err != nil { return nil } if len(keybuf) < 1+1+64 { // Exponent must be at least 1 byte and modulus at least 64 return nil } // RFC 2537/3110, section 2. RSA Public KEY Resource Records // Length is in the 0th byte, unless its zero, then it // it in bytes 1 and 2 and its a 16 bit number explen := uint16(keybuf[0]) keyoff := 1 if explen == 0 { explen = uint16(keybuf[1])<<8 | uint16(keybuf[2]) keyoff = 3 } if explen > 4 || explen == 0 || keybuf[keyoff] == 0 { // Exponent larger than supported by the crypto package, // empty, or contains prohibited leading zero. return nil } modoff := keyoff + int(explen) modlen := len(keybuf) - modoff if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 { // Modulus is too small, large, or contains prohibited leading zero. return nil } pubkey := new(rsa.PublicKey) var expo uint64 // The exponent of length explen is between keyoff and modoff. for _, v := range keybuf[keyoff:modoff] { expo <<= 8 expo |= uint64(v) } if expo > 1<<31-1 { // Larger exponent than supported by the crypto package. return nil } pubkey.E = int(expo) pubkey.N = new(big.Int).SetBytes(keybuf[modoff:]) return pubkey } // publicKeyECDSA returns the Curve public key from the DNSKEY record. func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey { keybuf, err := fromBase64([]byte(k.PublicKey)) if err != nil { return nil } pubkey := new(ecdsa.PublicKey) switch k.Algorithm { case ECDSAP256SHA256: pubkey.Curve = elliptic.P256() if len(keybuf) != 64 { // wrongly encoded key return nil } case ECDSAP384SHA384: pubkey.Curve = elliptic.P384() if len(keybuf) != 96 { // Wrongly encoded key return nil } } pubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2]) pubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:]) return pubkey } func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey { keybuf, err := fromBase64([]byte(k.PublicKey)) if err != nil { return nil } if len(keybuf) != ed25519.PublicKeySize { return nil } return keybuf } type wireSlice [][]byte func (p wireSlice) Len() int { return len(p) } func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p wireSlice) Less(i, j int) bool { _, ioff, _ := UnpackDomainName(p[i], 0) _, joff, _ := UnpackDomainName(p[j], 0) return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0 } // Return the raw signature data. func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { wires := make(wireSlice, len(rrset)) for i, r := range rrset { r1 := r.copy() h := r1.Header() h.Ttl = s.OrigTtl labels := SplitDomainName(h.Name) // 6.2. Canonical RR Form. (4) - wildcards if len(labels) > int(s.Labels) { // Wildcard h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "." } // RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase h.Name = CanonicalName(h.Name) // 6.2. Canonical RR Form. (3) - domain rdata to lowercase. // NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, // HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, // SRV, DNAME, A6 // // RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC): // Section 6.2 of [RFC4034] also erroneously lists HINFO as a record // that needs conversion to lowercase, and twice at that. Since HINFO // records contain no domain names, they are not subject to case // conversion. switch x := r1.(type) { case *NS: x.Ns = CanonicalName(x.Ns) case *MD: x.Md = CanonicalName(x.Md) case *MF: x.Mf = CanonicalName(x.Mf) case *CNAME: x.Target = CanonicalName(x.Target) case *SOA: x.Ns = CanonicalName(x.Ns) x.Mbox = CanonicalName(x.Mbox) case *MB: x.Mb = CanonicalName(x.Mb) case *MG: x.Mg = CanonicalName(x.Mg) case *MR: x.Mr = CanonicalName(x.Mr) case *PTR: x.Ptr = CanonicalName(x.Ptr) case *MINFO: x.Rmail = CanonicalName(x.Rmail) x.Email = CanonicalName(x.Email) case *MX: x.Mx = CanonicalName(x.Mx) case *RP: x.Mbox = CanonicalName(x.Mbox) x.Txt = CanonicalName(x.Txt) case *AFSDB: x.Hostname = CanonicalName(x.Hostname) case *RT: x.Host = CanonicalName(x.Host) case *SIG: x.SignerName = CanonicalName(x.SignerName) case *PX: x.Map822 = CanonicalName(x.Map822) x.Mapx400 = CanonicalName(x.Mapx400) case *NAPTR: x.Replacement = CanonicalName(x.Replacement) case *KX: x.Exchanger = CanonicalName(x.Exchanger) case *SRV: x.Target = CanonicalName(x.Target) case *DNAME: x.Target = CanonicalName(x.Target) } // 6.2. Canonical RR Form. (5) - origTTL wire := make([]byte, Len(r1)+1) // +1 to be safe(r) off, err1 := PackRR(r1, wire, 0, nil, false) if err1 != nil { return nil, err1 } wire = wire[:off] wires[i] = wire } sort.Sort(wires) for i, wire := range wires { if i > 0 && bytes.Equal(wire, wires[i-1]) { continue } buf = append(buf, wire...) } return buf, nil } func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) { // copied from zmsg.go RRSIG packing off, err := packUint16(sw.TypeCovered, msg, 0) if err != nil { return off, err } off, err = packUint8(sw.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(sw.Labels, msg, off) if err != nil { return off, err } off, err = packUint32(sw.OrigTtl, msg, off) if err != nil { return off, err } off, err = packUint32(sw.Expiration, msg, off) if err != nil { return off, err } off, err = packUint32(sw.Inception, msg, off) if err != nil { return off, err } off, err = packUint16(sw.KeyTag, msg, off) if err != nil { return off, err } off, err = PackDomainName(sw.SignerName, msg, off, nil, false) if err != nil { return off, err } return off, nil } func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) { // copied from zmsg.go DNSKEY packing off, err := packUint16(dw.Flags, msg, 0) if err != nil { return off, err } off, err = packUint8(dw.Protocol, msg, off) if err != nil { return off, err } off, err = packUint8(dw.Algorithm, msg, off) if err != nil { return off, err } off, err = packStringBase64(dw.PublicKey, msg, off) if err != nil { return off, err } return off, nil } golang-github-miekg-dns-1.1.64/dnssec_keygen.go000066400000000000000000000061501476742671700214050ustar00rootroot00000000000000package dns import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "math/big" ) // Generate generates a DNSKEY of the given bit size. // The public part is put inside the DNSKEY record. // The Algorithm in the key must be set as this will define // what kind of DNSKEY will be generated. // The ECDSA algorithms imply a fixed keysize, in that case // bits should be set to the size of the algorithm. func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) { switch k.Algorithm { case RSASHA1, RSASHA256, RSASHA1NSEC3SHA1: if bits < 512 || bits > 4096 { return nil, ErrKeySize } case RSASHA512: if bits < 1024 || bits > 4096 { return nil, ErrKeySize } case ECDSAP256SHA256: if bits != 256 { return nil, ErrKeySize } case ECDSAP384SHA384: if bits != 384 { return nil, ErrKeySize } case ED25519: if bits != 256 { return nil, ErrKeySize } default: return nil, ErrAlg } switch k.Algorithm { case RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1: priv, err := rsa.GenerateKey(rand.Reader, bits) if err != nil { return nil, err } k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N) return priv, nil case ECDSAP256SHA256, ECDSAP384SHA384: var c elliptic.Curve switch k.Algorithm { case ECDSAP256SHA256: c = elliptic.P256() case ECDSAP384SHA384: c = elliptic.P384() } priv, err := ecdsa.GenerateKey(c, rand.Reader) if err != nil { return nil, err } k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y) return priv, nil case ED25519: pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, err } k.setPublicKeyED25519(pub) return priv, nil default: return nil, ErrAlg } } // Set the public key (the value E and N) func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool { if _E == 0 || _N == nil { return false } buf := exponentToBuf(_E) buf = append(buf, _N.Bytes()...) k.PublicKey = toBase64(buf) return true } // Set the public key for Elliptic Curves func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool { if _X == nil || _Y == nil { return false } var intlen int switch k.Algorithm { case ECDSAP256SHA256: intlen = 32 case ECDSAP384SHA384: intlen = 48 } k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen)) return true } // Set the public key for Ed25519 func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool { if _K == nil { return false } k.PublicKey = toBase64(_K) return true } // Set the public key (the values E and N) for RSA // RFC 3110: Section 2. RSA Public KEY Resource Records func exponentToBuf(_E int) []byte { var buf []byte i := big.NewInt(int64(_E)).Bytes() if len(i) < 256 { buf = make([]byte, 1, 1+len(i)) buf[0] = uint8(len(i)) } else { buf = make([]byte, 3, 3+len(i)) buf[0] = 0 buf[1] = uint8(len(i) >> 8) buf[2] = uint8(len(i)) } buf = append(buf, i...) return buf } // Set the public key for X and Y for Curve. The two // values are just concatenated. func curveToBuf(_X, _Y *big.Int, intlen int) []byte { buf := intToBytes(_X, intlen) buf = append(buf, intToBytes(_Y, intlen)...) return buf } golang-github-miekg-dns-1.1.64/dnssec_keyscan.go000066400000000000000000000143111476742671700215560ustar00rootroot00000000000000package dns import ( "bufio" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "io" "math/big" "strconv" "strings" ) // NewPrivateKey returns a PrivateKey by parsing the string s. // s should be in the same form of the BIND private key files. func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) { if s == "" || s[len(s)-1] != '\n' { // We need a closing newline return k.ReadPrivateKey(strings.NewReader(s+"\n"), "") } return k.ReadPrivateKey(strings.NewReader(s), "") } // ReadPrivateKey reads a private key from the io.Reader q. The string file is // only used in error reporting. // The public key must be known, because some cryptographic algorithms embed // the public inside the privatekey. func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) { m, err := parseKey(q, file) if m == nil { return nil, err } if _, ok := m["private-key-format"]; !ok { return nil, ErrPrivKey } if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" { return nil, ErrPrivKey } // TODO(mg): check if the pubkey matches the private key algoStr, _, _ := strings.Cut(m["algorithm"], " ") algo, err := strconv.ParseUint(algoStr, 10, 8) if err != nil { return nil, ErrPrivKey } switch uint8(algo) { case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512: priv, err := readPrivateKeyRSA(m) if err != nil { return nil, err } pub := k.publicKeyRSA() if pub == nil { return nil, ErrKey } priv.PublicKey = *pub return priv, nil case ECDSAP256SHA256, ECDSAP384SHA384: priv, err := readPrivateKeyECDSA(m) if err != nil { return nil, err } pub := k.publicKeyECDSA() if pub == nil { return nil, ErrKey } priv.PublicKey = *pub return priv, nil case ED25519: return readPrivateKeyED25519(m) default: return nil, ErrAlg } } // Read a private key (file) string and create a public key. Return the private key. func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) { p := new(rsa.PrivateKey) p.Primes = []*big.Int{nil, nil} for k, v := range m { switch k { case "modulus", "publicexponent", "privateexponent", "prime1", "prime2": v1, err := fromBase64([]byte(v)) if err != nil { return nil, err } switch k { case "modulus": p.PublicKey.N = new(big.Int).SetBytes(v1) case "publicexponent": i := new(big.Int).SetBytes(v1) p.PublicKey.E = int(i.Int64()) // int64 should be large enough case "privateexponent": p.D = new(big.Int).SetBytes(v1) case "prime1": p.Primes[0] = new(big.Int).SetBytes(v1) case "prime2": p.Primes[1] = new(big.Int).SetBytes(v1) } case "exponent1", "exponent2", "coefficient": // not used in Go (yet) case "created", "publish", "activate": // not used in Go (yet) } } return p, nil } func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) { p := new(ecdsa.PrivateKey) p.D = new(big.Int) // TODO: validate that the required flags are present for k, v := range m { switch k { case "privatekey": v1, err := fromBase64([]byte(v)) if err != nil { return nil, err } p.D.SetBytes(v1) case "created", "publish", "activate": /* not used in Go (yet) */ } } return p, nil } func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) { var p ed25519.PrivateKey // TODO: validate that the required flags are present for k, v := range m { switch k { case "privatekey": p1, err := fromBase64([]byte(v)) if err != nil { return nil, err } if len(p1) != ed25519.SeedSize { return nil, ErrPrivKey } p = ed25519.NewKeyFromSeed(p1) case "created", "publish", "activate": /* not used in Go (yet) */ } } return p, nil } // parseKey reads a private key from r. It returns a map[string]string, // with the key-value pairs, or an error when the file is not correct. func parseKey(r io.Reader, file string) (map[string]string, error) { m := make(map[string]string) var k string c := newKLexer(r) for l, ok := c.Next(); ok; l, ok = c.Next() { // It should alternate switch l.value { case zKey: k = l.token case zValue: if k == "" { return nil, &ParseError{file: file, err: "no private key seen", lex: l} } m[strings.ToLower(k)] = l.token k = "" } } // Surface any read errors from r. if err := c.Err(); err != nil { return nil, &ParseError{file: file, err: err.Error()} } return m, nil } type klexer struct { br io.ByteReader readErr error line int column int key bool eol bool // end-of-line } func newKLexer(r io.Reader) *klexer { br, ok := r.(io.ByteReader) if !ok { br = bufio.NewReaderSize(r, 1024) } return &klexer{ br: br, line: 1, key: true, } } func (kl *klexer) Err() error { if kl.readErr == io.EOF { return nil } return kl.readErr } // readByte returns the next byte from the input func (kl *klexer) readByte() (byte, bool) { if kl.readErr != nil { return 0, false } c, err := kl.br.ReadByte() if err != nil { kl.readErr = err return 0, false } // delay the newline handling until the next token is delivered, // fixes off-by-one errors when reporting a parse error. if kl.eol { kl.line++ kl.column = 0 kl.eol = false } if c == '\n' { kl.eol = true } else { kl.column++ } return c, true } func (kl *klexer) Next() (lex, bool) { var ( l lex str strings.Builder commt bool ) for x, ok := kl.readByte(); ok; x, ok = kl.readByte() { l.line, l.column = kl.line, kl.column switch x { case ':': if commt || !kl.key { break } kl.key = false // Next token is a space, eat it kl.readByte() l.value = zKey l.token = str.String() return l, true case ';': commt = true case '\n': if commt { // Reset a comment commt = false } if kl.key && str.Len() == 0 { // ignore empty lines break } kl.key = true l.value = zValue l.token = str.String() return l, true default: if commt { break } str.WriteByte(x) } } if kl.readErr != nil && kl.readErr != io.EOF { // Don't return any tokens after a read error occurs. return lex{value: zEOF}, false } if str.Len() > 0 { // Send remainder l.value = zValue l.token = str.String() return l, true } return lex{value: zEOF}, false } golang-github-miekg-dns-1.1.64/dnssec_privkey.go000066400000000000000000000044251476742671700216170ustar00rootroot00000000000000package dns import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "math/big" "strconv" ) const format = "Private-key-format: v1.3\n" var bigIntOne = big.NewInt(1) // PrivateKeyString converts a PrivateKey to a string. This string has the same // format as the private-key-file of BIND9 (Private-key-format: v1.3). // It needs some info from the key (the algorithm), so its a method of the DNSKEY. // It supports *rsa.PrivateKey, *ecdsa.PrivateKey and ed25519.PrivateKey. func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string { algorithm := strconv.Itoa(int(r.Algorithm)) algorithm += " (" + AlgorithmToString[r.Algorithm] + ")" switch p := p.(type) { case *rsa.PrivateKey: modulus := toBase64(p.PublicKey.N.Bytes()) e := big.NewInt(int64(p.PublicKey.E)) publicExponent := toBase64(e.Bytes()) privateExponent := toBase64(p.D.Bytes()) prime1 := toBase64(p.Primes[0].Bytes()) prime2 := toBase64(p.Primes[1].Bytes()) // Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm // and from: http://code.google.com/p/go/issues/detail?id=987 p1 := new(big.Int).Sub(p.Primes[0], bigIntOne) q1 := new(big.Int).Sub(p.Primes[1], bigIntOne) exp1 := new(big.Int).Mod(p.D, p1) exp2 := new(big.Int).Mod(p.D, q1) coeff := new(big.Int).ModInverse(p.Primes[1], p.Primes[0]) exponent1 := toBase64(exp1.Bytes()) exponent2 := toBase64(exp2.Bytes()) coefficient := toBase64(coeff.Bytes()) return format + "Algorithm: " + algorithm + "\n" + "Modulus: " + modulus + "\n" + "PublicExponent: " + publicExponent + "\n" + "PrivateExponent: " + privateExponent + "\n" + "Prime1: " + prime1 + "\n" + "Prime2: " + prime2 + "\n" + "Exponent1: " + exponent1 + "\n" + "Exponent2: " + exponent2 + "\n" + "Coefficient: " + coefficient + "\n" case *ecdsa.PrivateKey: var intlen int switch r.Algorithm { case ECDSAP256SHA256: intlen = 32 case ECDSAP384SHA384: intlen = 48 } private := toBase64(intToBytes(p.D, intlen)) return format + "Algorithm: " + algorithm + "\n" + "PrivateKey: " + private + "\n" case ed25519.PrivateKey: private := toBase64(p.Seed()) return format + "Algorithm: " + algorithm + "\n" + "PrivateKey: " + private + "\n" default: return "" } } golang-github-miekg-dns-1.1.64/dnssec_test.go000066400000000000000000001032501476742671700211010ustar00rootroot00000000000000package dns import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "reflect" "strings" "testing" "time" ) func getSoa() *SOA { soa := new(SOA) soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} soa.Ns = "open.nlnetlabs.nl." soa.Mbox = "miekg.atoom.net." soa.Serial = 1293945905 soa.Refresh = 14400 soa.Retry = 3600 soa.Expire = 604800 soa.Minttl = 86400 return soa } func TestSecure(t *testing.T) { soa := getSoa() sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = TypeSOA sig.Algorithm = RSASHA256 sig.Labels = 2 sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.OrigTtl = 14400 sig.KeyTag = 12051 sig.SignerName = "miek.nl." sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M=" key := new(DNSKEY) key.Hdr.Name = "miek.nl." key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" // It should validate. Period is checked separately, so this will keep on working if sig.Verify(key, []RR{soa}) != nil { t.Error("failure to validate") } } func TestSignature(t *testing.T) { sig := new(RRSIG) sig.Hdr.Name = "miek.nl." sig.Hdr.Class = ClassINET sig.Hdr.Ttl = 3600 sig.TypeCovered = TypeDNSKEY sig.Algorithm = RSASHA1 sig.Labels = 2 sig.OrigTtl = 4000 sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970 sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970 sig.KeyTag = 34641 sig.SignerName = "miek.nl." sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" // Should not be valid if sig.ValidityPeriod(time.Now()) { t.Error("should not be valid") } sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980 sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100 if !sig.ValidityPeriod(time.Now()) { t.Error("should be valid") } } func TestSignVerify(t *testing.T) { // The record we want to sign soa := new(SOA) soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} soa.Ns = "open.nlnetlabs.nl." soa.Mbox = "miekg.atoom.net." soa.Serial = 1293945905 soa.Refresh = 14400 soa.Retry = 3600 soa.Expire = 604800 soa.Minttl = 86400 soa1 := new(SOA) soa1.Hdr = RR_Header{"*.miek.nl.", TypeSOA, ClassINET, 14400, 0} soa1.Ns = "open.nlnetlabs.nl." soa1.Mbox = "miekg.atoom.net." soa1.Serial = 1293945905 soa1.Refresh = 14400 soa1.Retry = 3600 soa1.Expire = 604800 soa1.Minttl = 86400 srv := new(SRV) srv.Hdr = RR_Header{"srv.miek.nl.", TypeSRV, ClassINET, 14400, 0} srv.Port = 1000 srv.Weight = 800 srv.Target = "web1.miek.nl." hinfo := &HINFO{ Hdr: RR_Header{ Name: "miek.nl.", Rrtype: TypeHINFO, Class: ClassINET, Ttl: 3789, }, Cpu: "X", Os: "Y", } // With this key key := new(DNSKEY) key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Name = "miek.nl." key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 privkey, _ := key.Generate(512) // Fill in the values of the Sig, before signing sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = soa.Hdr.Rrtype sig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3 sig.OrigTtl = soa.Hdr.Ttl sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.KeyTag = key.KeyTag() // Get the keyfrom the Key sig.SignerName = key.Hdr.Name sig.Algorithm = RSASHA256 for _, r := range []RR{soa, soa1, srv, hinfo} { if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil { t.Error("failure to sign the record:", err) continue } if err := sig.Verify(key, []RR{r}); err != nil { t.Errorf("failure to validate: %s", r.Header().Name) continue } } } // Test if RRSIG.Verify() conforms to RFC 4035 Section 5.3.1 func TestShouldNotVerifyInvalidSig(t *testing.T) { // The RRSIG RR and the RRset MUST have the same owner name rrNameMismatch := getSoa() rrNameMismatch.Hdr.Name = "example.com." // ... and the same class rrClassMismatch := getSoa() rrClassMismatch.Hdr.Class = ClassCHAOS // The RRSIG RR's Type Covered field MUST equal the RRset's type. rrTypeMismatch := getSoa() rrTypeMismatch.Hdr.Rrtype = TypeA // The number of labels in the RRset owner name MUST be greater than // or equal to the value in the RRSIG RR's Labels field. rrLabelLessThan := getSoa() rrLabelLessThan.Hdr.Name = "nl." // Time checks are done in ValidityPeriod // With this key key := new(DNSKEY) key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Name = "miek.nl." key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 privkey, _ := key.Generate(512) normalSoa := getSoa() // Fill in the normal values of the Sig, before signing sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = TypeSOA sig.Labels = uint8(CountLabel(normalSoa.Hdr.Name)) sig.OrigTtl = normalSoa.Hdr.Ttl sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.KeyTag = key.KeyTag() // Get the keyfrom the Key sig.SignerName = key.Hdr.Name sig.Algorithm = RSASHA256 for i, rr := range []RR{rrNameMismatch, rrClassMismatch, rrTypeMismatch, rrLabelLessThan} { if i != 0 { // Just for the rrNameMismatch case, we need the name to mismatch sig := sig.copy().(*RRSIG) sig.SignerName = rr.Header().Name sig.Hdr.Name = rr.Header().Name key := key.copy().(*DNSKEY) key.Hdr.Name = rr.Header().Name } if err := sig.signAsIs(privkey.(*rsa.PrivateKey), []RR{rr}); err != nil { t.Error("failure to sign the record:", err) continue } if err := sig.Verify(key, []RR{rr}); err == nil { t.Error("should not validate: ", rr) continue } else { t.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)) } } // The RRSIG RR's Signer's Name field MUST be the name of the zone that contains the RRset. // The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner name, // algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset. sigMismatchName := sig.copy().(*RRSIG) sigMismatchName.SignerName = "example.com." soaMismatchName := getSoa() soaMismatchName.Hdr.Name = "example.com." keyMismatchName := key.copy().(*DNSKEY) keyMismatchName.Hdr.Name = "example.com." if err := sigMismatchName.signAsIs(privkey.(*rsa.PrivateKey), []RR{soaMismatchName}); err != nil { t.Error("failure to sign the record:", err) } else if err := sigMismatchName.Verify(keyMismatchName, []RR{soaMismatchName}); err == nil { t.Error("should not validate: ", soaMismatchName, ", RRSIG's signer's name does not match the owner name") } else { t.Logf("expected failure: %v for signer %s and owner %s", err, sigMismatchName.SignerName, sigMismatchName.Hdr.Name) } sigMismatchAlgo := sig.copy().(*RRSIG) sigMismatchAlgo.Algorithm = RSASHA1 sigMismatchKeyTag := sig.copy().(*RRSIG) sigMismatchKeyTag.KeyTag = 12345 for _, sigMismatch := range []*RRSIG{sigMismatchAlgo, sigMismatchKeyTag} { if err := sigMismatch.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil { t.Error("failure to sign the record:", err) } else if err := sigMismatch.Verify(key, []RR{normalSoa}); err == nil { t.Error("should not validate: ", normalSoa) } else { t.Logf("expected failure: %v for signer %s algo %d keytag %d", err, sigMismatch.SignerName, sigMismatch.Algorithm, sigMismatch.KeyTag) } } // The matching DNSKEY RR MUST have the Zone Flag bit (DNSKEY RDATA Flag bit 7) set. keyZoneBitWrong := key.copy().(*DNSKEY) keyZoneBitWrong.Flags = key.Flags &^ ZONE if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil { t.Error("failure to sign the record:", err) } else if err := sig.Verify(keyZoneBitWrong, []RR{normalSoa}); err == nil { t.Error("should not validate: ", normalSoa) } else { t.Logf("expected failure: %v for key flags %d", err, keyZoneBitWrong.Flags) } } func Test65534(t *testing.T) { t6 := new(RFC3597) t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0} t6.Rdata = "505D870001" key := new(DNSKEY) key.Hdr.Name = "miek.nl." key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 privkey, _ := key.Generate(512) sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = t6.Hdr.Rrtype sig.Labels = uint8(CountLabel(t6.Hdr.Name)) sig.OrigTtl = t6.Hdr.Ttl sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.KeyTag = key.KeyTag() sig.SignerName = key.Hdr.Name sig.Algorithm = RSASHA256 if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil { t.Error(err) t.Error("failure to sign the TYPE65534 record") } if err := sig.Verify(key, []RR{t6}); err != nil { t.Error(err) t.Errorf("failure to validate %s", t6.Header().Name) } } func TestDnskey(t *testing.T) { pubkey, err := ReadRR(strings.NewReader(` miek.nl. IN DNSKEY 256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b} `), "Kmiek.nl.+010+05240.key") if err != nil { t.Fatal(err) } privStr := `Private-key-format: v1.3 Algorithm: 10 (RSASHA512) Modulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs= PublicExponent: AQAB PrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE= Prime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ== Prime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw== Exponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ== Exponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw== Coefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ== ` privkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr), "Kmiek.nl.+010+05240.private") if err != nil { t.Fatal(err) } if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" { t.Error("pubkey is not what we've read") } if pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr { t.Error("privkey is not what we've read") t.Errorf("%v", pubkey.(*DNSKEY).PrivateKeyString(privkey)) } } func TestTag(t *testing.T) { key := new(DNSKEY) key.Hdr.Name = "miek.nl." key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Class = ClassINET key.Hdr.Ttl = 3600 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" tag := key.KeyTag() if tag != 12051 { t.Errorf("wrong key tag: %d for key %v", tag, key) } } func TestKeyRSA(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } key := new(DNSKEY) key.Hdr.Name = "miek.nl." key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Class = ClassINET key.Hdr.Ttl = 3600 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 priv, _ := key.Generate(512) soa := new(SOA) soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} soa.Ns = "open.nlnetlabs.nl." soa.Mbox = "miekg.atoom.net." soa.Serial = 1293945905 soa.Refresh = 14400 soa.Retry = 3600 soa.Expire = 604800 soa.Minttl = 86400 sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = TypeSOA sig.Algorithm = RSASHA256 sig.Labels = 2 sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.OrigTtl = soa.Hdr.Ttl sig.KeyTag = key.KeyTag() sig.SignerName = key.Hdr.Name if err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil { t.Error("failed to sign") return } if err := sig.Verify(key, []RR{soa}); err != nil { t.Error("failed to verify") } } func TestKeyToDS(t *testing.T) { key := new(DNSKEY) key.Hdr.Name = "miek.nl." key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Class = ClassINET key.Hdr.Ttl = 3600 key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" ds := key.ToDS(SHA1) if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" { t.Errorf("wrong DS digest for SHA1\n%v", ds) } } func TestSignRSA(t *testing.T) { pub := "miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ" priv := `Private-key-format: v1.3 Algorithm: 5 (RSASHA1) Modulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k= PublicExponent: AQAB PrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk= Prime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw== Prime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw== Exponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ== Exponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw== Coefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw== Created: 20110302104537 Publish: 20110302104537 Activate: 20110302104537` xk := testRR(pub) k := xk.(*DNSKEY) p, err := k.NewPrivateKey(priv) if err != nil { t.Error(err) } switch priv := p.(type) { case *rsa.PrivateKey: if priv.PublicKey.E != 65537 { t.Error("exponenent should be 65537") } default: t.Errorf("we should have read an RSA key: %v", priv) } if k.KeyTag() != 37350 { t.Errorf("keytag should be 37350, got %d %v", k.KeyTag(), k) } soa := new(SOA) soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} soa.Ns = "open.nlnetlabs.nl." soa.Mbox = "miekg.atoom.net." soa.Serial = 1293945905 soa.Refresh = 14400 soa.Retry = 3600 soa.Expire = 604800 soa.Minttl = 86400 sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.KeyTag = k.KeyTag() sig.SignerName = k.Hdr.Name sig.Algorithm = k.Algorithm sig.Sign(p.(*rsa.PrivateKey), []RR{soa}) if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" { t.Errorf("signature is not correct: %v", sig) } } func TestSignVerifyECDSA(t *testing.T) { pub := `example.net. 3600 IN DNSKEY 257 3 14 ( xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1 w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8 /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )` priv := `Private-key-format: v1.2 Algorithm: 14 (ECDSAP384SHA384) PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` eckey := testRR(pub) privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv) if err != nil { t.Fatal(err) } // TODO: Create separate test for this ds := eckey.(*DNSKEY).ToDS(SHA384) if ds.KeyTag != 10771 { t.Fatal("wrong keytag on DS") } if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" { t.Fatal("wrong DS Digest") } a := testRR("www.example.net. 3600 IN A 192.0.2.1") sig := new(RRSIG) sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0} sig.Expiration, _ = StringToTime("20100909102025") sig.Inception, _ = StringToTime("20100812102025") sig.KeyTag = eckey.(*DNSKEY).KeyTag() sig.SignerName = eckey.(*DNSKEY).Hdr.Name sig.Algorithm = eckey.(*DNSKEY).Algorithm if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil { t.Fatal("failure to sign the record") } if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil { t.Fatalf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v", eckey.(*DNSKEY).String(), a.String(), sig.String(), eckey.(*DNSKEY).PrivateKeyString(privkey), err, ) } } func TestSignVerifyECDSA2(t *testing.T) { srv1 := testRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.") srv := srv1.(*SRV) // With this key key := new(DNSKEY) key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Name = "miek.nl." key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = ECDSAP256SHA256 privkey, err := key.Generate(256) if err != nil { t.Fatal("failure to generate key") } // Fill in the values of the Sig, before signing sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = srv.Hdr.Rrtype sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3 sig.OrigTtl = srv.Hdr.Ttl sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.KeyTag = key.KeyTag() // Get the keyfrom the Key sig.SignerName = key.Hdr.Name sig.Algorithm = ECDSAP256SHA256 if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil { t.Fatal("failure to sign the record") } err = sig.Verify(key, []RR{srv}) if err != nil { t.Errorf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v", key.String(), srv.String(), sig.String(), key.PrivateKeyString(privkey), err, ) } } func TestSignVerifyEd25519(t *testing.T) { srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.") if err != nil { t.Fatal(err) } srv := srv1.(*SRV) // With this key key := new(DNSKEY) key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Name = "miek.nl." key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = ED25519 privkey, err := key.Generate(256) if err != nil { t.Fatal("failure to generate key") } // Fill in the values of the Sig, before signing sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.TypeCovered = srv.Hdr.Rrtype sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3 sig.OrigTtl = srv.Hdr.Ttl sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.KeyTag = key.KeyTag() // Get the keyfrom the Key sig.SignerName = key.Hdr.Name sig.Algorithm = ED25519 if sig.Sign(privkey.(ed25519.PrivateKey), []RR{srv}) != nil { t.Fatal("failure to sign the record") } err = sig.Verify(key, []RR{srv}) if err != nil { t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v", key.String(), srv.String(), sig.String(), key.PrivateKeyString(privkey), err, ) } } // Here the test vectors from the relevant RFCs are checked. // rfc6605 6.1 func TestRFC6605P256(t *testing.T) { exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 ( GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )` exPriv := `Private-key-format: v1.2 Algorithm: 13 (ECDSAP256SHA256) PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=` rrDNSKEY := testRR(exDNSKEY) priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv) if err != nil { t.Fatal(err) } exDS := `example.net. 3600 IN DS 55648 13 2 ( b4c8c1fe2e7477127b27115656ad6256f424625bf5c1 e2770ce6d6e37df61d17 )` rrDS := testRR(exDS) ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256) if !reflect.DeepEqual(ourDS, rrDS.(*DS)) { t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS)) } exA := `www.example.net. 3600 IN A 192.0.2.1` exRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 ( 20100909100439 20100812100439 55648 example.net. qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )` rrA := testRR(exA) rrRRSIG := testRR(exRRSIG) if err := rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil { t.Errorf("failure to validate the spec RRSIG: %v", err) } ourRRSIG := &RRSIG{ Hdr: RR_Header{ Ttl: rrA.Header().Ttl, }, KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(), SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name, Algorithm: rrDNSKEY.(*DNSKEY).Algorithm, } ourRRSIG.Expiration, _ = StringToTime("20100909100439") ourRRSIG.Inception, _ = StringToTime("20100812100439") err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA}) if err != nil { t.Fatal(err) } if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil { t.Errorf("failure to validate our RRSIG: %v", err) } // Signatures are randomized rrRRSIG.(*RRSIG).Signature = "" ourRRSIG.Signature = "" if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) { t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG)) } } // rfc6605 6.2 func TestRFC6605P384(t *testing.T) { exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 ( xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1 w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8 /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )` exPriv := `Private-key-format: v1.2 Algorithm: 14 (ECDSAP384SHA384) PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` rrDNSKEY := testRR(exDNSKEY) priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv) if err != nil { t.Fatal(err) } exDS := `example.net. 3600 IN DS 10771 14 4 ( 72d7b62976ce06438e9c0bf319013cf801f09ecc84b8 d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94 6df983d6 )` rrDS := testRR(exDS) ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384) if !reflect.DeepEqual(ourDS, rrDS.(*DS)) { t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS)) } exA := `www.example.net. 3600 IN A 192.0.2.1` exRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 ( 20100909102025 20100812102025 10771 example.net. /L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP 95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )` rrA := testRR(exA) rrRRSIG := testRR(exRRSIG) if err != nil { t.Fatal(err) } if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil { t.Errorf("failure to validate the spec RRSIG: %v", err) } ourRRSIG := &RRSIG{ Hdr: RR_Header{ Ttl: rrA.Header().Ttl, }, KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(), SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name, Algorithm: rrDNSKEY.(*DNSKEY).Algorithm, } ourRRSIG.Expiration, _ = StringToTime("20100909102025") ourRRSIG.Inception, _ = StringToTime("20100812102025") err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA}) if err != nil { t.Fatal(err) } if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil { t.Errorf("failure to validate our RRSIG: %v", err) } // Signatures are randomized rrRRSIG.(*RRSIG).Signature = "" ourRRSIG.Signature = "" if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) { t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG)) } } // rfc8080 6.1 func TestRFC8080Ed25519Example1(t *testing.T) { exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 ( l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )` exPriv := `Private-key-format: v1.2 Algorithm: 15 (ED25519) PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=` rrDNSKEY, err := NewRR(exDNSKEY) if err != nil { t.Fatal(err) } priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv) if err != nil { t.Fatal(err) } exDS := `example.com. 3600 IN DS 3613 15 2 ( 3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79 a304b )` rrDS, err := NewRR(exDS) if err != nil { t.Fatal(err) } ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256) if !reflect.DeepEqual(ourDS, rrDS.(*DS)) { t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS)) } exMX := `example.com. 3600 IN MX 10 mail.example.com.` exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 ( 1440021600 1438207200 3613 example.com. ( oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f x8A4M3e23mRZ9VrbpMngwcrqNAg== ) )` rrMX, err := NewRR(exMX) if err != nil { t.Fatal(err) } rrRRSIG, err := NewRR(exRRSIG) if err != nil { t.Fatal(err) } if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil { t.Errorf("failure to validate the spec RRSIG: %v", err) } ourRRSIG := &RRSIG{ Hdr: RR_Header{ Ttl: rrMX.Header().Ttl, }, KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(), SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name, Algorithm: rrDNSKEY.(*DNSKEY).Algorithm, } ourRRSIG.Expiration, _ = StringToTime("20150819220000") ourRRSIG.Inception, _ = StringToTime("20150729220000") err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX}) if err != nil { t.Fatal(err) } if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil { t.Errorf("failure to validate our RRSIG: %v", err) } if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) { t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG)) } } // rfc8080 6.1 func TestRFC8080Ed25519Example2(t *testing.T) { exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 ( zPnZ/QwEe7S8C5SPz2OfS5RR40ATk2/rYnE9xHIEijs= )` exPriv := `Private-key-format: v1.2 Algorithm: 15 (ED25519) PrivateKey: DSSF3o0s0f+ElWzj9E/Osxw8hLpk55chkmx0LYN5WiY=` rrDNSKEY, err := NewRR(exDNSKEY) if err != nil { t.Fatal(err) } priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv) if err != nil { t.Fatal(err) } exDS := `example.com. 3600 IN DS 35217 15 2 ( 401781b934e392de492ec77ae2e15d70f6575a1c0bc59c5275c04ebe80c 6614c )` rrDS, err := NewRR(exDS) if err != nil { t.Fatal(err) } ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256) if !reflect.DeepEqual(ourDS, rrDS.(*DS)) { t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS)) } exMX := `example.com. 3600 IN MX 10 mail.example.com.` exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 ( 1440021600 1438207200 35217 example.com. ( zXQ0bkYgQTEFyfLyi9QoiY6D8ZdYo4wyUhVioYZXFdT410QPRITQSqJSnzQ oSm5poJ7gD7AQR0O7KuI5k2pcBg== ) )` rrMX, err := NewRR(exMX) if err != nil { t.Fatal(err) } rrRRSIG, err := NewRR(exRRSIG) if err != nil { t.Fatal(err) } if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil { t.Errorf("failure to validate the spec RRSIG: %v", err) } ourRRSIG := &RRSIG{ Hdr: RR_Header{ Ttl: rrMX.Header().Ttl, }, KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(), SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name, Algorithm: rrDNSKEY.(*DNSKEY).Algorithm, } ourRRSIG.Expiration, _ = StringToTime("20150819220000") ourRRSIG.Inception, _ = StringToTime("20150729220000") err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX}) if err != nil { t.Fatal(err) } if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil { t.Errorf("failure to validate our RRSIG: %v", err) } if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) { t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG)) } } func TestInvalidRRSet(t *testing.T) { goodRecords := make([]RR, 2) goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}} // Generate key keyname := "cloudflare.com." key := &DNSKEY{ Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0}, Algorithm: ECDSAP256SHA256, Flags: ZONE, Protocol: 3, } privatekey, err := key.Generate(256) if err != nil { t.Fatal(err.Error()) } // Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm curTime := time.Now() signature := &RRSIG{ Inception: uint32(curTime.Unix()), Expiration: uint32(curTime.Add(time.Hour).Unix()), KeyTag: key.KeyTag(), SignerName: keyname, Algorithm: ECDSAP256SHA256, } // Inconsistent name between records badRecords := make([]RR, 2) badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}} if IsRRset(badRecords) { t.Fatal("Record set with inconsistent names considered valid") } badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}} if IsRRset(badRecords) { t.Fatal("Record set with inconsistent record types considered valid") } badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}} if IsRRset(badRecords) { t.Fatal("Record set with inconsistent record class considered valid") } // Sign the good record set and then make sure verification fails on the bad record set if err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil { t.Fatal("Signing good records failed") } if err := signature.Verify(key, badRecords); err != ErrRRset { t.Fatal("Verification did not return ErrRRset with inconsistent records") } } // Issue #688 - RSA exponent unpacked in reverse func TestRsaExponentUnpack(t *testing.T) { zskRrDnskey, _ := NewRR("isc.org. 7200 IN DNSKEY 256 3 5 AwEAAcdkaRUlsRD4gcF63PpPJJ1E6kOIb3yn/UHptVsPEQtEbgJ2y20O eix4unpwoQkz+bIAd2rrOU/95wgV530x0/qqKwBLWoGkxdcnNcvVT4hl 3SOTZy1VjwkAfyayHPU8VisXqJGbB3KWevBZlb6AtrXzFu8AHuBeeAAe /fOgreCh") kskRrDnskey, _ := 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") kskRrRrsig, _ := 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==") zskRrRrsig, _ := NewRR("isc.org. 7200 IN RRSIG DNSKEY 5 2 7200 20180627230244 20180528230244 19923 isc.org. RgCfzUeq4RJPGoe9RRB6cWf6d/Du+tHK5SxI5QL1waA3O5qVtQKFkY1C dq/yyVjwzfjD9F62TObujOaktv8X80ZMcNPmgHbvK1xOqelMBWv5hxj3 xRe+QQObLZ5NPfHFsphQKXvwgO5Sjk8py2B2iCr3BHCZ8S38oIfuSrQx sn8=") zsk, ksk := zskRrDnskey.(*DNSKEY), kskRrDnskey.(*DNSKEY) zskSig, kskSig := zskRrRrsig.(*RRSIG), kskRrRrsig.(*RRSIG) if e := zskSig.Verify(zsk, []RR{zsk, ksk}); e != nil { t.Fatalf("cannot verify RRSIG with keytag [%d]. Cause [%s]", zsk.KeyTag(), e.Error()) } if e := kskSig.Verify(ksk, []RR{zsk, ksk}); e != nil { t.Fatalf("cannot verify RRSIG with keytag [%d]. Cause [%s]", ksk.KeyTag(), e.Error()) } } func TestParseKeyReadError(t *testing.T) { m, err := parseKey(errReader{}, "") if err == nil || !strings.Contains(err.Error(), errTestReadError.Error()) { t.Errorf("expected error to contain %q, but got %v", errTestReadError, err) } if m != nil { t.Errorf("expected a nil map, but got %v", m) } } func TestRSAMD5KeyTag(t *testing.T) { rr1, _ := NewRR("test. IN DNSKEY 257 3 1 AwEAAcntNdoMnY8pvyPcpDTAaiqHyAhf53XUBANq166won/fjBFvmuzhTuP5r4el/pV0tzEBL73zpoU48BqF66uiL+qRijXCySJiaBUvLNll5rpwuduAOoVpmwOmkC4fV6izHOAx/Uy8c+pYP0YR8+1P7GuTFxgnMmt9sUGtoe+la0X/ ;{id = 27461 (ksk), size = 1024b}") rr2, _ := NewRR("test. IN DNSKEY 257 3 1 AwEAAf0bKO/m45ylk5BlSLmQHQRBLx1m/ZUXvyPFB387bJXxnTk6so3ub97L1RQ+8bOoiRh3Qm5EaYihjco7J8b/W5WbS3tVsE79nY584RfTKT2zcZ9AoFP2XLChXxPIf/6l0H9n6sH0aBjsG8vabEIp8e06INM3CXVPiMRPPeGNa0Ub ;{id = 27461 (ksk), size = 1024b}") exp := uint16(27461) if x := rr1.(*DNSKEY).KeyTag(); x != exp { t.Errorf("expected %d, got %d, as keytag for rr1", exp, x) } if x := rr2.(*DNSKEY).KeyTag(); x != exp { // yes, same key tag t.Errorf("expected %d, got %d, as keytag for rr2", exp, x) } } golang-github-miekg-dns-1.1.64/dnsutil/000077500000000000000000000000001476742671700177155ustar00rootroot00000000000000golang-github-miekg-dns-1.1.64/dnsutil/util.go000066400000000000000000000047231476742671700212270ustar00rootroot00000000000000// Package dnsutil contains higher-level methods useful with the dns // package. While package dns implements the DNS protocols itself, // these functions are related but not directly required for protocol // processing. They are often useful in preparing input/output of the // functions in package dns. package dnsutil import ( "strings" "github.com/miekg/dns" ) // AddOrigin adds origin to s if s is not already a FQDN. // Note that the result may not be a FQDN. If origin does not end // with a ".", the result won't either. // This implements the zonefile convention (specified in RFC 1035, // Section "5.1. Format") that "@" represents the // apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.". func AddOrigin(s, origin string) string { // ("foo.", "origin.") -> "foo." (already a FQDN) // ("foo", "origin.") -> "foo.origin." // ("foo", "origin") -> "foo.origin" // ("foo", ".") -> "foo." (Same as dns.Fqdn()) // ("foo.", ".") -> "foo." (Same as dns.Fqdn()) // ("@", "origin.") -> "origin." (@ represents the apex (bare) domain) // ("", "origin.") -> "origin." (not obvious) // ("foo", "") -> "foo" (not obvious) if dns.IsFqdn(s) { return s // s is already a FQDN, no need to mess with it. } if origin == "" { return s // Nothing to append. } if s == "@" || s == "" { return origin // Expand apex. } if origin == "." { return dns.Fqdn(s) } return s + "." + origin // The simple case. } // TrimDomainName trims origin from s if s is a subdomain. // This function will never return "", but returns "@" instead (@ represents the apex domain). func TrimDomainName(s, origin string) string { // An apex (bare) domain is always returned as "@". // If the return value ends in a ".", the domain was not the suffix. // origin can end in "." or not. Either way the results should be the same. if s == "" { return "@" } // Someone is using TrimDomainName(s, ".") to remove a dot if it exists. if origin == "." { return strings.TrimSuffix(s, origin) } original := s s = dns.Fqdn(s) origin = dns.Fqdn(origin) if !dns.IsSubDomain(origin, s) { return original } slabels := dns.Split(s) olabels := dns.Split(origin) m := dns.CompareDomainName(s, origin) if len(olabels) == m { if len(olabels) == len(slabels) { return "@" // origin == s } if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) { return "@" // TrimDomainName(".foo.", "foo.") } } // Return the first (len-m) labels: return s[:slabels[len(slabels)-m]-1] } golang-github-miekg-dns-1.1.64/dnsutil/util_test.go000066400000000000000000000076231476742671700222700ustar00rootroot00000000000000package dnsutil import "testing" func TestAddOrigin(t *testing.T) { var tests = []struct{ e1, e2, expected string }{ {"@", "example.com", "example.com"}, {"foo", "example.com", "foo.example.com"}, {"foo.", "example.com", "foo."}, {"@", "example.com.", "example.com."}, {"foo", "example.com.", "foo.example.com."}, {"foo.", "example.com.", "foo."}, {"example.com", ".", "example.com."}, {"example.com.", ".", "example.com."}, // Oddball tests: // In general origin should not be "" or "." but at least // these tests verify we don't crash and will keep results // from changing unexpectedly. {"*.", "", "*."}, {"@", "", "@"}, {"foobar", "", "foobar"}, {"foobar.", "", "foobar."}, {"*.", ".", "*."}, {"@", ".", "."}, {"foobar", ".", "foobar."}, {"foobar.", ".", "foobar."}, } for _, test := range tests { actual := AddOrigin(test.e1, test.e2) if test.expected != actual { t.Errorf("AddOrigin(%#v, %#v) expected %#v, got %#v\n", test.e1, test.e2, test.expected, actual) } } } func TestTrimDomainName(t *testing.T) { // Basic tests. // Try trimming "example.com" and "example.com." from typical use cases. testsEx := []struct{ experiment, expected string }{ {"foo.example.com", "foo"}, {"foo.example.com.", "foo"}, {".foo.example.com", ".foo"}, {".foo.example.com.", ".foo"}, {"*.example.com", "*"}, {"example.com", "@"}, {"example.com.", "@"}, {"com.", "com."}, {"foo.", "foo."}, {"serverfault.com.", "serverfault.com."}, {"serverfault.com", "serverfault.com"}, {".foo.ronco.com", ".foo.ronco.com"}, {".foo.ronco.com.", ".foo.ronco.com."}, } for _, dom := range []string{"example.com", "example.com."} { for i, test := range testsEx { actual := TrimDomainName(test.experiment, dom) if test.expected != actual { t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual) } } } // Paranoid tests. // These test shouldn't be needed but I was weary of off-by-one errors. // In theory, these can't happen because there are no single-letter TLDs, // but it is good to exercise the code this way. tests := []struct{ experiment, expected string }{ {"", "@"}, {".", "."}, {"a.b.c.d.e.f.", "a.b.c.d.e"}, {"b.c.d.e.f.", "b.c.d.e"}, {"c.d.e.f.", "c.d.e"}, {"d.e.f.", "d.e"}, {"e.f.", "e"}, {"f.", "@"}, {".a.b.c.d.e.f.", ".a.b.c.d.e"}, {".b.c.d.e.f.", ".b.c.d.e"}, {".c.d.e.f.", ".c.d.e"}, {".d.e.f.", ".d.e"}, {".e.f.", ".e"}, {".f.", "@"}, {"a.b.c.d.e.f", "a.b.c.d.e"}, {"a.b.c.d.e.", "a.b.c.d.e."}, {"a.b.c.d.e", "a.b.c.d.e"}, {"a.b.c.d.", "a.b.c.d."}, {"a.b.c.d", "a.b.c.d"}, {"a.b.c.", "a.b.c."}, {"a.b.c", "a.b.c"}, {"a.b.", "a.b."}, {"a.b", "a.b"}, {"a.", "a."}, {"a", "a"}, {".a.b.c.d.e.f", ".a.b.c.d.e"}, {".a.b.c.d.e.", ".a.b.c.d.e."}, {".a.b.c.d.e", ".a.b.c.d.e"}, {".a.b.c.d.", ".a.b.c.d."}, {".a.b.c.d", ".a.b.c.d"}, {".a.b.c.", ".a.b.c."}, {".a.b.c", ".a.b.c"}, {".a.b.", ".a.b."}, {".a.b", ".a.b"}, {".a.", ".a."}, {".a", ".a"}, } for _, dom := range []string{"f", "f."} { for i, test := range tests { actual := TrimDomainName(test.experiment, dom) if test.expected != actual { t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual) } } } // Test cases for bugs found in the wild. // These test cases provide both origin, s, and the expected result. // If you find a bug in the while, this is probably the easiest place // to add it as a test case. var testsWild = []struct{ e1, e2, expected string }{ {"mathoverflow.net.", ".", "mathoverflow.net"}, {"mathoverflow.net", ".", "mathoverflow.net"}, {"", ".", "@"}, {"@", ".", "@"}, } for i, test := range testsWild { actual := TrimDomainName(test.e1, test.e2) if test.expected != actual { t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.e1, test.e2, test.expected, actual) } } } golang-github-miekg-dns-1.1.64/doc.go000066400000000000000000000247451476742671700173430ustar00rootroot00000000000000/* Package dns implements a full featured interface to the Domain Name System. Both server- and client-side programming is supported. The package allows complete control over what is sent out to the DNS. The API follows the less-is-more principle, by presenting a small, clean interface. It supports (asynchronous) querying/replying, incoming/outgoing zone transfers, TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing. Note that domain names MUST be fully qualified before sending them, unqualified names in a message will result in a packing failure. Resource records are native types. They are not stored in wire format. Basic usage pattern for creating a new resource record: r := new(dns.MX) r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} r.Preference = 10 r.Mx = "mx.miek.nl." Or directly from a string: mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") Or when the default origin (.) and TTL (3600) and class (IN) suit you: mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl") Or even: mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") In the DNS messages are exchanged, these messages contain resource records (sets). Use pattern for creating a message: m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) Or when not certain if the domain name is fully qualified: m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX) The message m is now a message with the question section set to ask the MX records for the miek.nl. zone. The following is slightly more verbose, but more flexible: m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} After creating a message it can be sent. Basic use pattern for synchronous querying the DNS at a server configured on 127.0.0.1 and port 53: c := new(dns.Client) in, rtt, err := c.Exchange(m1, "127.0.0.1:53") Suppressing multiple outstanding queries (with the same question, type and class) is as easy as setting: c.SingleInflight = true More advanced options are available using a net.Dialer and the corresponding API. For example it is possible to set a timeout, or to specify a source IP address and port to use for the connection: c := new(dns.Client) laddr := net.UDPAddr{ IP: net.ParseIP("[::1]"), Port: 12345, Zone: "", } c.Dialer = &net.Dialer{ Timeout: 200 * time.Millisecond, LocalAddr: &laddr, } in, rtt, err := c.Exchange(m1, "8.8.8.8:53") If these "advanced" features are not needed, a simple UDP query can be sent, with: in, err := dns.Exchange(m1, "127.0.0.1:53") When this functions returns you will get DNS message. A DNS message consists out of four sections. The question section: in.Question, the answer section: in.Answer, the authority section: in.Ns and the additional section: in.Extra. Each of these sections (except the Question section) contain a []RR. Basic use pattern for accessing the rdata of a TXT RR as the first RR in the Answer section: if t, ok := in.Answer[0].(*dns.TXT); ok { // do something with t.Txt } # Domain Name and TXT Character String Representations Both domain names and TXT character strings are converted to presentation form both when unpacked and when converted to strings. For TXT character strings, tabs, carriage returns and line feeds will be converted to \t, \r and \n respectively. Back slashes and quotations marks will be escaped. Bytes below 32 and above 127 will be converted to \DDD form. For domain names, in addition to the above rules brackets, periods, spaces, semicolons and the at symbol are escaped. # DNSSEC DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses public key cryptography to sign resource records. The public keys are stored in DNSKEY records and the signatures in RRSIG records. Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit to a request. m := new(dns.Msg) m.SetEdns0(4096, true) Signature generation, signature verification and key generation are all supported. # DYNAMIC UPDATES Dynamic updates reuses the DNS message format, but renames three of the sections. Question is Zone, Answer is Prerequisite, Authority is Update, only the Additional is not renamed. See RFC 2136 for the gory details. You can set a rather complex set of rules for the existence of absence of certain resource records or names in a zone to specify if resource records should be added or removed. The table from RFC 2136 supplemented with the Go DNS function shows which functions exist to specify the prerequisites. 3.2.4 - Table Of Metavalues Used In Prerequisite Section CLASS TYPE RDATA Meaning Function -------------------------------------------------------------- ANY ANY empty Name is in use dns.NameUsed ANY rrset empty RRset exists (value indep) dns.RRsetUsed NONE ANY empty Name is not in use dns.NameNotUsed NONE rrset empty RRset does not exist dns.RRsetNotUsed zone rrset rr RRset exists (value dep) dns.Used The prerequisite section can also be left empty. If you have decided on the prerequisites you can tell what RRs should be added or deleted. The next table shows the options you have and what functions to call. 3.4.2.6 - Table Of Metavalues Used In Update Section CLASS TYPE RDATA Meaning Function --------------------------------------------------------------- ANY ANY empty Delete all RRsets from name dns.RemoveName ANY rrset empty Delete an RRset dns.RemoveRRset NONE rrset rr Delete an RR from RRset dns.Remove zone rrset rr Add to an RRset dns.Insert # TRANSACTION SIGNATURE An TSIG or transaction signature adds a HMAC TSIG record to each message sent. The supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512. Basic use pattern when querying with a TSIG name "axfr." (note that these key names must be fully qualified - as they are domain names) and the base64 secret "so6ZGir4GPAqINNh9U5c3A==": If an incoming message contains a TSIG record it MUST be the last record in the additional section (RFC2845 3.2). This means that you should make the call to SetTsig last, right before executing the query. If you make any changes to the RRset after calling SetTsig() the signature will be incorrect. c := new(dns.Client) c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix()) ... // When sending the TSIG RR is calculated and filled in before sending When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with TSIG, this is the basic use pattern. In this example we request an AXFR for miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A==" and using the server 176.58.119.54: t := new(dns.Transfer) m := new(dns.Msg) t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} m.SetAxfr("miek.nl.") m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix()) c, err := t.In(m, "176.58.119.54:53") for r := range c { ... } You can now read the records from the transfer as they come in. Each envelope is checked with TSIG. If something is not correct an error is returned. A custom TSIG implementation can be used. This requires additional code to perform any session establishment and signature generation/verification. The client must be configured with an implementation of the TsigProvider interface: type Provider struct{} func (*Provider) Generate(msg []byte, tsig *dns.TSIG) ([]byte, error) { // Use tsig.Hdr.Name and tsig.Algorithm in your code to // generate the MAC using msg as the payload. } func (*Provider) Verify(msg []byte, tsig *dns.TSIG) error { // Use tsig.Hdr.Name and tsig.Algorithm in your code to verify // that msg matches the value in tsig.MAC. } c := new(dns.Client) c.TsigProvider = new(Provider) m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) m.SetTsig(keyname, dns.HmacSHA256, 300, time.Now().Unix()) ... // TSIG RR is calculated by calling your Generate method Basic use pattern validating and replying to a message that has TSIG set. server := &dns.Server{Addr: ":53", Net: "udp"} server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} go server.ListenAndServe() dns.HandleFunc(".", handleRequest) func handleRequest(w dns.ResponseWriter, r *dns.Msg) { m := new(dns.Msg) m.SetReply(r) if r.IsTsig() != nil { if w.TsigStatus() == nil { // *Msg r has an TSIG record and it was validated m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix()) } else { // *Msg r has an TSIG records and it was not validated } } w.WriteMsg(m) } # PRIVATE RRS RFC 6895 sets aside a range of type codes for private use. This range is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these can be used, before requesting an official type code from IANA. See https://miek.nl/2014/september/21/idn-and-private-rr-in-go-dns/ for more information. # EDNS0 EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by RFC 6891. It defines a new RR type, the OPT RR, which is then completely abused. Basic use pattern for creating an (empty) OPT RR: o := new(dns.OPT) o.Hdr.Name = "." // MUST be the root zone, per definition. o.Hdr.Rrtype = dns.TypeOPT The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces. Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and EDNS0_SUBNET (RFC 7871). Note that these options may be combined in an OPT RR. Basic use pattern for a server to check if (and which) options are set: // o is a dns.OPT for _, s := range o.Option { switch e := s.(type) { case *dns.EDNS0_NSID: // do stuff with e.Nsid case *dns.EDNS0_SUBNET: // access e.Family, e.Address, etc. } } SIG(0) From RFC 2931: SIG(0) provides protection for DNS transactions and requests .... ... protection for glue records, DNS requests, protection for message headers on requests and responses, and protection of the overall integrity of a response. It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared secret approach in TSIG. Supported algorithms: ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512. Signing subsequent messages in multi-message sessions is not implemented. */ package dns golang-github-miekg-dns-1.1.64/duplicate.go000066400000000000000000000016711476742671700205410ustar00rootroot00000000000000package dns //go:generate go run duplicate_generate.go // IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL. // So this means the header data is equal *and* the RDATA is the same. Returns true // if so, otherwise false. It's a protocol violation to have identical RRs in a message. func IsDuplicate(r1, r2 RR) bool { // Check whether the record header is identical. if !r1.Header().isDuplicate(r2.Header()) { return false } // Check whether the RDATA is identical. return r1.isDuplicate(r2) } func (r1 *RR_Header) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RR_Header) if !ok { return false } if r1.Class != r2.Class { return false } if r1.Rrtype != r2.Rrtype { return false } if !isDuplicateName(r1.Name, r2.Name) { return false } // ignore TTL return true } // isDuplicateName checks if the domain names s1 and s2 are equal. func isDuplicateName(s1, s2 string) bool { return equal(s1, s2) } golang-github-miekg-dns-1.1.64/duplicate_generate.go000066400000000000000000000104211476742671700224040ustar00rootroot00000000000000//go:build ignore // +build ignore // types_generate.go is meant to run with go generate. It will use // go/{importer,types} to track down all the RR struct types. Then for each type // it will generate conversion tables (TypeToRR and TypeToString) and banal // methods (len, Header, copy) based on the struct tags. The generated source is // written to ztypes.go, and is meant to be checked into git. package main import ( "bytes" "fmt" "go/format" "go/types" "log" "os" "golang.org/x/tools/go/packages" ) var packageHdr = ` // Code generated by "go run duplicate_generate.go"; DO NOT EDIT. package dns ` func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { st, ok := t.Underlying().(*types.Struct) if !ok { return nil, false } if st.NumFields() == 0 { return nil, false } if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { return st, false } if st.Field(0).Anonymous() { st, _ := getTypeStruct(st.Field(0).Type(), scope) return st, true } return nil, false } // loadModule retrieves package description for a given module. func loadModule(name string) (*types.Package, error) { conf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo} pkgs, err := packages.Load(&conf, name) if err != nil { return nil, err } return pkgs[0].Types, nil } func main() { // Import and type-check the package pkg, err := loadModule("github.com/miekg/dns") fatalIfErr(err) scope := pkg.Scope() // Collect actual types (*X) var namedTypes []string for _, name := range scope.Names() { o := scope.Lookup(name) if o == nil || !o.Exported() { continue } if st, _ := getTypeStruct(o.Type(), scope); st == nil { continue } if name == "PrivateRR" || name == "OPT" { continue } namedTypes = append(namedTypes, o.Name()) } b := &bytes.Buffer{} b.WriteString(packageHdr) // Generate the duplicate check for each type. fmt.Fprint(b, "// isDuplicate() functions\n\n") for _, name := range namedTypes { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) fmt.Fprintf(b, "func (r1 *%s) isDuplicate(_r2 RR) bool {\n", name) fmt.Fprintf(b, "r2, ok := _r2.(*%s)\n", name) fmt.Fprint(b, "if !ok { return false }\n") fmt.Fprint(b, "_ = r2\n") for i := 1; i < st.NumFields(); i++ { field := st.Field(i).Name() o2 := func(s string) { fmt.Fprintf(b, s+"\n", field, field) } o3 := func(s string) { fmt.Fprintf(b, s+"\n", field, field, field) } // For some reason, a and aaaa don't pop up as *types.Slice here (mostly like because the are // *indirectly* defined as a slice in the net package). if _, ok := st.Field(i).Type().(*types.Slice); ok { o2("if len(r1.%s) != len(r2.%s) {\nreturn false\n}") if st.Tag(i) == `dns:"cdomain-name"` || st.Tag(i) == `dns:"domain-name"` { o3(`for i := 0; i < len(r1.%s); i++ { if !isDuplicateName(r1.%s[i], r2.%s[i]) { return false } }`) continue } if st.Tag(i) == `dns:"apl"` { o3(`for i := 0; i < len(r1.%s); i++ { if !r1.%s[i].equals(&r2.%s[i]) { return false } }`) continue } if st.Tag(i) == `dns:"pairs"` { o2(`if !areSVCBPairArraysEqual(r1.%s, r2.%s) { return false }`) continue } o3(`for i := 0; i < len(r1.%s); i++ { if r1.%s[i] != r2.%s[i] { return false } }`) continue } switch st.Tag(i) { case `dns:"-"`: // ignored case `dns:"a"`, `dns:"aaaa"`: o2("if !r1.%s.Equal(r2.%s) {\nreturn false\n}") case `dns:"cdomain-name"`, `dns:"domain-name"`: o2("if !isDuplicateName(r1.%s, r2.%s) {\nreturn false\n}") case `dns:"ipsechost"`, `dns:"amtrelayhost"`: o2(`switch r1.GatewayType { case IPSECGatewayIPv4, IPSECGatewayIPv6: if !r1.GatewayAddr.Equal(r2.GatewayAddr) { return false } case IPSECGatewayHost: if !isDuplicateName(r1.%s, r2.%s) { return false } } `) default: o2("if r1.%s != r2.%s {\nreturn false\n}") } } fmt.Fprint(b, "return true\n}\n\n") } // gofmt res, err := format.Source(b.Bytes()) if err != nil { b.WriteTo(os.Stderr) log.Fatal(err) } // write result f, err := os.Create("zduplicate.go") fatalIfErr(err) defer f.Close() f.Write(res) } func fatalIfErr(err error) { if err != nil { log.Fatal(err) } } golang-github-miekg-dns-1.1.64/duplicate_test.go000066400000000000000000000074261476742671700216040ustar00rootroot00000000000000package dns import "testing" func TestDuplicateA(t *testing.T) { a1, _ := NewRR("www.example.org. 2700 IN A 127.0.0.1") a2, _ := NewRR("www.example.org. IN A 127.0.0.1") if !IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } a2, _ = NewRR("www.example.org. IN A 127.0.0.2") if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } } func TestDuplicateTXT(t *testing.T) { a1, _ := NewRR("www.example.org. IN TXT \"aa\"") a2, _ := NewRR("www.example.org. IN TXT \"aa\"") if !IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } a2, _ = NewRR("www.example.org. IN TXT \"aa\" \"bb\"") if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } a1, _ = NewRR("www.example.org. IN TXT \"aa\" \"bc\"") if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } } func TestDuplicateSVCB(t *testing.T) { a1, _ := NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1::3:3:3:3 key65300=\254\032\030\000\ \043,\;`) a2, _ := NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1:0::3:3:3:3 key65300="\254\ \030\000 +\,;"`) if !IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } a2, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1::3:3:3:3 key65300="\255\ \030\000 +\,;"`) if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } a1, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv6hint=1::3:3:3:3`) if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } a2, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv4hint=1.1.1.1`) if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } a1, _ = NewRR(`example.com. 3600 IN SVCB 1 . ipv4hint=1.1.1.1,1.0.2.1`) if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s not to be duplicates, but got true", a1.String(), a2.String()) } } func TestDuplicateOwner(t *testing.T) { a1, _ := NewRR("www.example.org. IN A 127.0.0.1") a2, _ := NewRR("www.example.org. IN A 127.0.0.1") if !IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } a2, _ = NewRR("WWw.exaMPle.org. IN A 127.0.0.2") if IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } } func TestDuplicateDomain(t *testing.T) { a1, _ := NewRR("www.example.org. IN CNAME example.org.") a2, _ := NewRR("www.example.org. IN CNAME example.org.") if !IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } a2, _ = NewRR("www.example.org. IN CNAME exAMPLe.oRG.") if !IsDuplicate(a1, a2) { t.Errorf("expected %s/%s to be duplicates, but got false", a1.String(), a2.String()) } } func TestDuplicateWrongRrtype(t *testing.T) { // Test that IsDuplicate won't panic for a record that's lying about // it's Rrtype. r1 := &A{Hdr: RR_Header{Rrtype: TypeA}} r2 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}} if IsDuplicate(r1, r2) { t.Errorf("expected %s/%s not to be duplicates, but got true", r1.String(), r2.String()) } r3 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}} r4 := &A{Hdr: RR_Header{Rrtype: TypeA}} if IsDuplicate(r3, r4) { t.Errorf("expected %s/%s not to be duplicates, but got true", r3.String(), r4.String()) } r5 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}} r6 := &AAAA{Hdr: RR_Header{Rrtype: TypeA}} if !IsDuplicate(r5, r6) { t.Errorf("expected %s/%s to be duplicates, but got false", r5.String(), r6.String()) } } golang-github-miekg-dns-1.1.64/dyn_test.go000066400000000000000000000000451476742671700204120ustar00rootroot00000000000000package dns // Find better solution golang-github-miekg-dns-1.1.64/edns.go000066400000000000000000000646301476742671700175240ustar00rootroot00000000000000package dns import ( "encoding/binary" "encoding/hex" "errors" "fmt" "net" "strconv" ) // EDNS0 Option codes. const ( EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt EDNS0NSID = 0x3 // nsid (See RFC 5001) EDNS0ESU = 0x4 // ENUM Source-URI draft: https://datatracker.ietf.org/doc/html/draft-kaplan-enum-source-uri-00 EDNS0DAU = 0x5 // DNSSEC Algorithm Understood EDNS0DHU = 0x6 // DS Hash Understood EDNS0N3U = 0x7 // NSEC3 Hash Understood EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871) EDNS0EXPIRE = 0x9 // EDNS0 expire EDNS0COOKIE = 0xa // EDNS0 Cookie EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914) EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) _DO = 1 << 15 // DNSSEC OK _CO = 1 << 14 // Compact Answers OK ) // makeDataOpt is used to unpack the EDNS0 option(s) from a message. func makeDataOpt(code uint16) EDNS0 { // All the EDNS0.* constants above need to be in this switch. switch code { case EDNS0LLQ: return new(EDNS0_LLQ) case EDNS0UL: return new(EDNS0_UL) case EDNS0NSID: return new(EDNS0_NSID) case EDNS0DAU: return new(EDNS0_DAU) case EDNS0DHU: return new(EDNS0_DHU) case EDNS0N3U: return new(EDNS0_N3U) case EDNS0SUBNET: return new(EDNS0_SUBNET) case EDNS0EXPIRE: return new(EDNS0_EXPIRE) case EDNS0COOKIE: return new(EDNS0_COOKIE) case EDNS0TCPKEEPALIVE: return new(EDNS0_TCP_KEEPALIVE) case EDNS0PADDING: return new(EDNS0_PADDING) case EDNS0EDE: return new(EDNS0_EDE) case EDNS0ESU: return new(EDNS0_ESU) default: e := new(EDNS0_LOCAL) e.Code = code return e } } // OPT is the EDNS0 RR appended to messages to convey extra (meta) information. See RFC 6891. type OPT struct { Hdr RR_Header Option []EDNS0 `dns:"opt"` } func (rr *OPT) String() string { s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; " if rr.Do() { if rr.Co() { s += "flags: do, co; " } else { s += "flags: do; " } } else { s += "flags:; " } if rr.Hdr.Ttl&0x7FFF != 0 { s += fmt.Sprintf("MBZ: 0x%04x, ", rr.Hdr.Ttl&0x7FFF) } s += "udp: " + strconv.Itoa(int(rr.UDPSize())) for _, o := range rr.Option { switch o.(type) { case *EDNS0_NSID: s += "\n; NSID: " + o.String() h, e := o.pack() var r string if e == nil { for _, c := range h { r += "(" + string(c) + ")" } s += " " + r } case *EDNS0_SUBNET: s += "\n; SUBNET: " + o.String() case *EDNS0_COOKIE: s += "\n; COOKIE: " + o.String() case *EDNS0_EXPIRE: s += "\n; EXPIRE: " + o.String() case *EDNS0_TCP_KEEPALIVE: s += "\n; KEEPALIVE: " + o.String() case *EDNS0_UL: s += "\n; UPDATE LEASE: " + o.String() case *EDNS0_LLQ: s += "\n; LONG LIVED QUERIES: " + o.String() case *EDNS0_DAU: s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String() case *EDNS0_DHU: s += "\n; DS HASH UNDERSTOOD: " + o.String() case *EDNS0_N3U: s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String() case *EDNS0_LOCAL: s += "\n; LOCAL OPT: " + o.String() case *EDNS0_PADDING: s += "\n; PADDING: " + o.String() case *EDNS0_EDE: s += "\n; EDE: " + o.String() case *EDNS0_ESU: s += "\n; ESU: " + o.String() } } return s } func (rr *OPT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, o := range rr.Option { l += 4 // Account for 2-byte option code and 2-byte option length. lo, _ := o.pack() l += len(lo) } return l } func (*OPT) parse(c *zlexer, origin string) *ParseError { return &ParseError{err: "OPT records do not have a presentation format"} } func (rr *OPT) isDuplicate(r2 RR) bool { return false } // Version returns the EDNS version used. Only zero is defined. func (rr *OPT) Version() uint8 { return uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16) } // SetVersion sets the version of EDNS. This is usually zero. func (rr *OPT) SetVersion(v uint8) { rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)<<16 } // ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). func (rr *OPT) ExtendedRcode() int { return int(rr.Hdr.Ttl&0xFF000000>>24) << 4 } // SetExtendedRcode sets the EDNS extended RCODE field. // // If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0. func (rr *OPT) SetExtendedRcode(v uint16) { rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24 } // UDPSize returns the UDP buffer size. func (rr *OPT) UDPSize() uint16 { return rr.Hdr.Class } // SetUDPSize sets the UDP buffer size. func (rr *OPT) SetUDPSize(size uint16) { rr.Hdr.Class = size } // Do returns the value of the DO (DNSSEC OK) bit. func (rr *OPT) Do() bool { return rr.Hdr.Ttl&_DO == _DO } // SetDo sets the DO (DNSSEC OK) bit. // If we pass an argument, set the DO bit to that value. // It is possible to pass 2 or more arguments, but they will be ignored. func (rr *OPT) SetDo(do ...bool) { if len(do) == 1 { if do[0] { rr.Hdr.Ttl |= _DO } else { rr.Hdr.Ttl &^= _DO } } else { rr.Hdr.Ttl |= _DO } } // Co returns the value of the CO (Compact Answers OK) bit. func (rr *OPT) Co() bool { return rr.Hdr.Ttl&_CO == _CO } // SetCo sets the CO (Compact Answers OK) bit. // If we pass an argument, set the CO bit to that value. // It is possible to pass 2 or more arguments, but they will be ignored. func (rr *OPT) SetCo(co ...bool) { if len(co) == 1 { if co[0] { rr.Hdr.Ttl |= _CO } else { rr.Hdr.Ttl &^= _CO } } else { rr.Hdr.Ttl |= _CO } } // Z returns the Z part of the OPT RR as a uint16 with only the 14 least significant bits used. func (rr *OPT) Z() uint16 { return uint16(rr.Hdr.Ttl & 0x3FFF) } // SetZ sets the Z part of the OPT RR, note only the 14 least significant bits of z are used. func (rr *OPT) SetZ(z uint16) { rr.Hdr.Ttl = rr.Hdr.Ttl&^0x3FFF | uint32(z&0x3FFF) } // EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it. type EDNS0 interface { // Option returns the option code for the option. Option() uint16 // pack returns the bytes of the option data. pack() ([]byte, error) // unpack sets the data as found in the buffer. Is also sets // the length of the slice as the length of the option data. unpack([]byte) error // String returns the string representation of the option. String() string // copy returns a deep-copy of the option. copy() EDNS0 } // EDNS0_NSID option is used to retrieve a nameserver // identifier. When sending a request Nsid must be set to the empty string // The identifier is an opaque string encoded as hex. // Basic use pattern for creating an nsid option: // // o := new(dns.OPT) // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_NSID) // e.Code = dns.EDNS0NSID // e.Nsid = "AA" // o.Option = append(o.Option, e) type EDNS0_NSID struct { Code uint16 // always EDNS0NSID Nsid string // string needs to be hex encoded } func (e *EDNS0_NSID) pack() ([]byte, error) { h, err := hex.DecodeString(e.Nsid) if err != nil { return nil, err } return h, nil } // Option implements the EDNS0 interface. func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code. func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil } func (e *EDNS0_NSID) String() string { return e.Nsid } func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} } // EDNS0_SUBNET is the subnet option that is used to give the remote nameserver // an idea of where the client lives. See RFC 7871. It can then give back a different // answer depending on the location or network topology. // Basic use pattern for creating an subnet option: // // o := new(dns.OPT) // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_SUBNET) // e.Code = dns.EDNS0SUBNET // by default this is filled in through unpacking OPT packets (unpackDataOpt) // e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 // e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6 // e.SourceScope = 0 // e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 // // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 // o.Option = append(o.Option, e) // // This code will parse all the available bits when unpacking (up to optlen). // When packing it will apply SourceNetmask. If you need more advanced logic, // patches welcome and good luck. type EDNS0_SUBNET struct { Code uint16 // always EDNS0SUBNET Family uint16 // 1 for IP, 2 for IP6 SourceNetmask uint8 SourceScope uint8 Address net.IP } // Option implements the EDNS0 interface. func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET } func (e *EDNS0_SUBNET) pack() ([]byte, error) { b := make([]byte, 4) binary.BigEndian.PutUint16(b[0:], e.Family) b[2] = e.SourceNetmask b[3] = e.SourceScope switch e.Family { case 0: // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 // We might don't need to complain either if e.SourceNetmask != 0 { return nil, errors.New("dns: bad address family") } case 1: if e.SourceNetmask > net.IPv4len*8 { return nil, errors.New("dns: bad netmask") } if len(e.Address.To4()) != net.IPv4len { return nil, errors.New("dns: bad address") } ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up b = append(b, ip[:needLength]...) case 2: if e.SourceNetmask > net.IPv6len*8 { return nil, errors.New("dns: bad netmask") } if len(e.Address) != net.IPv6len { return nil, errors.New("dns: bad address") } ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up b = append(b, ip[:needLength]...) default: return nil, errors.New("dns: bad address family") } return b, nil } func (e *EDNS0_SUBNET) unpack(b []byte) error { if len(b) < 4 { return ErrBuf } e.Family = binary.BigEndian.Uint16(b) e.SourceNetmask = b[2] e.SourceScope = b[3] switch e.Family { case 0: // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 // It's okay to accept such a packet if e.SourceNetmask != 0 { return errors.New("dns: bad address family") } e.Address = net.IPv4(0, 0, 0, 0) case 1: if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 { return errors.New("dns: bad netmask") } addr := make(net.IP, net.IPv4len) copy(addr, b[4:]) e.Address = addr.To16() case 2: if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 { return errors.New("dns: bad netmask") } addr := make(net.IP, net.IPv6len) copy(addr, b[4:]) e.Address = addr default: return errors.New("dns: bad address family") } return nil } func (e *EDNS0_SUBNET) String() (s string) { if e.Address == nil { s = "" } else if e.Address.To4() != nil { s = e.Address.String() } else { s = "[" + e.Address.String() + "]" } s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope)) return } func (e *EDNS0_SUBNET) copy() EDNS0 { return &EDNS0_SUBNET{ e.Code, e.Family, e.SourceNetmask, e.SourceScope, e.Address, } } // The EDNS0_COOKIE option is used to add a DNS Cookie to a message. // // o := new(dns.OPT) // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_COOKIE) // e.Code = dns.EDNS0COOKIE // e.Cookie = "24a5ac.." // o.Option = append(o.Option, e) // // The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is // always 8 bytes. It may then optionally be followed by the server cookie. The server // cookie is of variable length, 8 to a maximum of 32 bytes. In other words: // // cCookie := o.Cookie[:16] // sCookie := o.Cookie[16:] // // There is no guarantee that the Cookie string has a specific length. type EDNS0_COOKIE struct { Code uint16 // always EDNS0COOKIE Cookie string // hex encoded cookie data } func (e *EDNS0_COOKIE) pack() ([]byte, error) { h, err := hex.DecodeString(e.Cookie) if err != nil { return nil, err } return h, nil } // Option implements the EDNS0 interface. func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE } func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil } func (e *EDNS0_COOKIE) String() string { return e.Cookie } func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.Cookie} } // The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set // an expiration on an update RR. This is helpful for clients that cannot clean // up after themselves. This is a draft RFC and more information can be found at // https://tools.ietf.org/html/draft-sekar-dns-ul-02 // // o := new(dns.OPT) // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_UL) // e.Code = dns.EDNS0UL // e.Lease = 120 // in seconds // o.Option = append(o.Option, e) type EDNS0_UL struct { Code uint16 // always EDNS0UL Lease uint32 KeyLease uint32 } // Option implements the EDNS0 interface. func (e *EDNS0_UL) Option() uint16 { return EDNS0UL } func (e *EDNS0_UL) String() string { return fmt.Sprintf("%d %d", e.Lease, e.KeyLease) } func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} } // Copied: http://golang.org/src/pkg/net/dnsmsg.go func (e *EDNS0_UL) pack() ([]byte, error) { var b []byte if e.KeyLease == 0 { b = make([]byte, 4) } else { b = make([]byte, 8) binary.BigEndian.PutUint32(b[4:], e.KeyLease) } binary.BigEndian.PutUint32(b, e.Lease) return b, nil } func (e *EDNS0_UL) unpack(b []byte) error { switch len(b) { case 4: e.KeyLease = 0 case 8: e.KeyLease = binary.BigEndian.Uint32(b[4:]) default: return ErrBuf } e.Lease = binary.BigEndian.Uint32(b) return nil } // EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 // Implemented for completeness, as the EDNS0 type code is assigned. type EDNS0_LLQ struct { Code uint16 // always EDNS0LLQ Version uint16 Opcode uint16 Error uint16 Id uint64 LeaseLife uint32 } // Option implements the EDNS0 interface. func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ } func (e *EDNS0_LLQ) pack() ([]byte, error) { b := make([]byte, 18) binary.BigEndian.PutUint16(b[0:], e.Version) binary.BigEndian.PutUint16(b[2:], e.Opcode) binary.BigEndian.PutUint16(b[4:], e.Error) binary.BigEndian.PutUint64(b[6:], e.Id) binary.BigEndian.PutUint32(b[14:], e.LeaseLife) return b, nil } func (e *EDNS0_LLQ) unpack(b []byte) error { if len(b) < 18 { return ErrBuf } e.Version = binary.BigEndian.Uint16(b[0:]) e.Opcode = binary.BigEndian.Uint16(b[2:]) e.Error = binary.BigEndian.Uint16(b[4:]) e.Id = binary.BigEndian.Uint64(b[6:]) e.LeaseLife = binary.BigEndian.Uint32(b[14:]) return nil } func (e *EDNS0_LLQ) String() string { s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) + " " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(e.Id, 10) + " " + strconv.FormatUint(uint64(e.LeaseLife), 10) return s } func (e *EDNS0_LLQ) copy() EDNS0 { return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife} } // EDNS0_DAU implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975. type EDNS0_DAU struct { Code uint16 // always EDNS0DAU AlgCode []uint8 } // Option implements the EDNS0 interface. func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU } func (e *EDNS0_DAU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_DAU) String() string { s := "" for _, alg := range e.AlgCode { if a, ok := AlgorithmToString[alg]; ok { s += " " + a } else { s += " " + strconv.Itoa(int(alg)) } } return s } func (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} } // EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975. type EDNS0_DHU struct { Code uint16 // always EDNS0DHU AlgCode []uint8 } // Option implements the EDNS0 interface. func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU } func (e *EDNS0_DHU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_DHU) String() string { s := "" for _, alg := range e.AlgCode { if a, ok := HashToString[alg]; ok { s += " " + a } else { s += " " + strconv.Itoa(int(alg)) } } return s } func (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} } // EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975. type EDNS0_N3U struct { Code uint16 // always EDNS0N3U AlgCode []uint8 } // Option implements the EDNS0 interface. func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U } func (e *EDNS0_N3U) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_N3U) String() string { // Re-use the hash map s := "" for _, alg := range e.AlgCode { if a, ok := HashToString[alg]; ok { s += " " + a } else { s += " " + strconv.Itoa(int(alg)) } } return s } func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} } // EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314. type EDNS0_EXPIRE struct { Code uint16 // always EDNS0EXPIRE Expire uint32 Empty bool // Empty is used to signal an empty Expire option in a backwards compatible way, it's not used on the wire. } // Option implements the EDNS0 interface. func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE } func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire, e.Empty} } func (e *EDNS0_EXPIRE) pack() ([]byte, error) { if e.Empty { return []byte{}, nil } b := make([]byte, 4) binary.BigEndian.PutUint32(b, e.Expire) return b, nil } func (e *EDNS0_EXPIRE) unpack(b []byte) error { if len(b) == 0 { // zero-length EXPIRE query, see RFC 7314 Section 2 e.Empty = true return nil } if len(b) < 4 { return ErrBuf } e.Expire = binary.BigEndian.Uint32(b) e.Empty = false return nil } func (e *EDNS0_EXPIRE) String() (s string) { if e.Empty { return "" } return strconv.FormatUint(uint64(e.Expire), 10) } // The EDNS0_LOCAL option is used for local/experimental purposes. The option // code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND] // (RFC6891), although any unassigned code can actually be used. The content of // the option is made available in Data, unaltered. // Basic use pattern for creating a local option: // // o := new(dns.OPT) // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_LOCAL) // e.Code = dns.EDNS0LOCALSTART // e.Data = []byte{72, 82, 74} // o.Option = append(o.Option, e) type EDNS0_LOCAL struct { Code uint16 Data []byte } // Option implements the EDNS0 interface. func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } func (e *EDNS0_LOCAL) String() string { return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) } func (e *EDNS0_LOCAL) copy() EDNS0 { return &EDNS0_LOCAL{e.Code, cloneSlice(e.Data)} } func (e *EDNS0_LOCAL) pack() ([]byte, error) { return cloneSlice(e.Data), nil } func (e *EDNS0_LOCAL) unpack(b []byte) error { e.Data = cloneSlice(b) return nil } // EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep // the TCP connection alive. See RFC 7828. type EDNS0_TCP_KEEPALIVE struct { Code uint16 // always EDNSTCPKEEPALIVE // Timeout is an idle timeout value for the TCP connection, specified in // units of 100 milliseconds, encoded in network byte order. If set to 0, // pack will return a nil slice. Timeout uint16 // Length is the option's length. // Deprecated: this field is deprecated and is always equal to 0. Length uint16 } // Option implements the EDNS0 interface. func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE } func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) { if e.Timeout > 0 { b := make([]byte, 2) binary.BigEndian.PutUint16(b, e.Timeout) return b, nil } return nil, nil } func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error { switch len(b) { case 0: case 2: e.Timeout = binary.BigEndian.Uint16(b) default: return fmt.Errorf("dns: length mismatch, want 0/2 but got %d", len(b)) } return nil } func (e *EDNS0_TCP_KEEPALIVE) String() string { s := "use tcp keep-alive" if e.Timeout == 0 { s += ", timeout omitted" } else { s += fmt.Sprintf(", timeout %dms", e.Timeout*100) } return s } func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Timeout, e.Length} } // EDNS0_PADDING option is used to add padding to a request/response. The default // value of padding SHOULD be 0x0 but other values MAY be used, for instance if // compression is applied before encryption which may break signatures. type EDNS0_PADDING struct { Padding []byte } // Option implements the EDNS0 interface. func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING } func (e *EDNS0_PADDING) pack() ([]byte, error) { return cloneSlice(e.Padding), nil } func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = cloneSlice(b); return nil } func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) } func (e *EDNS0_PADDING) copy() EDNS0 { return &EDNS0_PADDING{cloneSlice(e.Padding)} } // Extended DNS Error Codes (RFC 8914). const ( ExtendedErrorCodeOther uint16 = iota ExtendedErrorCodeUnsupportedDNSKEYAlgorithm ExtendedErrorCodeUnsupportedDSDigestType ExtendedErrorCodeStaleAnswer ExtendedErrorCodeForgedAnswer ExtendedErrorCodeDNSSECIndeterminate ExtendedErrorCodeDNSBogus ExtendedErrorCodeSignatureExpired ExtendedErrorCodeSignatureNotYetValid ExtendedErrorCodeDNSKEYMissing ExtendedErrorCodeRRSIGsMissing ExtendedErrorCodeNoZoneKeyBitSet ExtendedErrorCodeNSECMissing ExtendedErrorCodeCachedError ExtendedErrorCodeNotReady ExtendedErrorCodeBlocked ExtendedErrorCodeCensored ExtendedErrorCodeFiltered ExtendedErrorCodeProhibited ExtendedErrorCodeStaleNXDOMAINAnswer ExtendedErrorCodeNotAuthoritative ExtendedErrorCodeNotSupported ExtendedErrorCodeNoReachableAuthority ExtendedErrorCodeNetworkError ExtendedErrorCodeInvalidData ExtendedErrorCodeSignatureExpiredBeforeValid ExtendedErrorCodeTooEarly ExtendedErrorCodeUnsupportedNSEC3IterValue ExtendedErrorCodeUnableToConformToPolicy ExtendedErrorCodeSynthesized ExtendedErrorCodeInvalidQueryType ) // ExtendedErrorCodeToString maps extended error info codes to a human readable // description. var ExtendedErrorCodeToString = map[uint16]string{ ExtendedErrorCodeOther: "Other", ExtendedErrorCodeUnsupportedDNSKEYAlgorithm: "Unsupported DNSKEY Algorithm", ExtendedErrorCodeUnsupportedDSDigestType: "Unsupported DS Digest Type", ExtendedErrorCodeStaleAnswer: "Stale Answer", ExtendedErrorCodeForgedAnswer: "Forged Answer", ExtendedErrorCodeDNSSECIndeterminate: "DNSSEC Indeterminate", ExtendedErrorCodeDNSBogus: "DNSSEC Bogus", ExtendedErrorCodeSignatureExpired: "Signature Expired", ExtendedErrorCodeSignatureNotYetValid: "Signature Not Yet Valid", ExtendedErrorCodeDNSKEYMissing: "DNSKEY Missing", ExtendedErrorCodeRRSIGsMissing: "RRSIGs Missing", ExtendedErrorCodeNoZoneKeyBitSet: "No Zone Key Bit Set", ExtendedErrorCodeNSECMissing: "NSEC Missing", ExtendedErrorCodeCachedError: "Cached Error", ExtendedErrorCodeNotReady: "Not Ready", ExtendedErrorCodeBlocked: "Blocked", ExtendedErrorCodeCensored: "Censored", ExtendedErrorCodeFiltered: "Filtered", ExtendedErrorCodeProhibited: "Prohibited", ExtendedErrorCodeStaleNXDOMAINAnswer: "Stale NXDOMAIN Answer", ExtendedErrorCodeNotAuthoritative: "Not Authoritative", ExtendedErrorCodeNotSupported: "Not Supported", ExtendedErrorCodeNoReachableAuthority: "No Reachable Authority", ExtendedErrorCodeNetworkError: "Network Error", ExtendedErrorCodeInvalidData: "Invalid Data", ExtendedErrorCodeSignatureExpiredBeforeValid: "Signature Expired Before Valid", ExtendedErrorCodeTooEarly: "Too Early", ExtendedErrorCodeUnsupportedNSEC3IterValue: "Unsupported NSEC3 Iterations Value", ExtendedErrorCodeUnableToConformToPolicy: "Unable To Conform To Policy", ExtendedErrorCodeSynthesized: "Synthesized", ExtendedErrorCodeInvalidQueryType: "Invalid Query Type", } // StringToExtendedErrorCode is a map from human readable descriptions to // extended error info codes. var StringToExtendedErrorCode = reverseInt16(ExtendedErrorCodeToString) // EDNS0_EDE option is used to return additional information about the cause of // DNS errors. type EDNS0_EDE struct { InfoCode uint16 ExtraText string } // Option implements the EDNS0 interface. func (e *EDNS0_EDE) Option() uint16 { return EDNS0EDE } func (e *EDNS0_EDE) copy() EDNS0 { return &EDNS0_EDE{e.InfoCode, e.ExtraText} } func (e *EDNS0_EDE) String() string { info := strconv.FormatUint(uint64(e.InfoCode), 10) if s, ok := ExtendedErrorCodeToString[e.InfoCode]; ok { info += fmt.Sprintf(" (%s)", s) } return fmt.Sprintf("%s: (%s)", info, e.ExtraText) } func (e *EDNS0_EDE) pack() ([]byte, error) { b := make([]byte, 2+len(e.ExtraText)) binary.BigEndian.PutUint16(b[0:], e.InfoCode) copy(b[2:], e.ExtraText) return b, nil } func (e *EDNS0_EDE) unpack(b []byte) error { if len(b) < 2 { return ErrBuf } e.InfoCode = binary.BigEndian.Uint16(b[0:]) e.ExtraText = string(b[2:]) return nil } // The EDNS0_ESU option for ENUM Source-URI Extension. type EDNS0_ESU struct { Code uint16 // always EDNS0ESU Uri string } func (e *EDNS0_ESU) Option() uint16 { return EDNS0ESU } func (e *EDNS0_ESU) String() string { return e.Uri } func (e *EDNS0_ESU) copy() EDNS0 { return &EDNS0_ESU{e.Code, e.Uri} } func (e *EDNS0_ESU) pack() ([]byte, error) { return []byte(e.Uri), nil } func (e *EDNS0_ESU) unpack(b []byte) error { e.Uri = string(b) return nil } golang-github-miekg-dns-1.1.64/edns_test.go000066400000000000000000000157361476742671700205660ustar00rootroot00000000000000package dns import ( "bytes" "net" "testing" ) func TestOPTTtl(t *testing.T) { e := &OPT{} e.Hdr.Name = "." e.Hdr.Rrtype = TypeOPT // verify the default setting of DO=0 if e.Do() { t.Errorf("DO bit should be zero") } // There are 6 possible invocations of SetDo(): // // 1. Starting with DO=0, using SetDo() // 2. Starting with DO=0, using SetDo(true) // 3. Starting with DO=0, using SetDo(false) // 4. Starting with DO=1, using SetDo() // 5. Starting with DO=1, using SetDo(true) // 6. Starting with DO=1, using SetDo(false) // verify that invoking SetDo() sets DO=1 (TEST #1) e.SetDo() if !e.Do() { t.Errorf("DO bit should be non-zero") } // verify that using SetDo(true) works when DO=1 (TEST #5) e.SetDo(true) if !e.Do() { t.Errorf("DO bit should still be non-zero") } // verify that we can use SetDo(false) to set DO=0 (TEST #6) e.SetDo(false) if e.Do() { t.Errorf("DO bit should be zero") } // verify that if we call SetDo(false) when DO=0 that it is unchanged (TEST #3) e.SetDo(false) if e.Do() { t.Errorf("DO bit should still be zero") } // verify that using SetDo(true) works for DO=0 (TEST #2) e.SetDo(true) if !e.Do() { t.Errorf("DO bit should be non-zero") } // verify that using SetDo() works for DO=1 (TEST #4) e.SetDo() if !e.Do() { t.Errorf("DO bit should be non-zero") } // CO (Compact ANswers OK) flag tests follow the same pattern as DO tests // verify that invoking SetCo() sets CO=1 e.SetCo() if !e.Co() { t.Errorf("CO bit should be non-zero") } // verify that using SetCo(true) works when CO=1 e.SetCo(true) if !e.Co() { t.Errorf("CO bit should still be non-zero") } // verify that we can use SetCo(false) to set CO=0 e.SetCo(false) if e.Co() { t.Errorf("CO bit should be zero") } // verify that if we call SetCo(false) when CO=0 that it is unchanged e.SetCo(false) if e.Co() { t.Errorf("CO bit should still be zero") } // verify that using SetCo(true) works for CO=0 e.SetCo(true) if !e.Co() { t.Errorf("CO bit should be non-zero") } // verify that using SetCo() works for CO=1 e.SetCo() if !e.Co() { t.Errorf("CO bit should be non-zero") } if e.Version() != 0 { t.Errorf("version should be non-zero") } e.SetVersion(42) if e.Version() != 42 { t.Errorf("set 42, expected %d, got %d", 42, e.Version()) } e.SetExtendedRcode(42) // ExtendedRcode has the last 4 bits set to 0. if e.ExtendedRcode() != 42&0xFFFFFFF0 { t.Errorf("set 42, expected %d, got %d", 42&0xFFFFFFF0, e.ExtendedRcode()) } // This will reset the 8 upper bits of the extended rcode e.SetExtendedRcode(RcodeNotAuth) if e.ExtendedRcode() != 0 { t.Errorf("Setting a non-extended rcode is expected to set extended rcode to 0, got: %d", e.ExtendedRcode()) } } func TestEDNS0_SUBNETUnpack(t *testing.T) { for _, ip := range []net.IP{ net.IPv4(0xde, 0xad, 0xbe, 0xef), net.ParseIP("192.0.2.1"), net.ParseIP("2001:db8::68"), } { var s1 EDNS0_SUBNET s1.Address = ip if ip.To4() == nil { s1.Family = 2 s1.SourceNetmask = net.IPv6len * 8 } else { s1.Family = 1 s1.SourceNetmask = net.IPv4len * 8 } b, err := s1.pack() if err != nil { t.Fatalf("failed to pack: %v", err) } var s2 EDNS0_SUBNET if err := s2.unpack(b); err != nil { t.Fatalf("failed to unpack: %v", err) } if !ip.Equal(s2.Address) { t.Errorf("address different after unpacking; expected %s, got %s", ip, s2.Address) } } } func TestEDNS0_UL(t *testing.T) { cases := []struct { l uint32 kl uint32 }{ {0x01234567, 0}, {0x76543210, 0xFEDCBA98}, } for _, c := range cases { expect := EDNS0_UL{EDNS0UL, c.l, c.kl} b, err := expect.pack() if err != nil { t.Fatalf("failed to pack: %v", err) } actual := EDNS0_UL{EDNS0UL, ^uint32(0), ^uint32(0)} if err := actual.unpack(b); err != nil { t.Fatalf("failed to unpack: %v", err) } if expect != actual { t.Errorf("unpacked option is different; expected %v, got %v", expect, actual) } } } func TestZ(t *testing.T) { e := &OPT{} e.Hdr.Name = "." e.Hdr.Rrtype = TypeOPT e.SetVersion(8) e.SetDo() e.SetCo() if e.Z() != 0 { t.Errorf("expected Z of 0, got %d", e.Z()) } e.SetZ(5) if e.Z() != 5 { t.Errorf("expected Z of 5, got %d", e.Z()) } e.SetZ(0xFFFF) if e.Z() != 0x3FFF { t.Errorf("expected Z of 0x3FFFF, got %d", e.Z()) } if e.Version() != 8 { t.Errorf("expected version to still be 8, got %d", e.Version()) } if !e.Do() { t.Error("expected DO to be set") } } func TestEDNS0_ESU(t *testing.T) { p := []byte{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x29, 0x04, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x04, 0x00, 0x24, 0x73, 0x69, 0x70, 0x3A, 0x2B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x3B, 0x75, 0x73, 0x65, 0x72, 0x3D, 0x63, 0x67, 0x72, 0x61, 0x74, 0x65, 0x73, } m := new(Msg) if err := m.Unpack(p); err != nil { t.Fatalf("failed to unpack: %v", err) } opt := m.IsEdns0() if opt == nil { t.Fatalf("expected edns0 option") } if len(opt.Option) != 1 { t.Fatalf("expected only one option: %v", opt.Option) } edns0 := opt.Option[0] esu, ok := edns0.(*EDNS0_ESU) if !ok { t.Fatalf("expected option of type EDNS0_ESU, got %t", edns0) } expect := "sip:+123456789@test.com;user=cgrates" if esu.Uri != expect { t.Errorf("unpacked option is different; expected %v, got %v", expect, esu.Uri) } } func TestEDNS0_TCP_KEEPALIVE_unpack(t *testing.T) { cases := []struct { name string b []byte expected uint16 expectedErr bool }{ { name: "empty", b: []byte{}, expected: 0, }, { name: "timeout 1", b: []byte{0, 1}, expected: 1, }, { name: "invalid", b: []byte{0, 1, 3}, expectedErr: true, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { e := &EDNS0_TCP_KEEPALIVE{} err := e.unpack(tc.b) if err != nil && !tc.expectedErr { t.Error("failed to unpack, expected no error") } if err == nil && tc.expectedErr { t.Error("unpacked, but expected an error") } if e.Timeout != tc.expected { t.Errorf("invalid timeout, actual: %d, expected: %d", e.Timeout, tc.expected) } }) } } func TestEDNS0_TCP_KEEPALIVE_pack(t *testing.T) { cases := []struct { name string edns *EDNS0_TCP_KEEPALIVE expected []byte }{ { name: "empty", edns: &EDNS0_TCP_KEEPALIVE{ Code: EDNS0TCPKEEPALIVE, Timeout: 0, }, expected: nil, }, { name: "timeout 1", edns: &EDNS0_TCP_KEEPALIVE{ Code: EDNS0TCPKEEPALIVE, Timeout: 1, }, expected: []byte{0, 1}, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { b, err := tc.edns.pack() if err != nil { t.Error("expected no error") } if tc.expected == nil && b != nil { t.Errorf("invalid result, expected nil") } res := bytes.Compare(b, tc.expected) if res != 0 { t.Errorf("invalid result, expected: %v, actual: %v", tc.expected, b) } }) } } golang-github-miekg-dns-1.1.64/example_test.go000066400000000000000000000064071476742671700212630ustar00rootroot00000000000000package dns_test import ( "errors" "fmt" "log" "net" "github.com/miekg/dns" ) // Retrieve the MX records for miek.nl. func ExampleMX() { config, _ := dns.ClientConfigFromFile("/etc/resolv.conf") c := new(dns.Client) m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) m.RecursionDesired = true r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port)) if err != nil { return } if r.Rcode != dns.RcodeSuccess { return } for _, a := range r.Answer { if mx, ok := a.(*dns.MX); ok { fmt.Printf("%s\n", mx.String()) } } } // Retrieve the DNSKEY records of a zone and convert them // to DS records for SHA1, SHA256 and SHA384. func ExampleDS() { config, _ := dns.ClientConfigFromFile("/etc/resolv.conf") c := new(dns.Client) m := new(dns.Msg) zone := "miek.nl" m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY) m.SetEdns0(4096, true) r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port)) if err != nil { return } if r.Rcode != dns.RcodeSuccess { return } for _, k := range r.Answer { if key, ok := k.(*dns.DNSKEY); ok { for _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} { fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags) } } } } const TypeAPAIR = 0x0F99 type APAIR struct { addr [2]net.IP } func NewAPAIR() dns.PrivateRdata { return new(APAIR) } func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() } func (rd *APAIR) Parse(txt []string) error { if len(txt) != 2 { return errors.New("two addresses required for APAIR") } for i, s := range txt { ip := net.ParseIP(s) if ip == nil { return errors.New("invalid IP in APAIR text representation") } rd.addr[i] = ip } return nil } func (rd *APAIR) Pack(buf []byte) (int, error) { b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...) n := copy(buf, b) if n != len(b) { return n, dns.ErrBuf } return n, nil } func (rd *APAIR) Unpack(buf []byte) (int, error) { ln := net.IPv4len * 2 if len(buf) != ln { return 0, errors.New("invalid length of APAIR rdata") } cp := make([]byte, ln) copy(cp, buf) // clone bytes to use them in IPs rd.addr[0] = net.IP(cp[:3]) rd.addr[1] = net.IP(cp[4:]) return len(buf), nil } func (rd *APAIR) Copy(dest dns.PrivateRdata) error { cp := make([]byte, rd.Len()) _, err := rd.Pack(cp) if err != nil { return err } d := dest.(*APAIR) d.addr[0] = net.IP(cp[:3]) d.addr[1] = net.IP(cp[4:]) return nil } func (rd *APAIR) Len() int { return net.IPv4len * 2 } func ExamplePrivateHandle() { dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR) defer dns.PrivateHandleRemove(TypeAPAIR) var oldId = dns.Id dns.Id = func() uint16 { return 3 } defer func() { dns.Id = oldId }() rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)") if err != nil { log.Fatal("could not parse APAIR record: ", err) } fmt.Println(rr) // see first line of Output below m := new(dns.Msg) m.SetQuestion("miek.nl.", TypeAPAIR) m.Answer = append(m.Answer, rr) fmt.Println(m) // Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5 // ;; opcode: QUERY, status: NOERROR, id: 3 // ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 // // ;; QUESTION SECTION: // ;miek.nl. IN APAIR // // ;; ANSWER SECTION: // miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5 } golang-github-miekg-dns-1.1.64/format.go000066400000000000000000000046261476742671700200620ustar00rootroot00000000000000package dns import ( "net" "reflect" "strconv" ) // NumField returns the number of rdata fields r has. func NumField(r RR) int { return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header } // Field returns the rdata field i as a string. Fields are indexed starting from 1. // RR types that holds slice data, for instance the NSEC type bitmap will return a single // string where the types are concatenated using a space. // Accessing non existing fields will cause a panic. func Field(r RR, i int) string { if i == 0 { return "" } d := reflect.ValueOf(r).Elem().Field(i) switch d.Kind() { case reflect.String: return d.String() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return strconv.FormatInt(d.Int(), 10) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return strconv.FormatUint(d.Uint(), 10) case reflect.Slice: switch reflect.ValueOf(r).Elem().Type().Field(i).Tag { case `dns:"a"`: // TODO(miek): Hmm store this as 16 bytes if d.Len() < net.IPv4len { return "" } if d.Len() < net.IPv6len { return net.IPv4(byte(d.Index(0).Uint()), byte(d.Index(1).Uint()), byte(d.Index(2).Uint()), byte(d.Index(3).Uint())).String() } return net.IPv4(byte(d.Index(12).Uint()), byte(d.Index(13).Uint()), byte(d.Index(14).Uint()), byte(d.Index(15).Uint())).String() case `dns:"aaaa"`: if d.Len() < net.IPv6len { return "" } return net.IP{ byte(d.Index(0).Uint()), byte(d.Index(1).Uint()), byte(d.Index(2).Uint()), byte(d.Index(3).Uint()), byte(d.Index(4).Uint()), byte(d.Index(5).Uint()), byte(d.Index(6).Uint()), byte(d.Index(7).Uint()), byte(d.Index(8).Uint()), byte(d.Index(9).Uint()), byte(d.Index(10).Uint()), byte(d.Index(11).Uint()), byte(d.Index(12).Uint()), byte(d.Index(13).Uint()), byte(d.Index(14).Uint()), byte(d.Index(15).Uint()), }.String() case `dns:"nsec"`: if d.Len() == 0 { return "" } s := Type(d.Index(0).Uint()).String() for i := 1; i < d.Len(); i++ { s += " " + Type(d.Index(i).Uint()).String() } return s default: // if it does not have a tag its a string slice fallthrough case `dns:"txt"`: if d.Len() == 0 { return "" } s := d.Index(0).String() for i := 1; i < d.Len(); i++ { s += " " + d.Index(i).String() } return s } } return "" } golang-github-miekg-dns-1.1.64/format_test.go000066400000000000000000000004271476742671700211140ustar00rootroot00000000000000package dns import ( "testing" ) func TestFieldEmptyAOrAAAAData(t *testing.T) { res := Field(new(A), 1) if res != "" { t.Errorf("expected empty string but got %v", res) } res = Field(new(AAAA), 1) if res != "" { t.Errorf("expected empty string but got %v", res) } } golang-github-miekg-dns-1.1.64/fuzz.go000066400000000000000000000010501476742671700175540ustar00rootroot00000000000000//go:build fuzz // +build fuzz package dns import "strings" func Fuzz(data []byte) int { msg := new(Msg) if err := msg.Unpack(data); err != nil { return 0 } if _, err := msg.Pack(); err != nil { return 0 } return 1 } func FuzzNewRR(data []byte) int { str := string(data) // Do not fuzz lines that include the $INCLUDE keyword and hint the fuzzer // at avoiding them. // See GH#1025 for context. if strings.Contains(strings.ToUpper(str), "$INCLUDE") { return -1 } if _, err := NewRR(str); err != nil { return 0 } return 1 } golang-github-miekg-dns-1.1.64/fuzz_test.go000066400000000000000000000170531476742671700206250ustar00rootroot00000000000000package dns import ( "net" "testing" ) // TestPackDataOpt tests generated using fuzz.go and with a message pack // containing the following bytes: // "0000\x00\x00000000\x00\x00/00000" + // "0\x00\v\x00#\b00000000\x00\x00)000" + // "000\x00\x1c00\x00\x0000\x00\x01000\x00\x00\x00\b" + // "\x00\v\x00\x02\x0000000000" // That bytes sequence created the overflow error. func TestPackDataOpt(t *testing.T) { type args struct { option []EDNS0 msg []byte off int } tests := []struct { name string args args want int wantErr bool wantErrMsg string }{ { name: "overflow", args: args{ option: []EDNS0{ &EDNS0_LOCAL{Code: 0x3030, Data: []uint8{}}, &EDNS0_LOCAL{Code: 0x3030, Data: []uint8{0x30}}, &EDNS0_LOCAL{Code: 0x3030, Data: []uint8{}}, &EDNS0_SUBNET{ Code: 0x0, Family: 0x2, SourceNetmask: 0x0, SourceScope: 0x30, Address: net.IP{0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, }, msg: []byte{ 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x0b, 0x00, 0x23, 0x08, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x29, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, }, off: 54, }, wantErr: true, wantErrMsg: "dns: overflow packing opt", want: 57, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := packDataOpt(tt.args.option, tt.args.msg, tt.args.off) if (err != nil) != tt.wantErr { t.Errorf("packDataOpt() error = %v, wantErr %v", err, tt.wantErr) return } if err != nil && tt.wantErrMsg != err.Error() { t.Errorf("packDataOpt() error msg = %v, wantErrMsg %v", err.Error(), tt.wantErrMsg) return } if got != tt.want { t.Errorf("packDataOpt() = %v, want %v", got, tt.want) } }) } } // TestCrashNSEC tests generated using fuzz.go and with a message pack // containing the following bytes: // "0000\x00\x00000000\x00\x00/00000" + // "0\x00\v\x00#\b00000\x00\x00\x00\x00\x00\x1a000" + // "000\x00\x00\x00\x00\x1a000000\x00\x00\x00\x00\x1a0" + // "00000\x00\v00\a0000000\x00" // That byte sequence, when Unpack() and subsequent Pack() created a // panic: runtime error: slice bounds out of range // which was attributed to the fact that NSEC RR length computation was different (and smaller) // then when within packDataNsec. func TestCrashNSEC(t *testing.T) { compression := make(map[string]struct{}) nsec := &NSEC{ Hdr: RR_Header{ Name: ".", Rrtype: 0x2f, Class: 0x3030, Ttl: 0x30303030, Rdlength: 0xb, }, NextDomain: ".", TypeBitMap: []uint16{ 0x2302, 0x2303, 0x230a, 0x230b, 0x2312, 0x2313, 0x231a, 0x231b, 0x2322, 0x2323, }, } expectedLength := 19 l := nsec.len(0, compression) if l != expectedLength { t.Fatalf("expected length of %d, got %d", expectedLength, l) } } // TestCrashNSEC3 tests generated using fuzz.go and with a message pack // containing the following bytes: // "0000\x00\x00000000\x00\x00200000" + // "0\x00\v0000\x00\x00#\x0300\x00\x00\x00\x1a000" + // "000\x00\v00\x0200\x00\x03000\x00" // That byte sequence, when Unpack() and subsequent Pack() created a // panic: runtime error: slice bounds out of range // which was attributed to the fact that NSEC3 RR length computation was // different (and smaller) then within NSEC3.pack (which relies on // packDataNsec). func TestCrashNSEC3(t *testing.T) { compression := make(map[string]struct{}) nsec3 := &NSEC3{ Hdr: RR_Header{ Name: ".", Rrtype: 0x32, Class: 0x3030, Ttl: 0x30303030, Rdlength: 0xb, }, Hash: 0x30, Flags: 0x30, Iterations: 0x3030, SaltLength: 0x0, Salt: "", HashLength: 0x0, NextDomain: ".", TypeBitMap: []uint16{ 0x2302, 0x2303, 0x230a, 0x230b, }, } expectedLength := 24 l := nsec3.len(0, compression) if l != expectedLength { t.Fatalf("expected length of %d, got %d", expectedLength, l) } } // TestNewRRCommentLengthCrasherString test inputs to NewRR that generated crashes. func TestNewRRCommentLengthCrasherString(t *testing.T) { tests := []struct { name string in string err string }{ { "HINFO1", " HINFO ;;;;;;;;;;;;;" + ";;;;;;;;\x00\x19;;;;;;;;;;" + ";\u007f;;;;;;;;;;;;;;;;;;" + ";;}mP_Qq_3sJ_1_84X_5" + "45iW_3K4p8J8_v9_LT3_" + "6_0l_3D4VT3xq6N_3K__" + "_U_xX2m;;;;;;(;;;;;;" + ";;;;;;;;;;;;;;;\x1d;;;;" + ";;;;;;-0x804dBDe8ba " + "\t \t\tr HINFO \" \t\t\tve" + "k1xH11e__P6_dk1_51bo" + "g8gJK1V_O_v84_Bw4_1_" + "72jQ3_0J3V_S5iYn4h5X" + "R_2n___51J nN_ \t\tm " + "aa_XO4_5\t \t\t \t\tg6b" + "p_KI_1_YWc_K8c2b___A" + "e_Y1m__4Y_R_avy6t08x" + "b5Cp9_7uS_yLa\t\t\t d " + "EKe1Q83vS___ a \t\t " + "\tmP_Qq_3sJ_1_84X_545" + "iW_3K4p8J8_v9_LT3_6_" + "0l_3D4VT3xq6N_3K___U" + "_xX2\"\" \t \t_fL Ogl5" + "_09i_9__3O7C__QMAG2U" + "35IO8RRU6aJ9_6_57_6_" + "b05BMoX5I__4833_____" + "yfD_2_OPs__sqzM_pqQi" + "_\t\t \tN__GuY4_Trath_0" + "yy___cAK_a__0J0q5 L_" + "p63Fzdva_Lb_29V7_R__" + "Go_H2_8m_4__FJM5B_Y5" + "Slw_ghp_55l_X2_Pnt6Y" + "_Wd_hM7jRZ_\t\t \tm \t" + " \t\ta md rK \x00 7_\"sr " + "- sg o -0x804dBDe8b" + "a \t \t\tN_W6J3PBS_W__C" + "yJu__k6F_jY0INI_LC27" + "7x14b_1b___Y8f_K_3y_" + "0055yaP_LKu_72g_T_32" + "iBk1Zm_o 9i1P44_S0_" + "_4AXUpo2__H55tL_g78_" + "8V_8l0yg6bp_KI_1_YWc" + "_K8c2b \t \tmaa_XO4_5" + "rg6bp_KI_1_YWc_K8c2b" + " _C20w i_4 \t\t u_k d" + " rKsg09099 \"\"2335779" + "05047986112651e025 \t" + " \t\tN_W6J3PBS_W__CyJu" + "__k6F_jY0INI_LC277x1" + "4b_1b___Y8f_K_3y_005" + "5yaP_LKu_72g_T_32iBk" + "1Zm_o 9i1P44_S0__4A" + "XUpo2__H55tL_g78_8V_" + "8l0y_9K9_C__6af__wj_" + "UbSYy_ge29S_s_Qe259q" + "_kGod \t\t\t\t :0xb1AF1F" + "b71D2ACeaB3FEce2ssg " + "o dr-0x804dBDe8ba \t " + "\t\t$ Y5 _BzOc6S_Lk0K" + "y43j1TzV__9367tbX56_" + "6B3__q6_v8_4_0_t_2q_" + "nJ2gV3j9_tkOrx_H__a}" + "mT 0g6bp_KI_1_YWc_K8" + "c2b\t_ a\t \t54KM8f9_63" + "zJ2Q_c1_C_Zf4ICF4m0q" + "_RVm_3Zh4vr7yI_H2 a" + " m 0yq__TiqA_FQBv_SS" + "_Hm_8T8__M8F2_53TTo_" + "k_o2__u_W6Vr__524q9l" + "9CQsC_kOU___g_94 \"" + " ~a_j_16_6iUSu_96V1W" + "5r01j____gn157__8_LO" + "0y_08Jr6OR__WF8__JK_" + "N_wx_k_CGB_SjJ9R74i_" + "7_1t_6 m NULLNULLNUL" + "L \t \t\t\t drK\t\x00 7_\"\" 5" + "_5_y732S43__D_8U9FX2" + "27_k\t\tg6bp_KI_1_YWc_" + "K8c2b_J_wx8yw1CMw27j" + "___f_a8uw_ Er9gB_L2 " + "\t\t \t\t\tm aa_XO4_5 Y_" + " I_T7762_zlMi_n8_FjH" + "vy62p__M4S_8__r092af" + "P_T_vhp6__SA_jVF13c5" + "2__8J48K__S4YcjoY91X" + "_iNf06 am aa_XO4_5\t" + " d _ am_SYY4G__2h4QL" + "iUIDd \t\t \tXXp__KFjR" + "V__JU3o\"\" d \t_Iks_ " + "aa_XO4_5= 0 { if i+1 == len(token) { return zp.setParseError("bad step in $GENERATE range", l) } s, err := strconv.ParseInt(token[i+1:], 10, 64) if err != nil || s <= 0 { return zp.setParseError("bad step in $GENERATE range", l) } step = s token = token[:i] } startStr, endStr, ok := strings.Cut(token, "-") if !ok { return zp.setParseError("bad start-stop in $GENERATE range", l) } start, err := strconv.ParseInt(startStr, 10, 64) if err != nil { return zp.setParseError("bad start in $GENERATE range", l) } end, err := strconv.ParseInt(endStr, 10, 64) if err != nil { return zp.setParseError("bad stop in $GENERATE range", l) } if end < 0 || start < 0 || end < start || (end-start)/step > 65535 { return zp.setParseError("bad range in $GENERATE range", l) } // _BLANK l, ok = zp.c.Next() if !ok || l.value != zBlank { return zp.setParseError("garbage after $GENERATE range", l) } // Create a complete new string, which we then parse again. var s string for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() { if l.err { return zp.setParseError("bad data in $GENERATE directive", l) } if l.value == zNewline { break } s += l.token } r := &generateReader{ s: s, cur: start, start: start, end: end, step: step, file: zp.file, lex: &l, } zp.sub = NewZoneParser(r, zp.origin, zp.file) zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed zp.sub.generateDisallowed = true zp.sub.SetDefaultTTL(defaultTtl) return zp.subNext() } type generateReader struct { s string si int cur int64 start int64 end int64 step int64 mod bytes.Buffer escape bool eof bool file string lex *lex } func (r *generateReader) parseError(msg string, end int) *ParseError { r.eof = true // Make errors sticky. l := *r.lex l.token = r.s[r.si-1 : end] l.column += r.si // l.column starts one zBLANK before r.s return &ParseError{file: r.file, err: msg, lex: l} } func (r *generateReader) Read(p []byte) (int, error) { // NewZLexer, through NewZoneParser, should use ReadByte and // not end up here. panic("not implemented") } func (r *generateReader) ReadByte() (byte, error) { if r.eof { return 0, io.EOF } if r.mod.Len() > 0 { return r.mod.ReadByte() } if r.si >= len(r.s) { r.si = 0 r.cur += r.step r.eof = r.cur > r.end || r.cur < 0 return '\n', nil } si := r.si r.si++ switch r.s[si] { case '\\': if r.escape { r.escape = false return '\\', nil } r.escape = true return r.ReadByte() case '$': if r.escape { r.escape = false return '$', nil } mod := "%d" if si >= len(r.s)-1 { // End of the string fmt.Fprintf(&r.mod, mod, r.cur) return r.mod.ReadByte() } if r.s[si+1] == '$' { r.si++ return '$', nil } var offset int64 // Search for { and } if r.s[si+1] == '{' { // Modifier block sep := strings.Index(r.s[si+2:], "}") if sep < 0 { return 0, r.parseError("bad modifier in $GENERATE", len(r.s)) } var errMsg string mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep]) if errMsg != "" { return 0, r.parseError(errMsg, si+3+sep) } if r.start+offset < 0 || r.end+offset > 1<<31-1 { return 0, r.parseError("bad offset in $GENERATE", si+3+sep) } r.si += 2 + sep // Jump to it } fmt.Fprintf(&r.mod, mod, r.cur+offset) return r.mod.ReadByte() default: if r.escape { // Pretty useless here r.escape = false return r.ReadByte() } return r.s[si], nil } } // Convert a $GENERATE modifier 0,0,d to something Printf can deal with. func modToPrintf(s string) (string, int64, string) { // Modifier is { offset [ ,width [ ,base ] ] } - provide default // values for optional width and type, if necessary. offStr, s, ok0 := strings.Cut(s, ",") widthStr, s, ok1 := strings.Cut(s, ",") base, _, ok2 := strings.Cut(s, ",") if !ok0 { widthStr = "0" } if !ok1 { base = "d" } if ok2 { return "", 0, "bad modifier in $GENERATE" } switch base { case "o", "d", "x", "X": default: return "", 0, "bad base in $GENERATE" } offset, err := strconv.ParseInt(offStr, 10, 64) if err != nil { return "", 0, "bad offset in $GENERATE" } width, err := strconv.ParseUint(widthStr, 10, 8) if err != nil { return "", 0, "bad width in $GENERATE" } if width == 0 { return "%" + base, offset, "" } return "%0" + widthStr + base, offset, "" } golang-github-miekg-dns-1.1.64/generate_test.go000066400000000000000000000154501476742671700214200ustar00rootroot00000000000000package dns import ( "fmt" "os" "path/filepath" "strings" "testing" ) func TestGenerateRangeGuard(t *testing.T) { tmpdir := t.TempDir() for i := 0; i <= 1; i++ { path := filepath.Join(tmpdir, fmt.Sprintf("%04d.conf", i)) data := []byte(fmt.Sprintf("dhcp-%04d A 10.0.0.%d", i, i)) if err := os.WriteFile(path, data, 0o644); err != nil { t.Fatalf("could not create tmpfile for test: %v", err) } } tests := [...]struct { zone string fail bool }{ {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 dhcp-${0,4,d} A 10.0.0.$ `, false}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 dhcp-${0,0,x} A 10.0.0.$ `, false}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 128-129 dhcp-${-128,4,d} A 10.0.0.$ `, false}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 128-129 dhcp-${-129,4,d} A 10.0.0.$ `, true}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-2 dhcp-${2147483647,4,d} A 10.0.0.$ `, true}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 dhcp-${2147483646,4,d} A 10.0.0.$ `, false}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1/step dhcp-${0,4,d} A 10.0.0.$ `, true}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1/ dhcp-${0,4,d} A 10.0.0.$ `, true}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-10/2 dhcp-${0,4,d} A 10.0.0.$ `, false}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1/0 dhcp-${0,4,d} A 10.0.0.$ `, true}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 $$INCLUDE ` + tmpdir + string(filepath.Separator) + `${0,4,d}.conf `, false}, {`@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 dhcp-${0,4,d} A 10.0.0.$ $GENERATE 0-2 dhcp-${0,4,d} A 10.1.0.$ `, false}, } for i := range tests { z := NewZoneParser(strings.NewReader(tests[i].zone), "test.", "test") z.SetIncludeAllowed(true) for _, ok := z.Next(); ok; _, ok = z.Next() { } err := z.Err() if err != nil && !tests[i].fail { t.Errorf("expected \n\n%s\nto be parsed, but got %v", tests[i].zone, err) } else if err == nil && tests[i].fail { t.Errorf("expected \n\n%s\nto fail, but got no error", tests[i].zone) } } } func TestGenerateIncludeDepth(t *testing.T) { tmpfile, err := os.CreateTemp("", "dns") if err != nil { t.Fatalf("could not create tmpfile for test: %v", err) } defer os.Remove(tmpfile.Name()) zone := `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 $$INCLUDE ` + tmpfile.Name() + ` ` if _, err := tmpfile.WriteString(zone); err != nil { t.Fatalf("could not write to tmpfile for test: %v", err) } if err := tmpfile.Close(); err != nil { t.Fatalf("could not close tmpfile for test: %v", err) } zp := NewZoneParser(strings.NewReader(zone), ".", tmpfile.Name()) zp.SetIncludeAllowed(true) for _, ok := zp.Next(); ok; _, ok = zp.Next() { } const expected = "too deeply nested $INCLUDE" if err := zp.Err(); err == nil || !strings.Contains(err.Error(), expected) { t.Errorf("expected error to include %q, got %v", expected, err) } } func TestGenerateIncludeDisallowed(t *testing.T) { const zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 $$INCLUDE test.conf ` zp := NewZoneParser(strings.NewReader(zone), ".", "") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } const expected = "$INCLUDE directive not allowed" if err := zp.Err(); err == nil || !strings.Contains(err.Error(), expected) { t.Errorf("expected error to include %q, got %v", expected, err) } } func TestGenerateSurfacesErrors(t *testing.T) { const zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 dhcp-${0,4,dd} A 10.0.0.$ ` zp := NewZoneParser(strings.NewReader(zone), ".", "test") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } const expected = `test: dns: bad base in $GENERATE: "${0,4,dd}" at line: 2:20` if err := zp.Err(); err == nil || err.Error() != expected { t.Errorf("expected specific error, wanted %q, got %v", expected, err) } } func TestGenerateSurfacesLexerErrors(t *testing.T) { const zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 0-1 dhcp-${0,4,d} A 10.0.0.$ ) ` zp := NewZoneParser(strings.NewReader(zone), ".", "test") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } const expected = `test: dns: bad data in $GENERATE directive: "extra closing brace" at line: 2:40` if err := zp.Err(); err == nil || err.Error() != expected { t.Errorf("expected specific error, wanted %q, got %v", expected, err) } } func TestGenerateModToPrintf(t *testing.T) { tests := []struct { mod string wantFmt string wantOffset int64 wantErr bool }{ {"0,0,d", "%d", 0, false}, {"0,0", "%d", 0, false}, {"0", "%d", 0, false}, {"3,2,d", "%02d", 3, false}, {"3,2", "%02d", 3, false}, {"3", "%d", 3, false}, {"0,0,o", "%o", 0, false}, {"0,0,x", "%x", 0, false}, {"0,0,X", "%X", 0, false}, {"0,0,z", "", 0, true}, {"0,0,0,d", "", 0, true}, {"-100,0,d", "%d", -100, false}, } for _, test := range tests { gotFmt, gotOffset, errMsg := modToPrintf(test.mod) switch { case errMsg != "" && !test.wantErr: t.Errorf("modToPrintf(%q) - expected empty-error, but got %v", test.mod, errMsg) case errMsg == "" && test.wantErr: t.Errorf("modToPrintf(%q) - expected error, but got empty-error", test.mod) case gotFmt != test.wantFmt: t.Errorf("modToPrintf(%q) - expected format %q, but got %q", test.mod, test.wantFmt, gotFmt) case gotOffset != test.wantOffset: t.Errorf("modToPrintf(%q) - expected offset %d, but got %d", test.mod, test.wantOffset, gotOffset) } } } func BenchmarkGenerate(b *testing.B) { const zone = `@ IN SOA ns.test. hostmaster.test. ( 1 8h 2h 7d 1d ) $GENERATE 32-158 dhcp-${-32,4,d} A 10.0.0.$ ` for n := 0; n < b.N; n++ { zp := NewZoneParser(strings.NewReader(zone), ".", "") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } if err := zp.Err(); err != nil { b.Fatal(err) } } } func TestCrasherString(t *testing.T) { tests := []struct { in string err string }{ {"$GENERATE 0-300103\"$$GENERATE 2-2", "bad range in $GENERATE"}, {"$GENERATE 0-5414137360", "bad range in $GENERATE"}, {"$GENERATE 11522-3668518066406258", "bad range in $GENERATE"}, {"$GENERATE 0-200\"(;00000000000000\n$$GENERATE 0-0", "dns: garbage after $GENERATE range: \"\\\"\" at line: 1:16"}, {"$GENERATE 6-2048 $$GENERATE 6-036160 $$$$ORIGIN \\$", `dns: nested $GENERATE directive not allowed: "6-036160" at line: 1:19`}, } for _, tc := range tests { t.Run(tc.in, func(t *testing.T) { _, err := NewRR(tc.in) if err == nil { t.Errorf("Expecting error for crasher line %s", tc.in) } if !strings.Contains(err.Error(), tc.err) { t.Errorf("Expecting error %s, got %s", tc.err, err.Error()) } }) } } golang-github-miekg-dns-1.1.64/go.mod000066400000000000000000000003411476742671700173370ustar00rootroot00000000000000module github.com/miekg/dns go 1.22.0 toolchain go1.24.0 require ( golang.org/x/net v0.35.0 golang.org/x/sync v0.11.0 golang.org/x/sys v0.30.0 golang.org/x/tools v0.30.0 ) require golang.org/x/mod v0.23.0 // indirect golang-github-miekg-dns-1.1.64/go.sum000066400000000000000000000016521476742671700173720ustar00rootroot00000000000000github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang-github-miekg-dns-1.1.64/hash.go000066400000000000000000000015531476742671700175110ustar00rootroot00000000000000package dns import ( "bytes" "crypto" "hash" ) // identityHash will not hash, it only buffers the data written into it and returns it as-is. type identityHash struct { b *bytes.Buffer } // Implement the hash.Hash interface. func (i identityHash) Write(b []byte) (int, error) { return i.b.Write(b) } func (i identityHash) Size() int { return i.b.Len() } func (i identityHash) BlockSize() int { return 1024 } func (i identityHash) Reset() { i.b.Reset() } func (i identityHash) Sum(b []byte) []byte { return append(b, i.b.Bytes()...) } func hashFromAlgorithm(alg uint8) (hash.Hash, crypto.Hash, error) { hashnumber, ok := AlgorithmToHash[alg] if !ok { return nil, 0, ErrAlg } if hashnumber == 0 { return identityHash{b: &bytes.Buffer{}}, hashnumber, nil } return hashnumber.New(), hashnumber, nil } golang-github-miekg-dns-1.1.64/issue_test.go000066400000000000000000000023001476742671700207440ustar00rootroot00000000000000package dns // Tests that solve that an specific issue. import ( "strings" "testing" ) func TestNSEC3MissingSalt(t *testing.T) { rr := testRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 aabbccdd K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H") m := new(Msg) m.Answer = []RR{rr} mb, err := m.Pack() if err != nil { t.Fatalf("expected to pack message. err: %s", err) } if err := m.Unpack(mb); err != nil { t.Fatalf("expected to unpack message. missing salt? err: %s", err) } in := rr.(*NSEC3).Salt out := m.Answer[0].(*NSEC3).Salt if in != out { t.Fatalf("expected salts to match. packed: `%s`. returned: `%s`", in, out) } } func TestNSEC3MixedNextDomain(t *testing.T) { rr := testRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 - k8udemvp1j2f7eg6jebps17vp3n8i58h") m := new(Msg) m.Answer = []RR{rr} mb, err := m.Pack() if err != nil { t.Fatalf("expected to pack message. err: %s", err) } if err := m.Unpack(mb); err != nil { t.Fatalf("expected to unpack message. err: %s", err) } in := strings.ToUpper(rr.(*NSEC3).NextDomain) out := m.Answer[0].(*NSEC3).NextDomain if in != out { t.Fatalf("expected round trip to produce NextDomain `%s`, instead `%s`", in, out) } } golang-github-miekg-dns-1.1.64/labels.go000066400000000000000000000102261476742671700200250ustar00rootroot00000000000000package dns // Holds a bunch of helper functions for dealing with labels. // SplitDomainName splits a name string into it's labels. // www.miek.nl. returns []string{"www", "miek", "nl"} // .www.miek.nl. returns []string{"", "www", "miek", "nl"}, // The root label (.) returns nil. Note that using // strings.Split(s) will work in most cases, but does not handle // escaped dots (\.) for instance. // s must be a syntactically valid domain name, see IsDomainName. func SplitDomainName(s string) (labels []string) { if s == "" { return nil } fqdnEnd := 0 // offset of the final '.' or the length of the name idx := Split(s) begin := 0 if IsFqdn(s) { fqdnEnd = len(s) - 1 } else { fqdnEnd = len(s) } switch len(idx) { case 0: return nil case 1: // no-op default: for _, end := range idx[1:] { labels = append(labels, s[begin:end-1]) begin = end } } return append(labels, s[begin:fqdnEnd]) } // CompareDomainName compares the names s1 and s2 and // returns how many labels they have in common starting from the *right*. // The comparison stops at the first inequality. The names are downcased // before the comparison. // // www.miek.nl. and miek.nl. have two labels in common: miek and nl // www.miek.nl. and www.bla.nl. have one label in common: nl // // s1 and s2 must be syntactically valid domain names. func CompareDomainName(s1, s2 string) (n int) { // the first check: root label if s1 == "." || s2 == "." { return 0 } l1 := Split(s1) l2 := Split(s2) j1 := len(l1) - 1 // end i1 := len(l1) - 2 // start j2 := len(l2) - 1 i2 := len(l2) - 2 // the second check can be done here: last/only label // before we fall through into the for-loop below if equal(s1[l1[j1]:], s2[l2[j2]:]) { n++ } else { return } for { if i1 < 0 || i2 < 0 { break } if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) { n++ } else { break } j1-- i1-- j2-- i2-- } return } // CountLabel counts the number of labels in the string s. // s must be a syntactically valid domain name. func CountLabel(s string) (labels int) { if s == "." { return } off := 0 end := false for { off, end = NextLabel(s, off) labels++ if end { return } } } // Split splits a name s into its label indexes. // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. // The root name (.) returns nil. Also see SplitDomainName. // s must be a syntactically valid domain name. func Split(s string) []int { if s == "." { return nil } idx := make([]int, 1, 3) off := 0 end := false for { off, end = NextLabel(s, off) if end { return idx } idx = append(idx, off) } } // NextLabel returns the index of the start of the next label in the // string s starting at offset. A negative offset will cause a panic. // The bool end is true when the end of the string has been reached. // Also see PrevLabel. func NextLabel(s string, offset int) (i int, end bool) { if s == "" { return 0, true } for i = offset; i < len(s)-1; i++ { if s[i] != '.' { continue } j := i - 1 for j >= 0 && s[j] == '\\' { j-- } if (j-i)%2 == 0 { continue } return i + 1, false } return i + 1, true } // PrevLabel returns the index of the label when starting from the right and // jumping n labels to the left. // The bool start is true when the start of the string has been overshot. // Also see NextLabel. func PrevLabel(s string, n int) (i int, start bool) { if s == "" { return 0, true } if n == 0 { return len(s), false } l := len(s) - 1 if s[l] == '.' { l-- } for ; l >= 0 && n > 0; l-- { if s[l] != '.' { continue } j := l - 1 for j >= 0 && s[j] == '\\' { j-- } if (j-l)%2 == 0 { continue } n-- if n == 0 { return l + 1, false } } return 0, n > 1 } // equal compares a and b while ignoring case. It returns true when equal otherwise false. func equal(a, b string) bool { // might be lifted into API function. la := len(a) lb := len(b) if la != lb { return false } for i := la - 1; i >= 0; i-- { ai := a[i] bi := b[i] if ai >= 'A' && ai <= 'Z' { ai |= 'a' - 'A' } if bi >= 'A' && bi <= 'Z' { bi |= 'a' - 'A' } if ai != bi { return false } } return true } golang-github-miekg-dns-1.1.64/labels_test.go000066400000000000000000000201051476742671700210610ustar00rootroot00000000000000package dns import "testing" func TestCompareDomainName(t *testing.T) { s1 := "www.miek.nl." s2 := "miek.nl." s3 := "www.bla.nl." s4 := "nl.www.bla." s5 := "nl." s6 := "miek.nl." if CompareDomainName(s1, s2) != 2 { t.Errorf("%s with %s should be %d", s1, s2, 2) } if CompareDomainName(s1, s3) != 1 { t.Errorf("%s with %s should be %d", s1, s3, 1) } if CompareDomainName(s3, s4) != 0 { t.Errorf("%s with %s should be %d", s3, s4, 0) } // Non qualified tests if CompareDomainName(s1, s5) != 1 { t.Errorf("%s with %s should be %d", s1, s5, 1) } if CompareDomainName(s1, s6) != 2 { t.Errorf("%s with %s should be %d", s1, s5, 2) } if CompareDomainName(s1, ".") != 0 { t.Errorf("%s with %s should be %d", s1, s5, 0) } if CompareDomainName(".", ".") != 0 { t.Errorf("%s with %s should be %d", ".", ".", 0) } if CompareDomainName("test.com.", "TEST.COM.") != 2 { t.Errorf("test.com. and TEST.COM. should be an exact match") } } func TestSplit(t *testing.T) { splitter := map[string]int{ "www.miek.nl.": 3, "www.miek.nl": 3, "www..miek.nl": 4, `www\.miek.nl.`: 2, `www\\.miek.nl.`: 3, `www\\\.miek.nl.`: 2, ".": 0, "nl.": 1, "nl": 1, "com.": 1, ".com.": 2, } for s, i := range splitter { if x := len(Split(s)); x != i { t.Errorf("labels should be %d, got %d: %s %v", i, x, s, Split(s)) } } } func TestSplit2(t *testing.T) { splitter := map[string][]int{ "www.miek.nl.": {0, 4, 9}, "www.miek.nl": {0, 4, 9}, "nl": {0}, } for s, i := range splitter { x := Split(s) switch len(i) { case 1: if x[0] != i[0] { t.Errorf("labels should be %v, got %v: %s", i, x, s) } default: if x[0] != i[0] || x[1] != i[1] || x[2] != i[2] { t.Errorf("labels should be %v, got %v: %s", i, x, s) } } } } func TestNextLabel(t *testing.T) { type next struct { string int } nexts := map[next]int{ {"", 1}: 0, {"www.miek.nl.", 0}: 4, {"www.miek.nl.", 4}: 9, {"www.miek.nl.", 9}: 12, } for s, i := range nexts { x, ok := NextLabel(s.string, s.int) if i != x { t.Errorf("label should be %d, got %d, %t: next %d, %s", i, x, ok, s.int, s.string) } } } func TestPrevLabel(t *testing.T) { type prev struct { string int } prever := map[prev]int{ {"", 1}: 0, {"www.miek.nl.", 0}: 12, {"www.miek.nl.", 1}: 9, {"www.miek.nl.", 2}: 4, {"www.miek.nl", 0}: 11, {"www.miek.nl", 1}: 9, {"www.miek.nl", 2}: 4, {"www.miek.nl.", 5}: 0, {"www.miek.nl", 5}: 0, {"www.miek.nl.", 3}: 0, {"www.miek.nl", 3}: 0, } for s, i := range prever { x, ok := PrevLabel(s.string, s.int) if i != x { t.Errorf("label should be %d, got %d, %t: previous %d, %s", i, x, ok, s.int, s.string) } } } func TestCountLabel(t *testing.T) { splitter := map[string]int{ "www.miek.nl.": 3, "www.miek.nl": 3, "nl": 1, ".": 0, } for s, i := range splitter { x := CountLabel(s) if x != i { t.Errorf("CountLabel should have %d, got %d", i, x) } } } func TestSplitDomainName(t *testing.T) { labels := map[string][]string{ "miek.nl": {"miek", "nl"}, ".": nil, "www.miek.nl.": {"www", "miek", "nl"}, "www.miek.nl": {"www", "miek", "nl"}, "www..miek.nl": {"www", "", "miek", "nl"}, `www\.miek.nl`: {`www\.miek`, "nl"}, `www\\.miek.nl`: {`www\\`, "miek", "nl"}, ".www.miek.nl.": {"", "www", "miek", "nl"}, } domainLoop: for domain, splits := range labels { parts := SplitDomainName(domain) if len(parts) != len(splits) { t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits) continue domainLoop } for i := range parts { if parts[i] != splits[i] { t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits) continue domainLoop } } } } func TestIsDomainName(t *testing.T) { type ret struct { ok bool lab int } names := map[string]*ret{ ".": {true, 1}, "..": {false, 0}, "double-dot..test": {false, 1}, ".leading-dot.test": {false, 0}, "@.": {true, 1}, "www.example.com": {true, 3}, "www.e%ample.com": {true, 3}, "www.example.com.": {true, 3}, "mi\\k.nl.": {true, 2}, "mi\\k.nl": {true, 2}, longestDomain: {true, 4}, longestUnprintableDomain: {true, 4}, } for d, ok := range names { l, k := IsDomainName(d) if ok.ok != k || ok.lab != l { t.Errorf(" got %v %d for %s ", k, l, d) t.Errorf("have %v %d for %s ", ok.ok, ok.lab, d) } } } func TestIsFqdnEscaped(t *testing.T) { for s, expect := range map[string]bool{ ".": true, "\\.": false, "\\\\.": true, "\\\\\\.": false, "\\\\\\\\.": true, "a.": true, "a\\.": false, "a\\\\.": true, "a\\\\\\.": false, "ab.": true, "ab\\.": false, "ab\\\\.": true, "ab\\\\\\.": false, "..": true, ".\\.": false, ".\\\\.": true, ".\\\\\\.": false, "example.org.": true, "example.org\\.": false, "example.org\\\\.": true, "example.org\\\\\\.": false, "example\\.org.": true, "example\\\\.org.": true, "example\\\\\\.org.": true, "\\example.org.": true, "\\\\example.org.": true, "\\\\\\example.org.": true, } { if got := IsFqdn(s); got != expect { t.Errorf("IsFqdn(%q) = %t, expected %t", s, got, expect) } } } func TestCanonicalName(t *testing.T) { for s, expect := range map[string]string{ "": ".", ".": ".", "tld": "tld.", "tld.": "tld.", "example.test": "example.test.", "Lower.CASE.test.": "lower.case.test.", "*.Test": "*.test.", "ÉxamplE.com": "Éxample.com.", "É.com": "É.com.", } { if got := CanonicalName(s); got != expect { t.Errorf("CanonicalName(%q) = %q, expected %q", s, got, expect) } } } func BenchmarkSplitLabels(b *testing.B) { for i := 0; i < b.N; i++ { Split("www.example.com.") } } func BenchmarkLenLabels(b *testing.B) { for i := 0; i < b.N; i++ { CountLabel("www.example.com.") } } func BenchmarkCompareDomainName(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { CompareDomainName("www.example.com.", "aa.example.com.") } } func BenchmarkIsSubDomain(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { IsSubDomain("www.example.com.", "aa.example.com.") IsSubDomain("example.com.", "aa.example.com.") IsSubDomain("miek.nl.", "aa.example.com.") } } func BenchmarkNextLabelSimple(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { NextLabel("www.example.com", 0) NextLabel("www.example.com", 5) NextLabel("www.example.com", 12) } } func BenchmarkPrevLabelSimple(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { PrevLabel("www.example.com", 0) PrevLabel("www.example.com", 5) PrevLabel("www.example.com", 12) } } func BenchmarkNextLabelComplex(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { NextLabel(`www\.example.com`, 0) NextLabel(`www\\.example.com`, 0) NextLabel(`www\\\.example.com`, 0) } } func BenchmarkPrevLabelComplex(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { PrevLabel(`www\.example.com`, 10) PrevLabel(`www\\.example.com`, 10) PrevLabel(`www\\\.example.com`, 10) } } func BenchmarkNextLabelMixed(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { NextLabel("www.example.com", 0) NextLabel(`www\.example.com`, 0) NextLabel("www.example.com", 5) NextLabel(`www\\.example.com`, 0) NextLabel("www.example.com", 12) NextLabel(`www\\\.example.com`, 0) } } func BenchmarkPrevLabelMixed(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { PrevLabel("www.example.com", 0) PrevLabel(`www\.example.com`, 10) PrevLabel("www.example.com", 5) PrevLabel(`www\\.example.com`, 10) PrevLabel("www.example.com", 12) PrevLabel(`www\\\.example.com`, 10) } } golang-github-miekg-dns-1.1.64/leak_test.go000066400000000000000000000034731476742671700205440ustar00rootroot00000000000000package dns import ( "fmt" "os" "runtime" "sort" "strings" "testing" "time" ) // copied from net/http/main_test.go func interestingGoroutines() (gs []string) { buf := make([]byte, 2<<20) buf = buf[:runtime.Stack(buf, true)] for _, g := range strings.Split(string(buf), "\n\n") { sl := strings.SplitN(g, "\n", 2) if len(sl) != 2 { continue } stack := strings.TrimSpace(sl[1]) if stack == "" || strings.Contains(stack, "testing.(*M).before.func1") || strings.Contains(stack, "os/signal.signal_recv") || strings.Contains(stack, "created by net.startServer") || strings.Contains(stack, "created by testing.RunTests") || strings.Contains(stack, "closeWriteAndWait") || strings.Contains(stack, "testing.Main(") || strings.Contains(stack, "testing.(*T).Run(") || // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) strings.Contains(stack, "runtime.goexit") || strings.Contains(stack, "created by runtime.gc") || strings.Contains(stack, "dns.interestingGoroutines") || strings.Contains(stack, "runtime.MHeap_Scavenger") { continue } gs = append(gs, stack) } sort.Strings(gs) return } func goroutineLeaked() error { if testing.Short() { // Don't worry about goroutine leaks in -short mode or in // benchmark mode. Too distracting when there are false positives. return nil } var stackCount map[string]int for i := 0; i < 5; i++ { n := 0 stackCount = make(map[string]int) gs := interestingGoroutines() for _, g := range gs { stackCount[g]++ n++ } if n == 0 { return nil } // Wait for goroutines to schedule and die off: time.Sleep(100 * time.Millisecond) } for stack, count := range stackCount { fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) } return fmt.Errorf("too many goroutines running after dns test(s)") } golang-github-miekg-dns-1.1.64/length_test.go000066400000000000000000000507631476742671700211150ustar00rootroot00000000000000package dns import ( "encoding/hex" "fmt" "net" "strings" "testing" ) func TestCompressLength(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeMX) ul := m.Len() m.Compress = true if ul != m.Len() { t.Fatalf("should be equal") } } // Does the predicted length match final packed length? func TestMsgCompressLength(t *testing.T) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) msg.Compress = true return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrA := testRR(name1 + " 3600 IN A 192.0.2.1") rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) tests := []*Msg{ makeMsg(name1, []RR{rrA}, nil, nil), makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)} for _, msg := range tests { predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted < len(buf) { t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) } } } func TestMsgLength(t *testing.T) { makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.Compress = true msg.SetQuestion(Fqdn(question), TypeANY) msg.Answer = append(msg.Answer, ans...) msg.Ns = append(msg.Ns, ns...) msg.Extra = append(msg.Extra, e...) return msg } name1 := "12345678901234567890123456789012345.12345678.123." rrA := testRR(name1 + " 3600 IN A 192.0.2.1") rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) tests := []*Msg{ makeMsg(name1, []RR{rrA}, nil, nil), makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)} for _, msg := range tests { predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted < len(buf) { t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d", msg.Question[0].Name, predicted, len(buf)) } } } func TestCompressionLenSearchInsert(t *testing.T) { c := make(map[string]struct{}) compressionLenSearch(c, "example.com", 12) if _, ok := c["example.com"]; !ok { t.Errorf("bad example.com") } if _, ok := c["com"]; !ok { t.Errorf("bad com") } // Test boundaries c = make(map[string]struct{}) // foo label starts at 16379 // com label starts at 16384 compressionLenSearch(c, "foo.com", 16379) if _, ok := c["foo.com"]; !ok { t.Errorf("bad foo.com") } // com label is accessible if _, ok := c["com"]; !ok { t.Errorf("bad com") } c = make(map[string]struct{}) // foo label starts at 16379 // com label starts at 16385 => outside range compressionLenSearch(c, "foo.com", 16380) if _, ok := c["foo.com"]; !ok { t.Errorf("bad foo.com") } // com label is NOT accessible if _, ok := c["com"]; ok { t.Errorf("bad com") } c = make(map[string]struct{}) compressionLenSearch(c, "example.com", 16375) if _, ok := c["example.com"]; !ok { t.Errorf("bad example.com") } // com starts AFTER 16384 if _, ok := c["com"]; !ok { t.Errorf("bad com") } c = make(map[string]struct{}) compressionLenSearch(c, "example.com", 16376) if _, ok := c["example.com"]; !ok { t.Errorf("bad example.com") } // com starts AFTER 16384 if _, ok := c["com"]; ok { t.Errorf("bad com") } } func TestCompressionLenSearch(t *testing.T) { c := make(map[string]struct{}) compressed, ok := compressionLenSearch(c, "a.b.org.", maxCompressionOffset) if compressed != 0 || ok { t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok) } c["org."] = struct{}{} compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset) if compressed != 4 || !ok { t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok) } c["b.org."] = struct{}{} compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset) if compressed != 2 || !ok { t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok) } // Not found long compression c["x.b.org."] = struct{}{} compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset) if compressed != 2 || !ok { t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok) } // Found long compression c["a.b.org."] = struct{}{} compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset) if compressed != 0 || !ok { t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok) } } func TestMsgLength2(t *testing.T) { // Serialized replies var testMessages = []string{ // google.com. IN A? "064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000", // amazon.com. IN A? (reply has no EDNS0 record) "6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001", // yahoo.com. IN A? "fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000", // microsoft.com. IN A? "f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000", // google.com. IN MX? "724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000", // reddit.com. IN A? "12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000", } for i, hexData := range testMessages { // we won't fail the decoding of the hex input, _ := hex.DecodeString(hexData) m := new(Msg) m.Unpack(input) m.Compress = true lenComp := m.Len() b, _ := m.Pack() pacComp := len(b) m.Compress = false lenUnComp := m.Len() b, _ = m.Pack() pacUnComp := len(b) if pacComp != lenComp { t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i) } if pacUnComp != lenUnComp { t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i) } } } func TestMsgLengthCompressionMalformed(t *testing.T) { // SOA with empty hostmaster, which is illegal soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345}, Ns: ".", Mbox: "", Serial: 0, Refresh: 28800, Retry: 7200, Expire: 604800, Minttl: 60} m := new(Msg) m.Compress = true m.Ns = []RR{soa} m.Len() // Should not crash. } func TestMsgCompressLength2(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion(Fqdn("bliep."), TypeANY) msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "blaat.", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: "foo.bar."}) msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: "foo.bar.", Rrtype: 0x1, Class: 0x1, Ttl: 0x3c}, A: net.IP{0xac, 0x11, 0x0, 0x3}}) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) } } func TestMsgCompressLengthLargeRecords(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("my.service.acme.", TypeSRV) j := 1 for i := 0; i < 250; i++ { target := fmt.Sprintf("host-redis-1-%d.test.acme.com.node.dc1.consul.", i) msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) msg.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)}) } predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Fatalf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) } } func compressionMapsEqual(a map[string]struct{}, b map[string]int) bool { if len(a) != len(b) { return false } for k := range a { if _, ok := b[k]; !ok { return false } } return true } func compressionMapsDifference(a map[string]struct{}, b map[string]int) string { var s strings.Builder var c int fmt.Fprintf(&s, "length compression map (%d):", len(a)) for k := range b { if _, ok := a[k]; !ok { if c > 0 { s.WriteString(",") } fmt.Fprintf(&s, " missing %q", k) c++ } } c = 0 fmt.Fprintf(&s, "\npack compression map (%d):", len(b)) for k := range a { if _, ok := b[k]; !ok { if c > 0 { s.WriteString(",") } fmt.Fprintf(&s, " missing %q", k) c++ } } return s.String() } func TestCompareCompressionMapsForANY(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("a.service.acme.", TypeANY) // Be sure to have more than 14bits for i := 0; i < 2000; i++ { target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i) msg.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)}}) msg.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)}}) if msg.Len() > 16384 { break } } for labelSize := 0; labelSize < 63; labelSize++ { msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeANY) compressionFake := make(map[string]struct{}) lenFake := msgLenWithCompressionMap(msg, compressionFake) compressionReal := make(map[string]int) buf, err := msg.packBufferWithCompressionMap(nil, compressionMap{ext: compressionReal}, true) if err != nil { t.Fatal(err) } if lenFake != len(buf) { t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf)) } if !compressionMapsEqual(compressionFake, compressionReal) { t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n%s", labelSize, compressionMapsDifference(compressionFake, compressionReal)) } } } func TestCompareCompressionMapsForSRV(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("a.service.acme.", TypeSRV) // Be sure to have more than 14bits for i := 0; i < 2000; i++ { target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i) msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: ClassINET, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) msg.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)}}) if msg.Len() > 16384 { break } } for labelSize := 0; labelSize < 63; labelSize++ { msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeAAAA) compressionFake := make(map[string]struct{}) lenFake := msgLenWithCompressionMap(msg, compressionFake) compressionReal := make(map[string]int) buf, err := msg.packBufferWithCompressionMap(nil, compressionMap{ext: compressionReal}, true) if err != nil { t.Fatal(err) } if lenFake != len(buf) { t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf)) } if !compressionMapsEqual(compressionFake, compressionReal) { t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n%s", labelSize, compressionMapsDifference(compressionFake, compressionReal)) } } } func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("my.service.acme.", TypeSRV) for i := 0; i < 250; i++ { target := fmt.Sprintf("host-redis-x-%d.test.acme.com.node.dc1.consul.", i) msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.x.%d.", i)}) } for labelSize := 1; labelSize < 63; labelSize++ { msg.SetQuestion(fmt.Sprintf("my.%s.service.acme.", strings.Repeat("x", labelSize)), TypeSRV) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.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)) } } } func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) { // we want to cross the 14 (16384) bit boundary here, so we build it up to just below and go slightly over. msg := new(Msg) msg.Compress = true msg.SetQuestion("redis.service.consul.", TypeSRV) for i := 0; i < 170; i++ { target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256) msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) msg.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)}) } // msg.Len() == 15458 // msg.Len() == 16470 at 180 for i := 170; i < 181; i++ { target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256) msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) msg.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)}) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.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)) } } } func TestMsgCompressionMultipleQuestions(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("www.example.org.", TypeA) msg.Question = append(msg.Question, Question{"other.example.org.", TypeA, ClassINET}) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf)) } } func TestMsgCompressMultipleCompressedNames(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("www.example.com.", TypeSRV) msg.Answer = append(msg.Answer, &MINFO{ Hdr: RR_Header{Name: "www.example.com.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Rmail: "mail.example.org.", Email: "mail.example.org.", }) msg.Answer = append(msg.Answer, &SOA{ Hdr: RR_Header{Name: "www.example.com.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Ns: "ns.example.net.", Mbox: "mail.example.net.", }) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf)) } } func TestMsgCompressLengthEscapingMatch(t *testing.T) { // Although slightly non-optimal, "example.org." and "ex\\097mple.org." // are not considered equal in the compression map, even though \097 is // a valid escaping of a. This test ensures that the Len code and the // Pack code don't disagree on this. msg := new(Msg) msg.Compress = true msg.SetQuestion("www.example.org.", TypeA) msg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: "ex\\097mple.org.", Rrtype: TypeNS, Class: ClassINET}, Ns: "ns.example.org."}) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf)) } } func TestMsgLengthEscaped(t *testing.T) { msg := new(Msg) msg.SetQuestion(`\000\001\002.\003\004\005\006\007\008\009.\a\b\c.`, TypeA) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf)) } } func TestMsgCompressLengthEscaped(t *testing.T) { msg := new(Msg) msg.Compress = true msg.SetQuestion("www.example.org.", TypeA) msg.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.`}) msg.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."}) predicted := msg.Len() buf, err := msg.Pack() if err != nil { t.Error(err) } if predicted != len(buf) { t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf)) } } golang-github-miekg-dns-1.1.64/listen_no_socket_options.go000066400000000000000000000016311476742671700237000ustar00rootroot00000000000000//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd // +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd package dns import ( "fmt" "net" ) const ( supportsReusePort = false supportsReuseAddr = false ) func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) { if reuseport || reuseaddr { // TODO(tmthrgd): return an error? } return net.Listen(network, addr) } func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) { if reuseport || reuseaddr { // TODO(tmthrgd): return an error? } return net.ListenPacket(network, addr) } // this is just for test compatibility func checkReuseport(fd uintptr) (bool, error) { return false, fmt.Errorf("not supported") } // this is just for test compatibility func checkReuseaddr(fd uintptr) (bool, error) { return false, fmt.Errorf("not supported") } golang-github-miekg-dns-1.1.64/listen_socket_options.go000066400000000000000000000042011476742671700232000ustar00rootroot00000000000000//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd // +build aix darwin dragonfly freebsd linux netbsd openbsd package dns import ( "context" "net" "syscall" "golang.org/x/sys/unix" ) const supportsReusePort = true func reuseportControl(network, address string, c syscall.RawConn) error { var opErr error err := c.Control(func(fd uintptr) { opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) }) if err != nil { return err } return opErr } const supportsReuseAddr = true func reuseaddrControl(network, address string, c syscall.RawConn) error { var opErr error err := c.Control(func(fd uintptr) { opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) }) if err != nil { return err } return opErr } func reuseaddrandportControl(network, address string, c syscall.RawConn) error { err := reuseaddrControl(network, address, c) if err != nil { return err } return reuseportControl(network, address, c) } // this is just for test compatibility func checkReuseport(fd uintptr) (bool, error) { v, err := unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT) if err != nil { return false, err } return v == 1, nil } // this is just for test compatibility func checkReuseaddr(fd uintptr) (bool, error) { v, err := unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR) if err != nil { return false, err } return v == 1, nil } func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) { var lc net.ListenConfig switch { case reuseaddr && reuseport: lc.Control = reuseaddrandportControl case reuseport: lc.Control = reuseportControl case reuseaddr: lc.Control = reuseaddrControl } return lc.Listen(context.Background(), network, addr) } func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) { var lc net.ListenConfig switch { case reuseaddr && reuseport: lc.Control = reuseaddrandportControl case reuseport: lc.Control = reuseportControl case reuseaddr: lc.Control = reuseaddrControl } return lc.ListenPacket(context.Background(), network, addr) } golang-github-miekg-dns-1.1.64/msg.go000066400000000000000000001005641476742671700173560ustar00rootroot00000000000000// DNS packet assembly, see RFC 1035. Converting from - Unpack() - // and to - Pack() - wire format. // All the packers and unpackers take a (msg []byte, off int) // and return (off1 int, ok bool). If they return ok==false, they // also return off1==len(msg), so that the next unpacker will // also fail. This lets us avoid checks of ok until the end of a // packing sequence. package dns //go:generate go run msg_generate.go import ( "crypto/rand" "encoding/binary" "fmt" "math/big" "strconv" "strings" ) const ( maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4 // This is the maximum number of compression pointers that should occur in a // semantically valid message. Each label in a domain name must be at least one // octet and is separated by a period. The root label won't be represented by a // compression pointer to a compression pointer, hence the -2 to exclude the // smallest valid root label. // // It is possible to construct a valid message that has more compression pointers // than this, and still doesn't loop, by pointing to a previous pointer. This is // not something a well written implementation should ever do, so we leave them // to trip the maximum compression pointer check. maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2 // This is the maximum length of a domain name in presentation format. The // maximum wire length of a domain name is 255 octets (see above), with the // maximum label length being 63. The wire format requires one extra byte over // the presentation format, reducing the number of octets by 1. Each label in // the name will be separated by a single period, with each octet in the label // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum // length, then the final label can only be 61 octets long to not exceed the // maximum allowed wire length. maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1 ) // Errors defined in this package. var ( ErrAlg error = &Error{err: "bad algorithm"} // ErrAlg indicates an error with the (DNSSEC) algorithm. ErrAuth error = &Error{err: "bad authentication"} // ErrAuth indicates an error in the TSIG authentication. ErrBuf error = &Error{err: "buffer size too small"} // ErrBuf indicates that the buffer used is too small for the message. ErrConnEmpty error = &Error{err: "conn has no connection"} // ErrConnEmpty indicates a connection is being used before it is initialized. ErrExtendedRcode error = &Error{err: "bad extended rcode"} // ErrExtendedRcode ... ErrFqdn error = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot. ErrId error = &Error{err: "id mismatch"} // ErrId indicates there is a mismatch with the message's ID. ErrKeyAlg error = &Error{err: "bad key algorithm"} // ErrKeyAlg indicates that the algorithm in the key is not valid. ErrKey error = &Error{err: "bad key"} ErrKeySize error = &Error{err: "bad key size"} ErrLongDomain error = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)} ErrNoSig error = &Error{err: "no signature found"} ErrPrivKey error = &Error{err: "bad private key"} ErrRcode error = &Error{err: "bad rcode"} ErrRdata error = &Error{err: "bad rdata"} ErrRRset error = &Error{err: "bad rrset"} ErrSecret error = &Error{err: "no secrets defined"} ErrShortRead error = &Error{err: "short read"} ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. ) // Id by default returns a 16-bit random number to be used as a message id. The // number is drawn from a cryptographically secure random number generator. // This being a variable the function can be reassigned to a custom function. // For instance, to make it return a static value for testing: // // dns.Id = func() uint16 { return 3 } var Id = id // id returns a 16 bits random number to be used as a // message id. The random provided should be good enough. func id() uint16 { var output uint16 err := binary.Read(rand.Reader, binary.BigEndian, &output) if err != nil { panic("dns: reading random id failed: " + err.Error()) } return output } // MsgHdr is a a manually-unpacked version of (id, bits). type MsgHdr struct { Id uint16 Response bool Opcode int Authoritative bool Truncated bool RecursionDesired bool RecursionAvailable bool Zero bool AuthenticatedData bool CheckingDisabled bool Rcode int } // Msg contains the layout of a DNS message. type Msg struct { MsgHdr Compress bool `json:"-"` // If true, the message will be compressed when converted to wire format. Question []Question // Holds the RR(s) of the question section. Answer []RR // Holds the RR(s) of the answer section. Ns []RR // Holds the RR(s) of the authority section. Extra []RR // Holds the RR(s) of the additional section. } // ClassToString is a maps Classes to strings for each CLASS wire type. var ClassToString = map[uint16]string{ ClassINET: "IN", ClassCSNET: "CS", ClassCHAOS: "CH", ClassHESIOD: "HS", ClassNONE: "NONE", ClassANY: "ANY", } // OpcodeToString maps Opcodes to strings. var OpcodeToString = map[int]string{ OpcodeQuery: "QUERY", OpcodeIQuery: "IQUERY", OpcodeStatus: "STATUS", OpcodeNotify: "NOTIFY", OpcodeUpdate: "UPDATE", } // RcodeToString maps Rcodes to strings. var RcodeToString = map[int]string{ RcodeSuccess: "NOERROR", RcodeFormatError: "FORMERR", RcodeServerFailure: "SERVFAIL", RcodeNameError: "NXDOMAIN", RcodeNotImplemented: "NOTIMP", RcodeRefused: "REFUSED", RcodeYXDomain: "YXDOMAIN", // See RFC 2136 RcodeYXRrset: "YXRRSET", RcodeNXRrset: "NXRRSET", RcodeNotAuth: "NOTAUTH", RcodeNotZone: "NOTZONE", RcodeBadSig: "BADSIG", // Also known as RcodeBadVers, see RFC 6891 // RcodeBadVers: "BADVERS", RcodeBadKey: "BADKEY", RcodeBadTime: "BADTIME", RcodeBadMode: "BADMODE", RcodeBadName: "BADNAME", RcodeBadAlg: "BADALG", RcodeBadTrunc: "BADTRUNC", RcodeBadCookie: "BADCOOKIE", } // compressionMap is used to allow a more efficient compression map // to be used for internal packDomainName calls without changing the // signature or functionality of public API. // // In particular, map[string]uint16 uses 25% less per-entry memory // than does map[string]int. type compressionMap struct { ext map[string]int // external callers int map[string]uint16 // internal callers } func (m compressionMap) valid() bool { return m.int != nil || m.ext != nil } func (m compressionMap) insert(s string, pos int) { if m.ext != nil { m.ext[s] = pos } else { m.int[s] = uint16(pos) } } func (m compressionMap) find(s string) (int, bool) { if m.ext != nil { pos, ok := m.ext[s] return pos, ok } pos, ok := m.int[s] return int(pos), ok } // Domain names are a sequence of counted strings // split at the dots. They end with a zero-length string. // PackDomainName packs a domain name s into msg[off:]. // If compression is wanted compress must be true and the compression // map needs to hold a mapping between domain names and offsets // pointing into msg. func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { return packDomainName(s, msg, off, compressionMap{ext: compression}, compress) } func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { // XXX: A logical copy of this function exists in IsDomainName and // should be kept in sync with this function. ls := len(s) if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. return off, nil } // If not fully qualified, error out. if !IsFqdn(s) { return len(msg), ErrFqdn } // Each dot ends a segment of the name. // We trade each dot byte for a length byte. // Except for escaped dots (\.), which are normal dots. // There is also a trailing zero. // Compression pointer := -1 // Emit sequence of counted strings, chopping at dots. var ( begin int compBegin int compOff int bs []byte wasDot bool ) loop: for i := 0; i < ls; i++ { var c byte if bs == nil { c = s[i] } else { c = bs[i] } switch c { case '\\': if off+1 > len(msg) { return len(msg), ErrBuf } if bs == nil { bs = []byte(s) } // check for \DDD if isDDD(bs[i+1:]) { bs[i] = dddToByte(bs[i+1:]) copy(bs[i+1:ls-3], bs[i+4:]) ls -= 3 compOff += 3 } else { copy(bs[i:ls-1], bs[i+1:]) ls-- compOff++ } wasDot = false case '.': if i == 0 && len(s) > 1 { // leading dots are not legal except for the root zone return len(msg), ErrRdata } if wasDot { // two dots back to back is not legal return len(msg), ErrRdata } wasDot = true labelLen := i - begin if labelLen >= 1<<6 { // top two bits of length must be clear return len(msg), ErrRdata } // off can already (we're in a loop) be bigger than len(msg) // this happens when a name isn't fully qualified if off+1+labelLen > len(msg) { return len(msg), ErrBuf } // Don't try to compress '.' // We should only compress when compress is true, but we should also still pick // up names that can be used for *future* compression(s). if compression.valid() && !isRootLabel(s, bs, begin, ls) { if p, ok := compression.find(s[compBegin:]); ok { // The first hit is the longest matching dname // keep the pointer offset we get back and store // the offset of the current name, because that's // where we need to insert the pointer later // If compress is true, we're allowed to compress this dname if compress { pointer = p // Where to point to break loop } } else if off < maxCompressionOffset { // Only offsets smaller than maxCompressionOffset can be used. compression.insert(s[compBegin:], off) } } // The following is covered by the length check above. msg[off] = byte(labelLen) if bs == nil { copy(msg[off+1:], s[begin:i]) } else { copy(msg[off+1:], bs[begin:i]) } off += 1 + labelLen begin = i + 1 compBegin = begin + compOff default: wasDot = false } } // Root label is special if isRootLabel(s, bs, 0, ls) { return off, nil } // If we did compression and we find something add the pointer here if pointer != -1 { // We have two bytes (14 bits) to put the pointer in binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000)) return off + 2, nil } if off < len(msg) { msg[off] = 0 } return off + 1, nil } // isRootLabel returns whether s or bs, from off to end, is the root // label ".". // // If bs is nil, s will be checked, otherwise bs will be checked. func isRootLabel(s string, bs []byte, off, end int) bool { if bs == nil { return s[off:end] == "." } return end-off == 1 && bs[off] == '.' } // Unpack a domain name. // In addition to the simple sequences of counted strings above, // domain names are allowed to refer to strings elsewhere in the // packet, to avoid repeating common suffixes when returning // many entries in a single domain. The pointers are marked // by a length byte with the top two bits set. Ignoring those // two bits, that byte and the next give a 14 bit offset from msg[0] // where we should pick up the trail. // Note that if we jump elsewhere in the packet, // we return off1 == the offset after the first pointer we found, // which is where the next record will start. // In theory, the pointers are only allowed to jump backward. // We let them jump anywhere and stop jumping after a while. // UnpackDomainName unpacks a domain name into a string. It returns // the name, the new offset into msg and any error that occurred. // // When an error is encountered, the unpacked name will be discarded // and len(msg) will be returned as the offset. func UnpackDomainName(msg []byte, off int) (string, int, error) { s := make([]byte, 0, maxDomainNamePresentationLength) off1 := 0 lenmsg := len(msg) budget := maxDomainNameWireOctets ptr := 0 // number of pointers followed Loop: for { if off >= lenmsg { return "", lenmsg, ErrBuf } c := int(msg[off]) off++ switch c & 0xC0 { case 0x00: if c == 0x00 { // end of name break Loop } // literal string if off+c > lenmsg { return "", lenmsg, ErrBuf } budget -= c + 1 // +1 for the label separator if budget <= 0 { return "", lenmsg, ErrLongDomain } for _, b := range msg[off : off+c] { if isDomainNameLabelSpecial(b) { s = append(s, '\\', b) } else if b < ' ' || b > '~' { s = append(s, escapeByte(b)...) } else { s = append(s, b) } } s = append(s, '.') off += c case 0xC0: // pointer to somewhere else in msg. // remember location after first ptr, // since that's how many bytes we consumed. // also, don't follow too many pointers -- // maybe there's a loop. if off >= lenmsg { return "", lenmsg, ErrBuf } c1 := msg[off] off++ if ptr == 0 { off1 = off } if ptr++; ptr > maxCompressionPointers { return "", lenmsg, &Error{err: "too many compression pointers"} } // pointer should guarantee that it advances and points forwards at least // but the condition on previous three lines guarantees that it's // at least loop-free off = (c^0xC0)<<8 | int(c1) default: // 0x80 and 0x40 are reserved return "", lenmsg, ErrRdata } } if ptr == 0 { off1 = off } if len(s) == 0 { return ".", off1, nil } return string(s), off1, nil } func packTxt(txt []string, msg []byte, offset int) (int, error) { if len(txt) == 0 { if offset >= len(msg) { return offset, ErrBuf } msg[offset] = 0 return offset, nil } var err error for _, s := range txt { offset, err = packTxtString(s, msg, offset) if err != nil { return offset, err } } return offset, nil } func packTxtString(s string, msg []byte, offset int) (int, error) { lenByteOffset := offset if offset >= len(msg) || len(s) > 256*4+1 /* If all \DDD */ { return offset, ErrBuf } offset++ for i := 0; i < len(s); i++ { if len(msg) <= offset { return offset, ErrBuf } if s[i] == '\\' { i++ if i == len(s) { break } // check for \DDD if isDDD(s[i:]) { msg[offset] = dddToByte(s[i:]) i += 2 } else { msg[offset] = s[i] } } else { msg[offset] = s[i] } offset++ } l := offset - lenByteOffset - 1 if l > 255 { return offset, &Error{err: "string exceeded 255 bytes in txt"} } msg[lenByteOffset] = byte(l) return offset, nil } func packOctetString(s string, msg []byte, offset int) (int, error) { if offset >= len(msg) || len(s) > 256*4+1 { return offset, ErrBuf } for i := 0; i < len(s); i++ { if len(msg) <= offset { return offset, ErrBuf } if s[i] == '\\' { i++ if i == len(s) { break } // check for \DDD if isDDD(s[i:]) { msg[offset] = dddToByte(s[i:]) i += 2 } else { msg[offset] = s[i] } } else { msg[offset] = s[i] } offset++ } return offset, nil } func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { off = off0 var s string for off < len(msg) && err == nil { s, off, err = unpackString(msg, off) if err == nil { ss = append(ss, s) } } return } // Helpers for dealing with escaped bytes func isDigit(b byte) bool { return b >= '0' && b <= '9' } func isDDD[T ~[]byte | ~string](s T) bool { return len(s) >= 3 && isDigit(s[0]) && isDigit(s[1]) && isDigit(s[2]) } func dddToByte[T ~[]byte | ~string](s T) byte { _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } // Helper function for packing and unpacking func intToBytes(i *big.Int, length int) []byte { buf := i.Bytes() if len(buf) < length { b := make([]byte, length) copy(b[length-len(buf):], buf) return b } return buf } // PackRR packs a resource record rr into msg[off:]. // See PackDomainName for documentation about the compression. func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress) if err == nil { // packRR no longer sets the Rdlength field on the rr, but // callers might be expecting it so we set it here. rr.Header().Rdlength = uint16(off1 - headerEnd) } return off1, err } func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) { if rr == nil { return len(msg), len(msg), &Error{err: "nil rr"} } headerEnd, err = rr.Header().packHeader(msg, off, compression, compress) if err != nil { return headerEnd, len(msg), err } off1, err = rr.pack(msg, headerEnd, compression, compress) if err != nil { return headerEnd, len(msg), err } rdlength := off1 - headerEnd if int(uint16(rdlength)) != rdlength { // overflow return headerEnd, len(msg), ErrRdata } // The RDLENGTH field is the last field in the header and we set it here. binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength)) return headerEnd, off1, nil } // UnpackRR unpacks msg[off:] into an RR. func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { h, off, msg, err := unpackHeader(msg, off) if err != nil { return nil, len(msg), err } return UnpackRRWithHeader(h, msg, off) } // UnpackRRWithHeader unpacks the record type specific payload given an existing // RR_Header. func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) { if newFn, ok := TypeToRR[h.Rrtype]; ok { rr = newFn() *rr.Header() = h } else { rr = &RFC3597{Hdr: h} } if off < 0 || off > len(msg) { return &h, off, &Error{err: "bad off"} } end := off + int(h.Rdlength) if end < off || end > len(msg) { return &h, end, &Error{err: "bad rdlength"} } if noRdata(h) { return rr, off, nil } off, err = rr.unpack(msg, off) if err != nil { return nil, end, err } if off != end { return &h, end, &Error{err: "bad rdlength"} } return rr, off, nil } // unpackRRslice unpacks msg[off:] into an []RR. // If we cannot unpack the whole array, then it will return nil func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) { var r RR // Don't pre-allocate, l may be under attacker control var dst []RR for i := 0; i < l; i++ { off1 := off r, off, err = UnpackRR(msg, off) if err != nil { off = len(msg) break } // If offset does not increase anymore, l is a lie if off1 == off { break } dst = append(dst, r) } if err != nil && off == len(msg) { dst = nil } return dst, off, err } // Convert a MsgHdr to a string, with dig-like headers: // // ;; opcode: QUERY, status: NOERROR, id: 48404 // // ;; flags: qr aa rd ra; func (h *MsgHdr) String() string { if h == nil { return " MsgHdr" } s := ";; opcode: " + OpcodeToString[h.Opcode] s += ", status: " + RcodeToString[h.Rcode] s += ", id: " + strconv.Itoa(int(h.Id)) + "\n" s += ";; flags:" if h.Response { s += " qr" } if h.Authoritative { s += " aa" } if h.Truncated { s += " tc" } if h.RecursionDesired { s += " rd" } if h.RecursionAvailable { s += " ra" } if h.Zero { // Hmm s += " z" } if h.AuthenticatedData { s += " ad" } if h.CheckingDisabled { s += " cd" } s += ";" return s } // Pack packs a Msg: it is converted to wire format. // If the dns.Compress is true the message will be in compressed wire format. func (dns *Msg) Pack() (msg []byte, err error) { return dns.PackBuffer(nil) } // PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated. func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { // If this message can't be compressed, avoid filling the // compression map and creating garbage. if dns.Compress && dns.isCompressible() { compression := make(map[string]uint16) // Compression pointer mappings. return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true) } return dns.packBufferWithCompressionMap(buf, compressionMap{}, false) } // packBufferWithCompressionMap packs a Msg, using the given buffer buf. func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) { if dns.Rcode < 0 || dns.Rcode > 0xFFF { return nil, ErrRcode } // Set extended rcode unconditionally if we have an opt, this will allow // resetting the extended rcode bits if they need to. if opt := dns.IsEdns0(); opt != nil { opt.SetExtendedRcode(uint16(dns.Rcode)) } else if dns.Rcode > 0xF { // If Rcode is an extended one and opt is nil, error out. return nil, ErrExtendedRcode } // Convert convenient Msg into wire-like Header. var dh Header dh.Id = dns.Id dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF) if dns.Response { dh.Bits |= _QR } if dns.Authoritative { dh.Bits |= _AA } if dns.Truncated { dh.Bits |= _TC } if dns.RecursionDesired { dh.Bits |= _RD } if dns.RecursionAvailable { dh.Bits |= _RA } if dns.Zero { dh.Bits |= _Z } if dns.AuthenticatedData { dh.Bits |= _AD } if dns.CheckingDisabled { dh.Bits |= _CD } dh.Qdcount = uint16(len(dns.Question)) dh.Ancount = uint16(len(dns.Answer)) dh.Nscount = uint16(len(dns.Ns)) dh.Arcount = uint16(len(dns.Extra)) // We need the uncompressed length here, because we first pack it and then compress it. msg = buf uncompressedLen := msgLenWithCompressionMap(dns, nil) if packLen := uncompressedLen + 1; len(msg) < packLen { msg = make([]byte, packLen) } // Pack it in: header and then the pieces. off := 0 off, err = dh.pack(msg, off, compression, compress) if err != nil { return nil, err } for _, r := range dns.Question { off, err = r.pack(msg, off, compression, compress) if err != nil { return nil, err } } for _, r := range dns.Answer { _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } for _, r := range dns.Ns { _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } for _, r := range dns.Extra { _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } return msg[:off], nil } func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { // If we are at the end of the message we should return *just* the // header. This can still be useful to the caller. 9.9.9.9 sends these // when responding with REFUSED for instance. if off == len(msg) { // reset sections before returning dns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil return nil } // Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are // attacker controlled. This means we can't use them to pre-allocate // slices. dns.Question = nil for i := 0; i < int(dh.Qdcount); i++ { off1 := off var q Question q, off, err = unpackQuestion(msg, off) if err != nil { return err } if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie! dh.Qdcount = uint16(i) break } dns.Question = append(dns.Question, q) } dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off) // The header counts might have been wrong so we need to update it dh.Ancount = uint16(len(dns.Answer)) if err == nil { dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off) } // The header counts might have been wrong so we need to update it dh.Nscount = uint16(len(dns.Ns)) if err == nil { dns.Extra, _, err = unpackRRslice(int(dh.Arcount), msg, off) } // The header counts might have been wrong so we need to update it dh.Arcount = uint16(len(dns.Extra)) // Set extended Rcode if opt := dns.IsEdns0(); opt != nil { dns.Rcode |= opt.ExtendedRcode() } // TODO(miek) make this an error? // use PackOpt to let people tell how detailed the error reporting should be? // if off != len(msg) { // // println("dns: extra bytes in dns packet", off, "<", len(msg)) // } return err } // Unpack unpacks a binary message to a Msg structure. func (dns *Msg) Unpack(msg []byte) (err error) { dh, off, err := unpackMsgHdr(msg, 0) if err != nil { return err } dns.setHdr(dh) return dns.unpack(dh, msg, off) } // Convert a complete message to a string with dig-like output. func (dns *Msg) String() string { if dns == nil { return " MsgHdr" } s := dns.MsgHdr.String() + " " if dns.MsgHdr.Opcode == OpcodeUpdate { s += "ZONE: " + strconv.Itoa(len(dns.Question)) + ", " s += "PREREQ: " + strconv.Itoa(len(dns.Answer)) + ", " s += "UPDATE: " + strconv.Itoa(len(dns.Ns)) + ", " s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" } else { s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" } opt := dns.IsEdns0() if opt != nil { // OPT PSEUDOSECTION s += opt.String() + "\n" } if len(dns.Question) > 0 { if dns.MsgHdr.Opcode == OpcodeUpdate { s += "\n;; ZONE SECTION:\n" } else { s += "\n;; QUESTION SECTION:\n" } for _, r := range dns.Question { s += r.String() + "\n" } } if len(dns.Answer) > 0 { if dns.MsgHdr.Opcode == OpcodeUpdate { s += "\n;; PREREQUISITE SECTION:\n" } else { s += "\n;; ANSWER SECTION:\n" } for _, r := range dns.Answer { if r != nil { s += r.String() + "\n" } } } if len(dns.Ns) > 0 { if dns.MsgHdr.Opcode == OpcodeUpdate { s += "\n;; UPDATE SECTION:\n" } else { s += "\n;; AUTHORITY SECTION:\n" } for _, r := range dns.Ns { if r != nil { s += r.String() + "\n" } } } if len(dns.Extra) > 0 && (opt == nil || len(dns.Extra) > 1) { s += "\n;; ADDITIONAL SECTION:\n" for _, r := range dns.Extra { if r != nil && r.Header().Rrtype != TypeOPT { s += r.String() + "\n" } } } return s } // isCompressible returns whether the msg may be compressible. func (dns *Msg) isCompressible() bool { // If we only have one question, there is nothing we can ever compress. return len(dns.Question) > 1 || len(dns.Answer) > 0 || len(dns.Ns) > 0 || len(dns.Extra) > 0 } // Len returns the message length when in (un)compressed wire format. // If dns.Compress is true compression it is taken into account. Len() // is provided to be a faster way to get the size of the resulting packet, // than packing it, measuring the size and discarding the buffer. func (dns *Msg) Len() int { // If this message can't be compressed, avoid filling the // compression map and creating garbage. if dns.Compress && dns.isCompressible() { compression := make(map[string]struct{}) return msgLenWithCompressionMap(dns, compression) } return msgLenWithCompressionMap(dns, nil) } func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int { l := headerSize for _, r := range dns.Question { l += r.len(l, compression) } for _, r := range dns.Answer { if r != nil { l += r.len(l, compression) } } for _, r := range dns.Ns { if r != nil { l += r.len(l, compression) } } for _, r := range dns.Extra { if r != nil { l += r.len(l, compression) } } return l } func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int { if s == "" || s == "." { return 1 } escaped := strings.Contains(s, "\\") if compression != nil && (compress || off < maxCompressionOffset) { // compressionLenSearch will insert the entry into the compression // map if it doesn't contain it. if l, ok := compressionLenSearch(compression, s, off); ok && compress { if escaped { return escapedNameLen(s[:l]) + 2 } return l + 2 } } if escaped { return escapedNameLen(s) + 1 } return len(s) + 1 } func escapedNameLen(s string) int { nameLen := len(s) for i := 0; i < len(s); i++ { if s[i] != '\\' { continue } if isDDD(s[i+1:]) { nameLen -= 3 i += 3 } else { nameLen-- i++ } } return nameLen } func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) { for off, end := 0, false; !end; off, end = NextLabel(s, off) { if _, ok := c[s[off:]]; ok { return off, true } if msgOff+off < maxCompressionOffset { c[s[off:]] = struct{}{} } } return 0, false } // Copy returns a new RR which is a deep-copy of r. func Copy(r RR) RR { return r.copy() } // Len returns the length (in octets) of the uncompressed RR in wire format. func Len(r RR) int { return r.len(0, nil) } // Copy returns a new *Msg which is a deep-copy of dns. func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) } // CopyTo copies the contents to the provided message using a deep-copy and returns the copy. func (dns *Msg) CopyTo(r1 *Msg) *Msg { r1.MsgHdr = dns.MsgHdr r1.Compress = dns.Compress if len(dns.Question) > 0 { // TODO(miek): Question is an immutable value, ok to do a shallow-copy r1.Question = cloneSlice(dns.Question) } rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) r1.Answer, rrArr = rrArr[:0:len(dns.Answer)], rrArr[len(dns.Answer):] r1.Ns, rrArr = rrArr[:0:len(dns.Ns)], rrArr[len(dns.Ns):] r1.Extra = rrArr[:0:len(dns.Extra)] for _, r := range dns.Answer { r1.Answer = append(r1.Answer, r.copy()) } for _, r := range dns.Ns { r1.Ns = append(r1.Ns, r.copy()) } for _, r := range dns.Extra { r1.Extra = append(r1.Extra, r.copy()) } return r1 } func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { off, err := packDomainName(q.Name, msg, off, compression, compress) if err != nil { return off, err } off, err = packUint16(q.Qtype, msg, off) if err != nil { return off, err } off, err = packUint16(q.Qclass, msg, off) if err != nil { return off, err } return off, nil } func unpackQuestion(msg []byte, off int) (Question, int, error) { var ( q Question err error ) q.Name, off, err = UnpackDomainName(msg, off) if err != nil { return q, off, err } if off == len(msg) { return q, off, nil } q.Qtype, off, err = unpackUint16(msg, off) if err != nil { return q, off, err } if off == len(msg) { return q, off, nil } q.Qclass, off, err = unpackUint16(msg, off) if off == len(msg) { return q, off, nil } return q, off, err } func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { off, err := packUint16(dh.Id, msg, off) if err != nil { return off, err } off, err = packUint16(dh.Bits, msg, off) if err != nil { return off, err } off, err = packUint16(dh.Qdcount, msg, off) if err != nil { return off, err } off, err = packUint16(dh.Ancount, msg, off) if err != nil { return off, err } off, err = packUint16(dh.Nscount, msg, off) if err != nil { return off, err } off, err = packUint16(dh.Arcount, msg, off) if err != nil { return off, err } return off, nil } func unpackMsgHdr(msg []byte, off int) (Header, int, error) { var ( dh Header err error ) dh.Id, off, err = unpackUint16(msg, off) if err != nil { return dh, off, err } dh.Bits, off, err = unpackUint16(msg, off) if err != nil { return dh, off, err } dh.Qdcount, off, err = unpackUint16(msg, off) if err != nil { return dh, off, err } dh.Ancount, off, err = unpackUint16(msg, off) if err != nil { return dh, off, err } dh.Nscount, off, err = unpackUint16(msg, off) if err != nil { return dh, off, err } dh.Arcount, off, err = unpackUint16(msg, off) if err != nil { return dh, off, err } return dh, off, nil } // setHdr set the header in the dns using the binary data in dh. func (dns *Msg) setHdr(dh Header) { dns.Id = dh.Id dns.Response = dh.Bits&_QR != 0 dns.Opcode = int(dh.Bits>>11) & 0xF dns.Authoritative = dh.Bits&_AA != 0 dns.Truncated = dh.Bits&_TC != 0 dns.RecursionDesired = dh.Bits&_RD != 0 dns.RecursionAvailable = dh.Bits&_RA != 0 dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite. dns.AuthenticatedData = dh.Bits&_AD != 0 dns.CheckingDisabled = dh.Bits&_CD != 0 dns.Rcode = int(dh.Bits & 0xF) } golang-github-miekg-dns-1.1.64/msg_generate.go000066400000000000000000000246721476742671700212350ustar00rootroot00000000000000//go:build ignore // +build ignore // msg_generate.go is meant to run with go generate. It will use // go/{importer,types} to track down all the RR struct types. Then for each type // it will generate pack/unpack methods based on the struct tags. The generated source is // written to zmsg.go, and is meant to be checked into git. package main import ( "bytes" "fmt" "go/format" "go/types" "log" "os" "strings" "golang.org/x/tools/go/packages" ) var packageHdr = ` // Code generated by "go run msg_generate.go"; DO NOT EDIT. package dns ` // getTypeStruct will take a type and the package scope, and return the // (innermost) struct if the type is considered a RR type (currently defined as // those structs beginning with a RR_Header, could be redefined as implementing // the RR interface). The bool return value indicates if embedded structs were // resolved. func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { st, ok := t.Underlying().(*types.Struct) if !ok { return nil, false } if st.NumFields() == 0 { return nil, false } if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { return st, false } if st.Field(0).Anonymous() { st, _ := getTypeStruct(st.Field(0).Type(), scope) return st, true } return nil, false } // loadModule retrieves package description for a given module. func loadModule(name string) (*types.Package, error) { conf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo} pkgs, err := packages.Load(&conf, name) if err != nil { return nil, err } return pkgs[0].Types, nil } func main() { // Import and type-check the package pkg, err := loadModule("github.com/miekg/dns") fatalIfErr(err) scope := pkg.Scope() // Collect actual types (*X) var namedTypes []string for _, name := range scope.Names() { o := scope.Lookup(name) if o == nil || !o.Exported() { continue } if st, _ := getTypeStruct(o.Type(), scope); st == nil { continue } if name == "PrivateRR" { continue } // Check if corresponding TypeX exists if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" { log.Fatalf("Constant Type%s does not exist.", o.Name()) } namedTypes = append(namedTypes, o.Name()) } b := &bytes.Buffer{} b.WriteString(packageHdr) fmt.Fprint(b, "// pack*() functions\n\n") for _, name := range namedTypes { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n", name) for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) fmt.Fprint(b, `if err != nil { return off, err } `) } if _, ok := st.Field(i).Type().(*types.Slice); ok { switch st.Tag(i) { case `dns:"-"`: // ignored case `dns:"txt"`: o("off, err = packStringTxt(rr.%s, msg, off)\n") case `dns:"opt"`: o("off, err = packDataOpt(rr.%s, msg, off)\n") case `dns:"nsec"`: o("off, err = packDataNsec(rr.%s, msg, off)\n") case `dns:"pairs"`: o("off, err = packDataSVCB(rr.%s, msg, off)\n") case `dns:"domain-name"`: o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n") case `dns:"apl"`: o("off, err = packDataApl(rr.%s, msg, off)\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } continue } switch { case st.Tag(i) == `dns:"-"`: // ignored case st.Tag(i) == `dns:"cdomain-name"`: o("off, err = packDomainName(rr.%s, msg, off, compression, compress)\n") case st.Tag(i) == `dns:"domain-name"`: o("off, err = packDomainName(rr.%s, msg, off, compression, false)\n") case st.Tag(i) == `dns:"a"`: o("off, err = packDataA(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"aaaa"`: o("off, err = packDataAAAA(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"uint48"`: o("off, err = packUint48(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"txt"`: o("off, err = packString(rr.%s, msg, off)\n") case strings.HasPrefix(st.Tag(i), `dns:"size-base32`): // size-base32 can be packed just like base32 fallthrough case st.Tag(i) == `dns:"base32"`: o("off, err = packStringBase32(rr.%s, msg, off)\n") case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): // size-base64 can be packed just like base64 fallthrough case st.Tag(i) == `dns:"base64"`: o("off, err = packStringBase64(rr.%s, msg, off)\n") case strings.HasPrefix(st.Tag(i), `dns:"size-hex:SaltLength`): // directly write instead of using o() so we get the error check in the correct place field := st.Field(i).Name() fmt.Fprintf(b, `// Only pack salt if value is not "-", i.e. empty if rr.%s != "-" { off, err = packStringHex(rr.%s, msg, off) if err != nil { return off, err } } `, field, field) continue case strings.HasPrefix(st.Tag(i), `dns:"size-hex`): // size-hex can be packed just like hex fallthrough case st.Tag(i) == `dns:"hex"`: o("off, err = packStringHex(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"any"`: o("off, err = packStringAny(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"octet"`: o("off, err = packStringOctet(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"ipsechost"` || st.Tag(i) == `dns:"amtrelayhost"`: o("off, err = packIPSECGateway(rr.GatewayAddr, rr.%s, msg, off, rr.GatewayType, compression, false)\n") case st.Tag(i) == "": switch st.Field(i).Type().(*types.Basic).Kind() { case types.Uint8: o("off, err = packUint8(rr.%s, msg, off)\n") case types.Uint16: o("off, err = packUint16(rr.%s, msg, off)\n") case types.Uint32: o("off, err = packUint32(rr.%s, msg, off)\n") case types.Uint64: o("off, err = packUint64(rr.%s, msg, off)\n") case types.String: o("off, err = packString(rr.%s, msg, off)\n") default: log.Fatalln(name, st.Field(i).Name()) } default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } } fmt.Fprintln(b, "return off, nil }\n") } fmt.Fprint(b, "// unpack*() functions\n\n") for _, name := range namedTypes { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) fmt.Fprintf(b, "func (rr *%s) unpack(msg []byte, off int) (off1 int, err error) {\n", name) fmt.Fprint(b, `rdStart := off _ = rdStart `) for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) fmt.Fprint(b, `if err != nil { return off, err } `) } // size-* are special, because they reference a struct member we should use for the length. if strings.HasPrefix(st.Tag(i), `dns:"size-`) { structMember := structMember(st.Tag(i)) structTag := structTag(st.Tag(i)) switch structTag { case "hex": fmt.Fprintf(b, "rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember) case "base32": fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember) case "base64": fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember) default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } fmt.Fprint(b, `if err != nil { return off, err } `) continue } if _, ok := st.Field(i).Type().(*types.Slice); ok { switch st.Tag(i) { case `dns:"-"`: // ignored case `dns:"txt"`: o("rr.%s, off, err = unpackStringTxt(msg, off)\n") case `dns:"opt"`: o("rr.%s, off, err = unpackDataOpt(msg, off)\n") case `dns:"nsec"`: o("rr.%s, off, err = unpackDataNsec(msg, off)\n") case `dns:"pairs"`: o("rr.%s, off, err = unpackDataSVCB(msg, off)\n") case `dns:"domain-name"`: o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n") case `dns:"apl"`: o("rr.%s, off, err = unpackDataApl(msg, off)\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } continue } switch st.Tag(i) { case `dns:"-"`: // ignored case `dns:"cdomain-name"`: fallthrough case `dns:"domain-name"`: o("rr.%s, off, err = UnpackDomainName(msg, off)\n") case `dns:"a"`: o("rr.%s, off, err = unpackDataA(msg, off)\n") case `dns:"aaaa"`: o("rr.%s, off, err = unpackDataAAAA(msg, off)\n") case `dns:"uint48"`: o("rr.%s, off, err = unpackUint48(msg, off)\n") case `dns:"txt"`: o("rr.%s, off, err = unpackString(msg, off)\n") case `dns:"base32"`: o("rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\n") case `dns:"base64"`: o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n") case `dns:"hex"`: o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n") case `dns:"any"`: o("rr.%s, off, err = unpackStringAny(msg, off, rdStart + int(rr.Hdr.Rdlength))\n") case `dns:"octet"`: o("rr.%s, off, err = unpackStringOctet(msg, off)\n") case `dns:"ipsechost"`, `dns:"amtrelayhost"`: o("rr.GatewayAddr, rr.%s, off, err = unpackIPSECGateway(msg, off, rr.GatewayType)\n") case "": switch st.Field(i).Type().(*types.Basic).Kind() { case types.Uint8: o("rr.%s, off, err = unpackUint8(msg, off)\n") case types.Uint16: o("rr.%s, off, err = unpackUint16(msg, off)\n") case types.Uint32: o("rr.%s, off, err = unpackUint32(msg, off)\n") case types.Uint64: o("rr.%s, off, err = unpackUint64(msg, off)\n") case types.String: o("rr.%s, off, err = unpackString(msg, off)\n") default: log.Fatalln(name, st.Field(i).Name()) } default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } // If we've hit len(msg) we return without error. if i < st.NumFields()-1 { fmt.Fprint(b, `if off == len(msg) { return off, nil } `) } } fmt.Fprintf(b, "return off, nil }\n\n") } // gofmt res, err := format.Source(b.Bytes()) if err != nil { b.WriteTo(os.Stderr) log.Fatal(err) } // write result f, err := os.Create("zmsg.go") fatalIfErr(err) defer f.Close() f.Write(res) } // structMember will take a tag like dns:"size-base32:SaltLength" and return the last part of this string. func structMember(s string) string { idx := strings.LastIndex(s, ":") return strings.TrimSuffix(s[idx+1:], `"`) } // structTag will take a tag like dns:"size-base32:SaltLength" and return base32. func structTag(s string) string { s = strings.TrimPrefix(s, `dns:"size-`) s, _, _ = strings.Cut(s, ":") return s } func fatalIfErr(err error) { if err != nil { log.Fatal(err) } } golang-github-miekg-dns-1.1.64/msg_helpers.go000066400000000000000000000536231476742671700211030ustar00rootroot00000000000000package dns import ( "encoding/base32" "encoding/base64" "encoding/binary" "encoding/hex" "net" "sort" "strings" ) // helper functions called from the generated zmsg.go // These function are named after the tag to help pack/unpack, if there is no tag it is the name // of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or // packDataDomainName. func unpackDataA(msg []byte, off int) (net.IP, int, error) { if off+net.IPv4len > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking a"} } return cloneSlice(msg[off : off+net.IPv4len]), off + net.IPv4len, nil } func packDataA(a net.IP, msg []byte, off int) (int, error) { switch len(a) { case net.IPv4len, net.IPv6len: // It must be a slice of 4, even if it is 16, we encode only the first 4 if off+net.IPv4len > len(msg) { return len(msg), &Error{err: "overflow packing a"} } copy(msg[off:], a.To4()) off += net.IPv4len case 0: // Allowed, for dynamic updates. default: return len(msg), &Error{err: "overflow packing a"} } return off, nil } func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) { if off+net.IPv6len > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking aaaa"} } return cloneSlice(msg[off : off+net.IPv6len]), off + net.IPv6len, nil } func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) { switch len(aaaa) { case net.IPv6len: if off+net.IPv6len > len(msg) { return len(msg), &Error{err: "overflow packing aaaa"} } copy(msg[off:], aaaa) off += net.IPv6len case 0: // Allowed, dynamic updates. default: return len(msg), &Error{err: "overflow packing aaaa"} } return off, nil } // unpackHeader unpacks an RR header, returning the offset to the end of the header and a // re-sliced msg according to the expected length of the RR. func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) { hdr := RR_Header{} if off == len(msg) { return hdr, off, msg, nil } hdr.Name, off, err = UnpackDomainName(msg, off) if err != nil { return hdr, len(msg), msg, err } hdr.Rrtype, off, err = unpackUint16(msg, off) if err != nil { return hdr, len(msg), msg, err } hdr.Class, off, err = unpackUint16(msg, off) if err != nil { return hdr, len(msg), msg, err } hdr.Ttl, off, err = unpackUint32(msg, off) if err != nil { return hdr, len(msg), msg, err } hdr.Rdlength, off, err = unpackUint16(msg, off) if err != nil { return hdr, len(msg), msg, err } msg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength) return hdr, off, msg, err } // packHeader packs an RR header, returning the offset to the end of the header. // See PackDomainName for documentation about the compression. func (hdr RR_Header) packHeader(msg []byte, off int, compression compressionMap, compress bool) (int, error) { if off == len(msg) { return off, nil } off, err := packDomainName(hdr.Name, msg, off, compression, compress) if err != nil { return len(msg), err } off, err = packUint16(hdr.Rrtype, msg, off) if err != nil { return len(msg), err } off, err = packUint16(hdr.Class, msg, off) if err != nil { return len(msg), err } off, err = packUint32(hdr.Ttl, msg, off) if err != nil { return len(msg), err } off, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR. if err != nil { return len(msg), err } return off, nil } // helper helper functions. // truncateMsgFromRdLength truncates msg to match the expected length of the RR. // Returns an error if msg is smaller than the expected size. func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) { lenrd := off + int(rdlength) if lenrd > len(msg) { return msg, &Error{err: "overflowing header size"} } return msg[:lenrd], nil } var base32HexNoPadEncoding = base32.HexEncoding.WithPadding(base32.NoPadding) func fromBase32(s []byte) (buf []byte, err error) { for i, b := range s { if b >= 'a' && b <= 'z' { s[i] = b - 32 } } buflen := base32HexNoPadEncoding.DecodedLen(len(s)) buf = make([]byte, buflen) n, err := base32HexNoPadEncoding.Decode(buf, s) buf = buf[:n] return } func toBase32(b []byte) string { return base32HexNoPadEncoding.EncodeToString(b) } func fromBase64(s []byte) (buf []byte, err error) { buflen := base64.StdEncoding.DecodedLen(len(s)) buf = make([]byte, buflen) n, err := base64.StdEncoding.Decode(buf, s) buf = buf[:n] return } func toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) } // dynamicUpdate returns true if the Rdlength is zero. func noRdata(h RR_Header) bool { return h.Rdlength == 0 } func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) { if off+1 > len(msg) { return 0, len(msg), &Error{err: "overflow unpacking uint8"} } return msg[off], off + 1, nil } func packUint8(i uint8, msg []byte, off int) (off1 int, err error) { if off+1 > len(msg) { return len(msg), &Error{err: "overflow packing uint8"} } msg[off] = i return off + 1, nil } func unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) { if off+2 > len(msg) { return 0, len(msg), &Error{err: "overflow unpacking uint16"} } return binary.BigEndian.Uint16(msg[off:]), off + 2, nil } func packUint16(i uint16, msg []byte, off int) (off1 int, err error) { if off+2 > len(msg) { return len(msg), &Error{err: "overflow packing uint16"} } binary.BigEndian.PutUint16(msg[off:], i) return off + 2, nil } func unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) { if off+4 > len(msg) { return 0, len(msg), &Error{err: "overflow unpacking uint32"} } return binary.BigEndian.Uint32(msg[off:]), off + 4, nil } func packUint32(i uint32, msg []byte, off int) (off1 int, err error) { if off+4 > len(msg) { return len(msg), &Error{err: "overflow packing uint32"} } binary.BigEndian.PutUint32(msg[off:], i) return off + 4, nil } func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) { if off+6 > len(msg) { return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"} } // Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes) i = uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 | uint64(msg[off+4])<<8 | uint64(msg[off+5]) off += 6 return i, off, nil } func packUint48(i uint64, msg []byte, off int) (off1 int, err error) { if off+6 > len(msg) { return len(msg), &Error{err: "overflow packing uint64 as uint48"} } msg[off] = byte(i >> 40) msg[off+1] = byte(i >> 32) msg[off+2] = byte(i >> 24) msg[off+3] = byte(i >> 16) msg[off+4] = byte(i >> 8) msg[off+5] = byte(i) off += 6 return off, nil } func unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) { if off+8 > len(msg) { return 0, len(msg), &Error{err: "overflow unpacking uint64"} } return binary.BigEndian.Uint64(msg[off:]), off + 8, nil } func packUint64(i uint64, msg []byte, off int) (off1 int, err error) { if off+8 > len(msg) { return len(msg), &Error{err: "overflow packing uint64"} } binary.BigEndian.PutUint64(msg[off:], i) off += 8 return off, nil } func unpackString(msg []byte, off int) (string, int, error) { if off+1 > len(msg) { return "", off, &Error{err: "overflow unpacking txt"} } l := int(msg[off]) off++ if off+l > len(msg) { return "", off, &Error{err: "overflow unpacking txt"} } var s strings.Builder consumed := 0 for i, b := range msg[off : off+l] { switch { case b == '"' || b == '\\': if consumed == 0 { s.Grow(l * 2) } s.Write(msg[off+consumed : off+i]) s.WriteByte('\\') s.WriteByte(b) consumed = i + 1 case b < ' ' || b > '~': // unprintable if consumed == 0 { s.Grow(l * 2) } s.Write(msg[off+consumed : off+i]) s.WriteString(escapeByte(b)) consumed = i + 1 } } if consumed == 0 { // no escaping needed return string(msg[off : off+l]), off + l, nil } s.Write(msg[off+consumed : off+l]) return s.String(), off + l, nil } func packString(s string, msg []byte, off int) (int, error) { off, err := packTxtString(s, msg, off) if err != nil { return len(msg), err } return off, nil } func unpackStringBase32(msg []byte, off, end int) (string, int, error) { if end > len(msg) { return "", len(msg), &Error{err: "overflow unpacking base32"} } s := toBase32(msg[off:end]) return s, end, nil } func packStringBase32(s string, msg []byte, off int) (int, error) { b32, err := fromBase32([]byte(s)) if err != nil { return len(msg), err } if off+len(b32) > len(msg) { return len(msg), &Error{err: "overflow packing base32"} } copy(msg[off:off+len(b32)], b32) off += len(b32) return off, nil } func unpackStringBase64(msg []byte, off, end int) (string, int, error) { // Rest of the RR is base64 encoded value, so we don't need an explicit length // to be set. Thus far all RR's that have base64 encoded fields have those as their // last one. What we do need is the end of the RR! if end > len(msg) { return "", len(msg), &Error{err: "overflow unpacking base64"} } s := toBase64(msg[off:end]) return s, end, nil } func packStringBase64(s string, msg []byte, off int) (int, error) { b64, err := fromBase64([]byte(s)) if err != nil { return len(msg), err } if off+len(b64) > len(msg) { return len(msg), &Error{err: "overflow packing base64"} } copy(msg[off:off+len(b64)], b64) off += len(b64) return off, nil } func unpackStringHex(msg []byte, off, end int) (string, int, error) { // Rest of the RR is hex encoded value, so we don't need an explicit length // to be set. NSEC and TSIG have hex fields with a length field. // What we do need is the end of the RR! if end > len(msg) { return "", len(msg), &Error{err: "overflow unpacking hex"} } s := hex.EncodeToString(msg[off:end]) return s, end, nil } func packStringHex(s string, msg []byte, off int) (int, error) { h, err := hex.DecodeString(s) if err != nil { return len(msg), err } if off+len(h) > len(msg) { return len(msg), &Error{err: "overflow packing hex"} } copy(msg[off:off+len(h)], h) off += len(h) return off, nil } func unpackStringAny(msg []byte, off, end int) (string, int, error) { if end > len(msg) { return "", len(msg), &Error{err: "overflow unpacking anything"} } return string(msg[off:end]), end, nil } func packStringAny(s string, msg []byte, off int) (int, error) { if off+len(s) > len(msg) { return len(msg), &Error{err: "overflow packing anything"} } copy(msg[off:off+len(s)], s) off += len(s) return off, nil } func unpackStringTxt(msg []byte, off int) ([]string, int, error) { txt, off, err := unpackTxt(msg, off) if err != nil { return nil, len(msg), err } return txt, off, nil } func packStringTxt(s []string, msg []byte, off int) (int, error) { off, err := packTxt(s, msg, off) if err != nil { return len(msg), err } return off, nil } func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) { var edns []EDNS0 for off < len(msg) { if off+4 > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking opt"} } code := binary.BigEndian.Uint16(msg[off:]) off += 2 optlen := binary.BigEndian.Uint16(msg[off:]) off += 2 if off+int(optlen) > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking opt"} } opt := makeDataOpt(code) if err := opt.unpack(msg[off : off+int(optlen)]); err != nil { return nil, len(msg), err } edns = append(edns, opt) off += int(optlen) } return edns, off, nil } func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) { for _, el := range options { b, err := el.pack() if err != nil || off+4 > len(msg) { return len(msg), &Error{err: "overflow packing opt"} } binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length off += 4 if off+len(b) > len(msg) { return len(msg), &Error{err: "overflow packing opt"} } // Actual data copy(msg[off:off+len(b)], b) off += len(b) } return off, nil } func unpackStringOctet(msg []byte, off int) (string, int, error) { s := string(msg[off:]) return s, len(msg), nil } func packStringOctet(s string, msg []byte, off int) (int, error) { off, err := packOctetString(s, msg, off) if err != nil { return len(msg), err } return off, nil } func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) { var nsec []uint16 length, window, lastwindow := 0, 0, -1 for off < len(msg) { if off+2 > len(msg) { return nsec, len(msg), &Error{err: "overflow unpacking NSEC(3)"} } window = int(msg[off]) length = int(msg[off+1]) off += 2 if window <= lastwindow { // RFC 4034: Blocks are present in the NSEC RR RDATA in // increasing numerical order. return nsec, len(msg), &Error{err: "out of order NSEC(3) block in type bitmap"} } if length == 0 { // RFC 4034: Blocks with no types present MUST NOT be included. return nsec, len(msg), &Error{err: "empty NSEC(3) block in type bitmap"} } if length > 32 { return nsec, len(msg), &Error{err: "NSEC(3) block too long in type bitmap"} } if off+length > len(msg) { return nsec, len(msg), &Error{err: "overflowing NSEC(3) block in type bitmap"} } // Walk the bytes in the window and extract the type bits for j, b := range msg[off : off+length] { // Check the bits one by one, and set the type if b&0x80 == 0x80 { nsec = append(nsec, uint16(window*256+j*8+0)) } if b&0x40 == 0x40 { nsec = append(nsec, uint16(window*256+j*8+1)) } if b&0x20 == 0x20 { nsec = append(nsec, uint16(window*256+j*8+2)) } if b&0x10 == 0x10 { nsec = append(nsec, uint16(window*256+j*8+3)) } if b&0x8 == 0x8 { nsec = append(nsec, uint16(window*256+j*8+4)) } if b&0x4 == 0x4 { nsec = append(nsec, uint16(window*256+j*8+5)) } if b&0x2 == 0x2 { nsec = append(nsec, uint16(window*256+j*8+6)) } if b&0x1 == 0x1 { nsec = append(nsec, uint16(window*256+j*8+7)) } } off += length lastwindow = window } return nsec, off, nil } // typeBitMapLen is a helper function which computes the "maximum" length of // a the NSEC Type BitMap field. func typeBitMapLen(bitmap []uint16) int { var l int var lastwindow, lastlength uint16 for _, t := range bitmap { window := t / 256 length := (t-window*256)/8 + 1 if window > lastwindow && lastlength != 0 { // New window, jump to the new offset l += int(lastlength) + 2 lastlength = 0 } if window < lastwindow || length < lastlength { // packDataNsec would return Error{err: "nsec bits out of order"} here, but // when computing the length, we want do be liberal. continue } lastwindow, lastlength = window, length } l += int(lastlength) + 2 return l } func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) { if len(bitmap) == 0 { return off, nil } if off > len(msg) { return off, &Error{err: "overflow packing nsec"} } toZero := msg[off:] if maxLen := typeBitMapLen(bitmap); maxLen < len(toZero) { toZero = toZero[:maxLen] } for i := range toZero { toZero[i] = 0 } var lastwindow, lastlength uint16 for _, t := range bitmap { window := t / 256 length := (t-window*256)/8 + 1 if window > lastwindow && lastlength != 0 { // New window, jump to the new offset off += int(lastlength) + 2 lastlength = 0 } if window < lastwindow || length < lastlength { return len(msg), &Error{err: "nsec bits out of order"} } if off+2+int(length) > len(msg) { return len(msg), &Error{err: "overflow packing nsec"} } // Setting the window # msg[off] = byte(window) // Setting the octets length msg[off+1] = byte(length) // Setting the bit value for the type in the right octet msg[off+1+int(length)] |= byte(1 << (7 - t%8)) lastwindow, lastlength = window, length } off += int(lastlength) + 2 return off, nil } func unpackDataSVCB(msg []byte, off int) ([]SVCBKeyValue, int, error) { var xs []SVCBKeyValue var code uint16 var length uint16 var err error for off < len(msg) { code, off, err = unpackUint16(msg, off) if err != nil { return nil, len(msg), &Error{err: "overflow unpacking SVCB"} } length, off, err = unpackUint16(msg, off) if err != nil || off+int(length) > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking SVCB"} } e := makeSVCBKeyValue(SVCBKey(code)) if e == nil { return nil, len(msg), &Error{err: "bad SVCB key"} } if err := e.unpack(msg[off : off+int(length)]); err != nil { return nil, len(msg), err } if len(xs) > 0 && e.Key() <= xs[len(xs)-1].Key() { return nil, len(msg), &Error{err: "SVCB keys not in strictly increasing order"} } xs = append(xs, e) off += int(length) } return xs, off, nil } func packDataSVCB(pairs []SVCBKeyValue, msg []byte, off int) (int, error) { pairs = cloneSlice(pairs) sort.Slice(pairs, func(i, j int) bool { return pairs[i].Key() < pairs[j].Key() }) prev := svcb_RESERVED for _, el := range pairs { if el.Key() == prev { return len(msg), &Error{err: "repeated SVCB keys are not allowed"} } prev = el.Key() packed, err := el.pack() if err != nil { return len(msg), err } off, err = packUint16(uint16(el.Key()), msg, off) if err != nil { return len(msg), &Error{err: "overflow packing SVCB"} } off, err = packUint16(uint16(len(packed)), msg, off) if err != nil || off+len(packed) > len(msg) { return len(msg), &Error{err: "overflow packing SVCB"} } copy(msg[off:off+len(packed)], packed) off += len(packed) } return off, nil } func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) { var ( servers []string s string err error ) if end > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking domain names"} } for off < end { s, off, err = UnpackDomainName(msg, off) if err != nil { return servers, len(msg), err } servers = append(servers, s) } return servers, off, nil } func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) { var err error for _, name := range names { off, err = packDomainName(name, msg, off, compression, compress) if err != nil { return len(msg), err } } return off, nil } func packDataApl(data []APLPrefix, msg []byte, off int) (int, error) { var err error for i := range data { off, err = packDataAplPrefix(&data[i], msg, off) if err != nil { return len(msg), err } } return off, nil } func packDataAplPrefix(p *APLPrefix, msg []byte, off int) (int, error) { if len(p.Network.IP) != len(p.Network.Mask) { return len(msg), &Error{err: "address and mask lengths don't match"} } var err error prefix, _ := p.Network.Mask.Size() addr := p.Network.IP.Mask(p.Network.Mask)[:(prefix+7)/8] switch len(p.Network.IP) { case net.IPv4len: off, err = packUint16(1, msg, off) case net.IPv6len: off, err = packUint16(2, msg, off) default: err = &Error{err: "unrecognized address family"} } if err != nil { return len(msg), err } off, err = packUint8(uint8(prefix), msg, off) if err != nil { return len(msg), err } var n uint8 if p.Negation { n = 0x80 } // trim trailing zero bytes as specified in RFC3123 Sections 4.1 and 4.2. i := len(addr) - 1 for ; i >= 0 && addr[i] == 0; i-- { } addr = addr[:i+1] adflen := uint8(len(addr)) & 0x7f off, err = packUint8(n|adflen, msg, off) if err != nil { return len(msg), err } if off+len(addr) > len(msg) { return len(msg), &Error{err: "overflow packing APL prefix"} } off += copy(msg[off:], addr) return off, nil } func unpackDataApl(msg []byte, off int) ([]APLPrefix, int, error) { var result []APLPrefix for off < len(msg) { prefix, end, err := unpackDataAplPrefix(msg, off) if err != nil { return nil, len(msg), err } off = end result = append(result, prefix) } return result, off, nil } func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) { family, off, err := unpackUint16(msg, off) if err != nil { return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"} } prefix, off, err := unpackUint8(msg, off) if err != nil { return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"} } nlen, off, err := unpackUint8(msg, off) if err != nil { return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"} } var ip []byte switch family { case 1: ip = make([]byte, net.IPv4len) case 2: ip = make([]byte, net.IPv6len) default: return APLPrefix{}, len(msg), &Error{err: "unrecognized APL address family"} } if int(prefix) > 8*len(ip) { return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"} } afdlen := int(nlen & 0x7f) if afdlen > len(ip) { return APLPrefix{}, len(msg), &Error{err: "APL length too long"} } if off+afdlen > len(msg) { return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"} } // Address MUST NOT contain trailing zero bytes per RFC3123 Sections 4.1 and 4.2. off += copy(ip, msg[off:off+afdlen]) if afdlen > 0 { last := ip[afdlen-1] if last == 0 { return APLPrefix{}, len(msg), &Error{err: "extra APL address bits"} } } ipnet := net.IPNet{ IP: ip, Mask: net.CIDRMask(int(prefix), 8*len(ip)), } return APLPrefix{ Negation: (nlen & 0x80) != 0, Network: ipnet, }, off, nil } func unpackIPSECGateway(msg []byte, off int, gatewayType uint8) (net.IP, string, int, error) { var retAddr net.IP var retString string var err error switch gatewayType { case IPSECGatewayNone: // do nothing case IPSECGatewayIPv4: retAddr, off, err = unpackDataA(msg, off) case IPSECGatewayIPv6: retAddr, off, err = unpackDataAAAA(msg, off) case IPSECGatewayHost: retString, off, err = UnpackDomainName(msg, off) } return retAddr, retString, off, err } func packIPSECGateway(gatewayAddr net.IP, gatewayString string, msg []byte, off int, gatewayType uint8, compression compressionMap, compress bool) (int, error) { var err error switch gatewayType { case IPSECGatewayNone: // do nothing case IPSECGatewayIPv4: off, err = packDataA(gatewayAddr, msg, off) case IPSECGatewayIPv6: off, err = packDataAAAA(gatewayAddr, msg, off) case IPSECGatewayHost: off, err = packDomainName(gatewayString, msg, off, compression, compress) } return off, err } golang-github-miekg-dns-1.1.64/msg_helpers_test.go000066400000000000000000000337101476742671700221350ustar00rootroot00000000000000package dns import ( "bytes" "net" "testing" ) // TestPacketDataNsec tests generated using fuzz.go and with a message pack // containing the following bytes: 0000\x00\x00000000\x00\x002000000\x0060000\x00\x130000000000000000000" // That bytes sequence created the overflow error and further permutations of that sequence were able to trigger // the other code paths. func TestPackDataNsec(t *testing.T) { type args struct { bitmap []uint16 msg []byte off int } tests := []struct { name string args args wantOff int wantBytes []byte wantErr bool wantErrMsg string }{ { name: "overflow", args: args{ bitmap: []uint16{ 8962, 8963, 8970, 8971, 8978, 8979, 8986, 8987, 8994, 8995, 9002, 9003, 9010, 9011, 9018, 9019, 9026, 9027, 9034, 9035, 9042, 9043, 9050, 9051, 9058, 9059, 9066, }, msg: []byte{ 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, }, off: 48, }, wantErr: true, wantErrMsg: "dns: overflow packing nsec", wantOff: 48, }, { name: "disordered nsec bits", args: args{ bitmap: []uint16{ 8962, 1, }, msg: []byte{ 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, }, off: 0, }, wantErr: true, wantErrMsg: "dns: nsec bits out of order", wantOff: 155, }, { name: "simple message with only one window", args: args{ bitmap: []uint16{ 1, }, msg: []byte{ 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, }, off: 0, }, wantErr: false, wantOff: 3, wantBytes: []byte{0, 1, 64}, }, { name: "multiple types", args: args{ bitmap: []uint16{ TypeNS, TypeSOA, TypeRRSIG, TypeDNSKEY, TypeNSEC3PARAM, }, msg: []byte{ 48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48, }, off: 0, }, wantErr: false, wantOff: 9, wantBytes: []byte{0, 7, 34, 0, 0, 0, 0, 2, 144}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotOff, err := packDataNsec(tt.args.bitmap, tt.args.msg, tt.args.off) if (err != nil) != tt.wantErr { t.Errorf("packDataNsec() error = %v, wantErr %v", err, tt.wantErr) return } if err != nil && tt.wantErrMsg != err.Error() { t.Errorf("packDataNsec() error msg = %v, wantErrMsg %v", err.Error(), tt.wantErrMsg) return } if gotOff != tt.wantOff { t.Errorf("packDataNsec() = %v, want off %v", gotOff, tt.wantOff) } if err == nil && tt.args.off < len(tt.args.msg) && gotOff < len(tt.args.msg) { if want, got := tt.wantBytes, tt.args.msg[tt.args.off:gotOff]; !bytes.Equal(got, want) { t.Errorf("packDataNsec() = %v, want bytes %v", got, want) } } }) } } func TestPackDataNsecDirtyBuffer(t *testing.T) { zeroBuf := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0} dirtyBuf := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} off1, _ := packDataNsec([]uint16{TypeNS, TypeSOA, TypeRRSIG}, zeroBuf, 0) off2, _ := packDataNsec([]uint16{TypeNS, TypeSOA, TypeRRSIG}, dirtyBuf, 0) if off1 != off2 { t.Errorf("off1 %v != off2 %v", off1, off2) } if !bytes.Equal(zeroBuf[:off1], dirtyBuf[:off2]) { t.Errorf("dirty buffer differs from zero buffer: %v, %v", zeroBuf[:off1], dirtyBuf[:off2]) } } func BenchmarkPackDataNsec(b *testing.B) { benches := []struct { name string types []uint16 }{ {"empty", nil}, {"typical", []uint16{TypeNS, TypeSOA, TypeRRSIG, TypeDNSKEY, TypeNSEC3PARAM}}, {"multiple_windows", []uint16{1, 300, 350, 10000, 20000}}, } for _, bb := range benches { b.Run(bb.name, func(b *testing.B) { buf := make([]byte, 100) for n := 0; n < b.N; n++ { packDataNsec(bb.types, buf, 0) } }) } } func TestUnpackString(t *testing.T) { msg := []byte("\x00abcdef\x0f\\\"ghi\x04mmm\x7f") msg[0] = byte(len(msg) - 1) got, _, err := unpackString(msg, 0) if err != nil { t.Fatal(err) } if want := `abcdef\015\\\"ghi\004mmm\127`; want != got { t.Errorf("expected %q, got %q", want, got) } } func BenchmarkUnpackString(b *testing.B) { b.Run("Escaped", func(b *testing.B) { msg := []byte("\x00abcdef\x0f\\\"ghi\x04mmm") msg[0] = byte(len(msg) - 1) for n := 0; n < b.N; n++ { got, _, err := unpackString(msg, 0) if err != nil { b.Fatal(err) } if want := `abcdef\015\\\"ghi\004mmm`; want != got { b.Errorf("expected %q, got %q", want, got) } } }) b.Run("Unescaped", func(b *testing.B) { msg := []byte("\x00large.example.com") msg[0] = byte(len(msg) - 1) for n := 0; n < b.N; n++ { got, _, err := unpackString(msg, 0) if err != nil { b.Fatal(err) } if want := "large.example.com"; want != got { b.Errorf("expected %q, got %q", want, got) } } }) } func TestPackDataAplPrefix(t *testing.T) { tests := []struct { name string negation bool ip net.IP mask net.IPMask expect []byte }{ { "1:192.0.2.0/24", false, net.ParseIP("192.0.2.0").To4(), net.CIDRMask(24, 32), []byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2}, }, { "2:2001:db8:cafe::0/48", false, net.ParseIP("2001:db8:cafe::"), net.CIDRMask(48, 128), []byte{0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe}, }, { "with trailing zero bytes 2:2001:db8:cafe::0/64", false, net.ParseIP("2001:db8:cafe::"), net.CIDRMask(64, 128), []byte{0x00, 0x02, 0x40, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe}, }, { "no non-zero bytes 2::/16", false, net.ParseIP("::"), net.CIDRMask(16, 128), []byte{0x00, 0x02, 0x10, 0x00}, }, { "!2:2001:db8::/32", true, net.ParseIP("2001:db8::"), net.CIDRMask(32, 128), []byte{0x00, 0x02, 0x20, 0x84, 0x20, 0x01, 0x0d, 0xb8}, }, { "normalize 1:198.51.103.255/22", false, net.ParseIP("198.51.103.255").To4(), net.CIDRMask(22, 32), []byte{0x00, 0x01, 0x16, 0x03, 198, 51, 100}, // 1:198.51.100.0/22 }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ap := &APLPrefix{ Negation: tt.negation, Network: net.IPNet{IP: tt.ip, Mask: tt.mask}, } out := make([]byte, 16) off, err := packDataAplPrefix(ap, out, 0) if err != nil { t.Fatalf("expected no error, got %q", err) } if !bytes.Equal(tt.expect, out[:off]) { t.Fatalf("expected output %02x, got %02x", tt.expect, out[:off]) } // Make sure the packed bytes would be accepted by its own unpack _, _, err = unpackDataAplPrefix(out, 0) if err != nil { t.Fatalf("expected no error, got %q", err) } }) } } func TestPackDataAplPrefix_Failures(t *testing.T) { tests := []struct { name string ip net.IP mask net.IPMask }{ { "family mismatch", net.ParseIP("2001:db8::"), net.CIDRMask(24, 32), }, { "unrecognized family", []byte{0x42}, []byte{0xff}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ap := &APLPrefix{Network: net.IPNet{IP: tt.ip, Mask: tt.mask}} msg := make([]byte, 16) off, err := packDataAplPrefix(ap, msg, 0) if err == nil { t.Fatal("expected error, got none") } if off != len(msg) { t.Fatalf("expected %d, got %d", len(msg), off) } }) } } func TestPackDataAplPrefix_BufferBounds(t *testing.T) { ap := &APLPrefix{ Negation: false, Network: net.IPNet{ IP: net.ParseIP("2001:db8::"), Mask: net.CIDRMask(32, 128), }, } wire := []byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8} t.Run("small", func(t *testing.T) { msg := make([]byte, len(wire)) _, err := packDataAplPrefix(ap, msg, 1) // offset if err == nil { t.Fatal("expected error, got none") } }) t.Run("exact fit", func(t *testing.T) { msg := make([]byte, len(wire)) off, err := packDataAplPrefix(ap, msg, 0) if err != nil { t.Fatalf("expected no error, got %q", err) } if !bytes.Equal(wire, msg[:off]) { t.Fatalf("expected %02x, got %02x", wire, msg[:off]) } }) } func TestPackDataApl(t *testing.T) { in := []APLPrefix{ { Negation: true, Network: net.IPNet{ IP: net.ParseIP("198.51.0.0").To4(), Mask: net.CIDRMask(16, 32), }, }, { Negation: false, Network: net.IPNet{ IP: net.ParseIP("2001:db8:beef::"), Mask: net.CIDRMask(48, 128), }, }, } expect := []byte{ // 1:192.51.0.0/16 0x00, 0x01, 0x10, 0x82, 0xc6, 0x33, // 2:2001:db8:beef::0/48 0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xbe, 0xef, } msg := make([]byte, 32) off, err := packDataApl(in, msg, 0) if err != nil { t.Fatalf("expected no error, got %q", err) } if !bytes.Equal(expect, msg[:off]) { t.Fatalf("expected %02x, got %02x", expect, msg[:off]) } } func TestUnpackDataAplPrefix(t *testing.T) { tests := []struct { name string wire []byte negation bool ip net.IP mask net.IPMask }{ { "1:192.0.2.0/24", []byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2}, false, net.ParseIP("192.0.2.0").To4(), net.CIDRMask(24, 32), }, { "2:2001:db8::/32", []byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8}, false, net.ParseIP("2001:db8::"), net.CIDRMask(32, 128), }, { "!2:2001:db8:8000::/33", []byte{0x00, 0x02, 0x21, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80}, true, net.ParseIP("2001:db8:8000::"), net.CIDRMask(33, 128), }, { "1:0.0.0.0/0", []byte{0x00, 0x01, 0x00, 0x00}, false, net.ParseIP("0.0.0.0").To4(), net.CIDRMask(0, 32), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, off, err := unpackDataAplPrefix(tt.wire, 0) if err != nil { t.Fatalf("expected no error, got %q", err) } if off != len(tt.wire) { t.Fatalf("expected offset %d, got %d", len(tt.wire), off) } if got.Negation != tt.negation { t.Errorf("expected negation %v, got %v", tt.negation, got.Negation) } if !tt.ip.Equal(got.Network.IP) { t.Errorf("expected IP %02x, got %02x", tt.ip, got.Network.IP) } if !bytes.Equal(got.Network.Mask, tt.mask) { t.Errorf("expected mask %02x, got %02x", tt.mask, got.Network.Mask) } }) } } func TestUnpackDataAplPrefix_Errors(t *testing.T) { tests := []struct { name string wire []byte want string }{ { "incomplete header", []byte{0x00, 0x01, 0x18}, "dns: overflow unpacking APL prefix", }, { "unrecognized family", []byte{0x00, 0x03, 0x00, 0x00}, "dns: unrecognized APL address family", }, { "prefix too large for family IPv4", []byte{0x00, 0x01, 0x21, 0x04, 192, 0, 2, 0}, "dns: APL prefix too long", }, { "prefix too large for family IPv6", []byte{0x00, 0x02, 0x81, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80}, "dns: APL prefix too long", }, { "afdlen too long for address family IPv4", []byte{0x00, 0x01, 22, 0x05, 192, 0, 2, 0, 0}, "dns: APL length too long", }, { "overflow unpacking APL address", []byte{0x00, 0x01, 0x10, 0x02, 192}, "dns: overflow unpacking APL address", }, { "address included trailing zeros", []byte{0x00, 0x01, 0x10, 0x04, 192, 0, 2, 0}, "dns: extra APL address bits", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, _, err := unpackDataAplPrefix(tt.wire, 0) if err == nil { t.Fatal("expected error, got none") } if err.Error() != tt.want { t.Errorf("expected %s, got %s", tt.want, err.Error()) } }) } } func TestUnpackDataApl(t *testing.T) { wire := []byte{ // 2:2001:db8:cafe:4200:0/56 0x00, 0x02, 0x38, 0x07, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0x42, // 1:192.0.2.0/24 0x00, 0x01, 0x18, 0x03, 192, 0, 2, // !1:192.0.2.128/25 0x00, 0x01, 0x19, 0x84, 192, 0, 2, 128, // 1:10.0.0.0/24 0x00, 0x01, 0x18, 0x01, 0x0a, // !1:10.0.0.1/32 0x00, 0x01, 0x20, 0x84, 0x0a, 0, 0, 1, // !1:0.0.0.0/0 0x00, 0x01, 0x00, 0x80, // 2::0/0 0x00, 0x02, 0x00, 0x00, } expect := []APLPrefix{ { Negation: false, Network: net.IPNet{ IP: net.ParseIP("2001:db8:cafe:4200::"), Mask: net.CIDRMask(56, 128), }, }, { Negation: false, Network: net.IPNet{ IP: net.ParseIP("192.0.2.0").To4(), Mask: net.CIDRMask(24, 32), }, }, { Negation: true, Network: net.IPNet{ IP: net.ParseIP("192.0.2.128").To4(), Mask: net.CIDRMask(25, 32), }, }, { Negation: false, Network: net.IPNet{ IP: net.ParseIP("10.0.0.0").To4(), Mask: net.CIDRMask(24, 32), }, }, { Negation: true, Network: net.IPNet{ IP: net.ParseIP("10.0.0.1").To4(), Mask: net.CIDRMask(32, 32), }, }, { Negation: true, Network: net.IPNet{ IP: net.ParseIP("0.0.0.0").To4(), Mask: net.CIDRMask(0, 32), }, }, { Negation: false, Network: net.IPNet{ IP: net.ParseIP("::").To16(), Mask: net.CIDRMask(0, 128), }, }, } got, off, err := unpackDataApl(wire, 0) if err != nil { t.Fatalf("expected no error, got %q", err) } if off != len(wire) { t.Fatalf("expected offset %d, got %d", len(wire), off) } if len(got) != len(expect) { t.Fatalf("expected %d prefixes, got %d", len(expect), len(got)) } for i, exp := range expect { if got[i].Negation != exp.Negation { t.Errorf("[%d] expected negation %v, got %v", i, exp.Negation, got[i].Negation) } if !exp.Network.IP.Equal(got[i].Network.IP) { t.Errorf("[%d] expected IP %02x, got %02x", i, exp.Network.IP, got[i].Network.IP) } if !bytes.Equal(got[i].Network.Mask, exp.Network.Mask) { t.Errorf("[%d] expected mask %02x, got %02x", i, exp.Network.Mask, got[i].Network.Mask) } } } golang-github-miekg-dns-1.1.64/msg_test.go000066400000000000000000000221721476742671700204130ustar00rootroot00000000000000package dns import ( "fmt" "regexp" "strconv" "strings" "testing" ) const maxPrintableLabel = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789x" var ( longDomain = maxPrintableLabel[:53] + strings.TrimSuffix( strings.Join([]string{".", ".", ".", ".", "."}, maxPrintableLabel[:49]), ".") reChar = regexp.MustCompile(`.`) i = -1 maxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string { if i++; i >= 32 { i = 0 } return fmt.Sprintf("\\%03d", i) }) // These are the longest possible domain names in presentation format. longestDomain = maxPrintableLabel[:61] + strings.Join([]string{".", ".", ".", "."}, maxPrintableLabel) longestUnprintableDomain = maxUnprintableLabel[:61*4] + strings.Join([]string{".", ".", ".", "."}, maxUnprintableLabel) ) func TestPackNoSideEffect(t *testing.T) { m := new(Msg) m.SetQuestion(Fqdn("example.com."), TypeNS) a := new(Msg) o := &OPT{ Hdr: RR_Header{ Name: ".", Rrtype: TypeOPT, }, } o.SetUDPSize(DefaultMsgSize) a.Extra = append(a.Extra, o) a.SetRcode(m, RcodeBadVers) a.Pack() if a.Rcode != RcodeBadVers { t.Errorf("after pack: Rcode is expected to be BADVERS") } } func TestPackExtendedBadCookie(t *testing.T) { m := new(Msg) m.SetQuestion(Fqdn("example.com."), TypeNS) a := new(Msg) a.SetReply(m) o := &OPT{ Hdr: RR_Header{ Name: ".", Rrtype: TypeOPT, }, } o.SetUDPSize(DefaultMsgSize) a.Extra = append(a.Extra, o) a.SetRcode(m, RcodeBadCookie) edns0 := a.IsEdns0() if edns0 == nil { t.Fatal("Expected OPT RR") } // SetExtendedRcode is only called as part of `Pack()`, hence at this stage, // the OPT RR is not set yet. if edns0.ExtendedRcode() == RcodeBadCookie&0xFFFFFFF0 { t.Errorf("ExtendedRcode is expected to not be BADCOOKIE before Pack") } a.Pack() edns0 = a.IsEdns0() if edns0 == nil { t.Fatal("Expected OPT RR") } if edns0.ExtendedRcode() != RcodeBadCookie&0xFFFFFFF0 { t.Errorf("ExtendedRcode is expected to be BADCOOKIE after Pack") } } func TestUnPackExtendedRcode(t *testing.T) { m := new(Msg) m.SetQuestion(Fqdn("example.com."), TypeNS) a := new(Msg) a.SetReply(m) o := &OPT{ Hdr: RR_Header{ Name: ".", Rrtype: TypeOPT, }, } o.SetUDPSize(DefaultMsgSize) a.Extra = append(a.Extra, o) a.SetRcode(m, RcodeBadCookie) packed, err := a.Pack() if err != nil { t.Fatalf("Could not unpack %v", a) } unpacked := new(Msg) if err := unpacked.Unpack(packed); err != nil { t.Fatalf("Failed to unpack message") } if unpacked.Rcode != RcodeBadCookie { t.Fatalf("Rcode should be matching RcodeBadCookie (%d), got (%d)", RcodeBadCookie, unpacked.Rcode) } } func TestUnpackDomainName(t *testing.T) { var cases = []struct { label string input string expectedOutput string expectedError string }{ {"empty domain", "\x00", ".", ""}, {"long label", "?" + maxPrintableLabel + "\x00", maxPrintableLabel + ".", ""}, {"unprintable label", "?" + regexp.MustCompile(`\\[0-9]+`).ReplaceAllStringFunc(maxUnprintableLabel, func(escape string) string { n, _ := strconv.ParseInt(escape[1:], 10, 8) return string(rune(n)) }) + "\x00", maxUnprintableLabel + ".", ""}, {"long domain", "5" + strings.Replace(longDomain, ".", "1", -1) + "\x00", longDomain + ".", ""}, {"compression pointer", // an unrealistic but functional test referencing an offset _inside_ a label "\x03foo" + "\x05\x03com\x00" + "\x07example" + "\xC0\x05", "foo.\\003com\\000.example.com.", ""}, {"too long domain", "6" + "x" + strings.Replace(longDomain, ".", "1", -1) + "\x00", "", ErrLongDomain.Error()}, {"too long by pointer", // a matryoshka doll name to get over 255 octets after expansion via internal pointers string([]byte{ // 11 length values, first to last 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 0, // 12 filler values 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, // 10 pointers, last to first 192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1, }), "", ErrLongDomain.Error()}, {"long by pointer", // a matryoshka doll name _not_ exceeding 255 octets after expansion string([]byte{ // 11 length values, first to last 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 0, // 9 filler values 120, 120, 120, 120, 120, 120, 120, 120, 120, // 10 pointers, last to first 192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1, }), "" + (`\"\031\028\025\022\019\016\013\010\000xxxxxxxxx` + `\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003\192\002.`) + (`\031\028\025\022\019\016\013\010\000xxxxxxxxx` + `\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003.`) + (`\028\025\022\019\016\013\010\000xxxxxxxxx` + `\192\010\192\009\192\008\192\007\192\006\192\005\192\004.`) + (`\025\022\019\016\013\010\000xxxxxxxxx` + `\192\010\192\009\192\008\192\007\192\006\192\005.`) + `\022\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007\192\006.` + `\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007.` + `\016\013\010\000xxxxxxxxx\192\010\192\009\192\008.` + `\013\010\000xxxxxxxxx\192\010\192\009.` + `\010\000xxxxxxxxx\192\010.` + `\000xxxxxxxxx.`, ""}, {"truncated name", "\x07example\x03", "", "dns: buffer size too small"}, {"non-absolute name", "\x07example\x03com", "", "dns: buffer size too small"}, {"compression pointer cycle (too many)", "\xC0\x00", "", "dns: too many compression pointers"}, {"compression pointer cycle (too long)", "\x03foo" + "\x03bar" + "\x07example" + "\xC0\x04", "", ErrLongDomain.Error()}, {"forward compression pointer", "\x02\xC0\xFF\xC0\x01", "", ErrBuf.Error()}, {"reserved compression pointer 0b10", "\x07example\x80", "", "dns: bad rdata"}, {"reserved compression pointer 0b01", "\x07example\x40", "", "dns: bad rdata"}, } for _, test := range cases { output, idx, err := UnpackDomainName([]byte(test.input), 0) if test.expectedOutput != "" && output != test.expectedOutput { t.Errorf("%s: expected %s, got %s", test.label, test.expectedOutput, output) } if test.expectedError == "" && err != nil { t.Errorf("%s: expected no error, got %d %v", test.label, idx, err) } else if test.expectedError != "" && (err == nil || err.Error() != test.expectedError) { t.Errorf("%s: expected error %s, got %d %v", test.label, test.expectedError, idx, err) } } } func TestPackDomainNameCompressionMap(t *testing.T) { expected := map[string]struct{}{ `www\.this.is.\131an.example.org.`: {}, `is.\131an.example.org.`: {}, `\131an.example.org.`: {}, `example.org.`: {}, `org.`: {}, } msg := make([]byte, 256) for _, compress := range []bool{true, false} { compression := make(map[string]int) _, err := PackDomainName(`www\.this.is.\131an.example.org.`, msg, 0, compression, compress) if err != nil { t.Fatalf("PackDomainName failed: %v", err) } if !compressionMapsEqual(expected, compression) { t.Errorf("expected compression maps to be equal\n%s", compressionMapsDifference(expected, compression)) } } } func TestPackDomainNameNSECTypeBitmap(t *testing.T) { ownername := "some-very-long-ownername.com." msg := &Msg{ Compress: true, Answer: []RR{ &NS{ Hdr: RR_Header{ Name: ownername, Rrtype: TypeNS, Class: ClassINET, }, Ns: "ns1.server.com.", }, &NSEC{ Hdr: RR_Header{ Name: ownername, Rrtype: TypeNSEC, Class: ClassINET, }, NextDomain: "a.com.", TypeBitMap: []uint16{TypeNS, TypeNSEC}, }, }, } // Pack msg and then unpack into msg2 buf, err := msg.Pack() if err != nil { t.Fatalf("msg.Pack failed: %v", err) } var msg2 Msg if err := msg2.Unpack(buf); err != nil { t.Fatalf("msg2.Unpack failed: %v", err) } if !IsDuplicate(msg.Answer[1], msg2.Answer[1]) { t.Error("message differs after packing and unpacking") // Print NSEC RR for both cases t.Logf("expected: %v", msg.Answer[1]) t.Logf("got: %v", msg2.Answer[1]) } } func TestPackUnpackManyCompressionPointers(t *testing.T) { m := new(Msg) m.Compress = true m.SetQuestion("example.org.", TypeNS) for domain := "a."; len(domain) < maxDomainNameWireOctets; domain += "a." { m.Answer = append(m.Answer, &NS{Hdr: RR_Header{Name: domain, Rrtype: TypeNS, Class: ClassINET}, Ns: "example.org."}) b, err := m.Pack() if err != nil { t.Fatalf("Pack failed for %q and %d records with: %v", domain, len(m.Answer), err) } var m2 Msg if err := m2.Unpack(b); err != nil { t.Fatalf("Unpack failed for %q and %d records with: %v", domain, len(m.Answer), err) } } } func TestLenDynamicA(t *testing.T) { for _, rr := range []RR{ testRR("example.org. A"), testRR("example.org. AAAA"), testRR("example.org. L32"), } { msg := make([]byte, Len(rr)) off, err := PackRR(rr, msg, 0, nil, false) if err != nil { t.Fatalf("PackRR failed for %T: %v", rr, err) } if off != len(msg) { t.Errorf("Len(rr) wrong for %T: Len(rr) = %d, PackRR(rr) = %d", rr, len(msg), off) } } } golang-github-miekg-dns-1.1.64/msg_truncate.go000066400000000000000000000066261476742671700212670ustar00rootroot00000000000000package dns // Truncate ensures the reply message will fit into the requested buffer // size by removing records that exceed the requested size. // // It will first check if the reply fits without compression and then with // compression. If it won't fit with compression, Truncate then walks the // record adding as many records as possible without exceeding the // requested buffer size. // // If the message fits within the requested size without compression, // Truncate will set the message's Compress attribute to false. It is // the caller's responsibility to set it back to true if they wish to // compress the payload regardless of size. // // The TC bit will be set if any records were excluded from the message. // If the TC bit is already set on the message it will be retained. // TC indicates that the client should retry over TCP. // // According to RFC 2181, the TC bit should only be set if not all of the // "required" RRs can be included in the response. Unfortunately, we have // no way of knowing which RRs are required so we set the TC bit if any RR // had to be omitted from the response. // // The appropriate buffer size can be retrieved from the requests OPT // record, if present, and is transport specific otherwise. dns.MinMsgSize // should be used for UDP requests without an OPT record, and // dns.MaxMsgSize for TCP requests without an OPT record. func (dns *Msg) Truncate(size int) { if dns.IsTsig() != nil { // To simplify this implementation, we don't perform // truncation on responses with a TSIG record. return } // RFC 6891 mandates that the payload size in an OPT record // less than 512 (MinMsgSize) bytes must be treated as equal to 512 bytes. // // For ease of use, we impose that restriction here. if size < MinMsgSize { size = MinMsgSize } l := msgLenWithCompressionMap(dns, nil) // uncompressed length if l <= size { // Don't waste effort compressing this message. dns.Compress = false return } dns.Compress = true edns0 := dns.popEdns0() if edns0 != nil { // Account for the OPT record that gets added at the end, // by subtracting that length from our budget. // // The EDNS(0) OPT record must have the root domain and // it's length is thus unaffected by compression. size -= Len(edns0) } compression := make(map[string]struct{}) l = headerSize for _, r := range dns.Question { l += r.len(l, compression) } var numAnswer int if l < size { l, numAnswer = truncateLoop(dns.Answer, size, l, compression) } var numNS int if l < size { l, numNS = truncateLoop(dns.Ns, size, l, compression) } var numExtra int if l < size { _, numExtra = truncateLoop(dns.Extra, size, l, compression) } // See the function documentation for when we set this. dns.Truncated = dns.Truncated || len(dns.Answer) > numAnswer || len(dns.Ns) > numNS || len(dns.Extra) > numExtra dns.Answer = dns.Answer[:numAnswer] dns.Ns = dns.Ns[:numNS] dns.Extra = dns.Extra[:numExtra] if edns0 != nil { // Add the OPT record back onto the additional section. dns.Extra = append(dns.Extra, edns0) } } func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) { for i, r := range rrs { if r == nil { continue } l += r.len(l, compression) if l > size { // Return size, rather than l prior to this record, // to prevent any further records being added. return size, i } if l == size { return l, i + 1 } } return l, len(rrs) } golang-github-miekg-dns-1.1.64/msg_truncate_test.go000066400000000000000000000117731476742671700223250ustar00rootroot00000000000000package dns import ( "fmt" "testing" ) func TestRequestTruncateAnswer(t *testing.T) { m := new(Msg) m.SetQuestion("large.example.com.", TypeSRV) reply := new(Msg) reply.SetReply(m) for i := 1; i < 200; i++ { reply.Answer = append(reply.Answer, testRR( fmt.Sprintf("large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.", i))) } reply.Truncate(MinMsgSize) if want, got := MinMsgSize, reply.Len(); want < got { t.Errorf("message length should be below %d bytes, got %d bytes", want, got) } if !reply.Truncated { t.Errorf("truncated bit should be set") } } func TestRequestTruncateExtra(t *testing.T) { m := new(Msg) m.SetQuestion("large.example.com.", TypeSRV) reply := new(Msg) reply.SetReply(m) for i := 1; i < 200; i++ { reply.Extra = append(reply.Extra, testRR( fmt.Sprintf("large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.", i))) } reply.Truncate(MinMsgSize) if want, got := MinMsgSize, reply.Len(); want < got { t.Errorf("message length should be below %d bytes, got %d bytes", want, got) } if !reply.Truncated { t.Errorf("truncated bit should be set") } } func TestRequestTruncateExtraEdns0(t *testing.T) { const size = 4096 m := new(Msg) m.SetQuestion("large.example.com.", TypeSRV) m.SetEdns0(size, true) reply := new(Msg) reply.SetReply(m) for i := 1; i < 200; i++ { reply.Extra = append(reply.Extra, testRR( fmt.Sprintf("large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.", i))) } reply.SetEdns0(size, true) reply.Truncate(size) if want, got := size, reply.Len(); want < got { t.Errorf("message length should be below %d bytes, got %d bytes", want, got) } if !reply.Truncated { t.Errorf("truncated bit should be set") } opt := reply.Extra[len(reply.Extra)-1] if opt.Header().Rrtype != TypeOPT { t.Errorf("expected last RR to be OPT") } } func TestRequestTruncateExtraRegression(t *testing.T) { const size = 2048 m := new(Msg) m.SetQuestion("large.example.com.", TypeSRV) m.SetEdns0(size, true) reply := new(Msg) reply.SetReply(m) for i := 1; i < 33; i++ { reply.Answer = append(reply.Answer, testRR( fmt.Sprintf("large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.", i))) } for i := 1; i < 33; i++ { reply.Extra = append(reply.Extra, testRR( fmt.Sprintf("10-0-0-%d.default.pod.k8s.example.com. 10 IN A 10.0.0.%d", i, i))) } reply.SetEdns0(size, true) reply.Truncate(size) if want, got := size, reply.Len(); want < got { t.Errorf("message length should be below %d bytes, got %d bytes", want, got) } if !reply.Truncated { t.Errorf("truncated bit should be set") } opt := reply.Extra[len(reply.Extra)-1] if opt.Header().Rrtype != TypeOPT { t.Errorf("expected last RR to be OPT") } } func TestTruncation(t *testing.T) { reply := new(Msg) for i := 0; i < 61; i++ { reply.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))) } for i := 0; i < 5; i++ { reply.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))) } for i := 0; i < 5; i++ { reply.Ns = append(reply.Ns, testRR(fmt.Sprintf("srv.subdomain.example.org. 5 IN NS ip-10-10-33-6%d.subdomain.example.org.", i))) } for bufsize := 1024; bufsize <= 4096; bufsize += 12 { m := new(Msg) m.SetQuestion("http.service.tcp.srv.k8s.example.org.", TypeSRV) m.SetEdns0(uint16(bufsize), true) copy := reply.Copy() copy.SetReply(m) copy.Truncate(bufsize) if want, got := bufsize, copy.Len(); want < got { t.Errorf("message length should be below %d bytes, got %d bytes", want, got) } } } func TestRequestTruncateAnswerExact(t *testing.T) { const size = 867 // Bit fiddly, but this hits the rl == size break clause in Truncate, 52 RRs should remain. m := new(Msg) m.SetQuestion("large.example.com.", TypeSRV) m.SetEdns0(size, false) reply := new(Msg) reply.SetReply(m) for i := 1; i < 200; i++ { reply.Answer = append(reply.Answer, testRR(fmt.Sprintf("large.example.com. 10 IN A 127.0.0.%d", i))) } reply.Truncate(size) if want, got := size, reply.Len(); want < got { t.Errorf("message length should be below %d bytes, got %d bytes", want, got) } if expected := 52; len(reply.Answer) != expected { t.Errorf("wrong number of answers; expected %d, got %d", expected, len(reply.Answer)) } } func BenchmarkMsgTruncate(b *testing.B) { const size = 2048 m := new(Msg) m.SetQuestion("example.com.", TypeA) m.SetEdns0(size, true) reply := new(Msg) reply.SetReply(m) for i := 1; i < 33; i++ { reply.Answer = append(reply.Answer, testRR( fmt.Sprintf("large.example.com. 10 IN SRV 0 0 80 10-0-0-%d.default.pod.k8s.example.com.", i))) } for i := 1; i < 33; i++ { reply.Extra = append(reply.Extra, testRR( fmt.Sprintf("10-0-0-%d.default.pod.k8s.example.com. 10 IN A 10.0.0.%d", i, i))) } b.ResetTimer() for i := 0; i < b.N; i++ { b.StopTimer() copy := reply.Copy() b.StartTimer() copy.Truncate(size) } } golang-github-miekg-dns-1.1.64/nsecx.go000066400000000000000000000046121476742671700177050ustar00rootroot00000000000000package dns import ( "crypto/sha1" "encoding/hex" "strings" ) // HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase. func HashName(label string, ha uint8, iter uint16, salt string) string { if ha != SHA1 { return "" } wireSalt := make([]byte, hex.DecodedLen(len(salt))) n, err := packStringHex(salt, wireSalt, 0) if err != nil { return "" } wireSalt = wireSalt[:n] name := make([]byte, 255) off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false) if err != nil { return "" } name = name[:off] s := sha1.New() // k = 0 s.Write(name) s.Write(wireSalt) nsec3 := s.Sum(nil) // k > 0 for k := uint16(0); k < iter; k++ { s.Reset() s.Write(nsec3) s.Write(wireSalt) nsec3 = s.Sum(nsec3[:0]) } return toBase32(nsec3) } // Cover returns true if a name is covered by the NSEC3 record. func (rr *NSEC3) Cover(name string) bool { nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) owner := strings.ToUpper(rr.Hdr.Name) labelIndices := Split(owner) if len(labelIndices) < 2 { return false } ownerHash := owner[:labelIndices[1]-1] ownerZone := owner[labelIndices[1]:] if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone return false } nextHash := rr.NextDomain // if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash if ownerHash == nextHash && nameHash != ownerHash { // empty interval return true } if ownerHash > nextHash { // end of zone if nameHash > ownerHash { // covered since there is nothing after ownerHash return true } return nameHash < nextHash // if nameHash is before beginning of zone it is covered } if nameHash < ownerHash { // nameHash is before ownerHash, not covered return false } return nameHash < nextHash // if nameHash is before nextHash is it covered (between ownerHash and nextHash) } // Match returns true if a name matches the NSEC3 record func (rr *NSEC3) Match(name string) bool { nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) owner := strings.ToUpper(rr.Hdr.Name) labelIndices := Split(owner) if len(labelIndices) < 2 { return false } ownerHash := owner[:labelIndices[1]-1] ownerZone := owner[labelIndices[1]:] if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone return false } if ownerHash == nameHash { return true } return false } golang-github-miekg-dns-1.1.64/nsecx_test.go000066400000000000000000000114261476742671700207450ustar00rootroot00000000000000package dns import ( "strconv" "testing" ) func TestPackNsec3(t *testing.T) { nsec3 := HashName("dnsex.nl.", SHA1, 0, "DEAD") if nsec3 != "ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J" { t.Error(nsec3) } nsec3 = HashName("a.b.c.example.org.", SHA1, 2, "DEAD") if nsec3 != "6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG" { t.Error(nsec3) } } func TestNsec3(t *testing.T) { nsec3 := testRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM") if !nsec3.(*NSEC3).Match("nl.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.") } if !nsec3.(*NSEC3).Match("NL.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.NL. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.") } if nsec3.(*NSEC3).Match("com.") { // t.Fatal("com. is not in the zone nl.") } if nsec3.(*NSEC3).Match("test.nl.") { // name hash = gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q t.Fatal("gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q.nl. should not match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.") } nsec3 = testRR("nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM") if nsec3.(*NSEC3).Match("nl.") { t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should not match a record without a owner hash") } for _, tc := range []struct { rr *NSEC3 name string covers bool }{ // positive tests { // name hash between owner hash and next hash rr: &NSEC3{ Hdr: RR_Header{Name: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP.com."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "PT3RON8N7PM3A0OE989IB84OOSADP7O8", }, name: "bsd.com.", covers: true, }, { // end of zone, name hash is after owner hash rr: &NSEC3{ Hdr: RR_Header{Name: "3v62ulr0nre83v0rja2vjgtlif9v6rab.com."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP", }, name: "csd.com.", covers: true, }, { // end of zone, name hash is before beginning of zone rr: &NSEC3{ Hdr: RR_Header{Name: "PT3RON8N7PM3A0OE989IB84OOSADP7O8.com."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB", }, name: "asd.com.", covers: true, }, // negative tests { // too short owner name rr: &NSEC3{ Hdr: RR_Header{Name: "nl."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "39P99DCGG0MDLARTCRMCF6OFLLUL7PR6", }, name: "asd.com.", covers: false, }, { // outside of zone rr: &NSEC3{ Hdr: RR_Header{Name: "39p91242oslggest5e6a7cci4iaeqvnk.nl."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "39P99DCGG0MDLARTCRMCF6OFLLUL7PR6", }, name: "asd.com.", covers: false, }, { // empty interval rr: &NSEC3{ Hdr: RR_Header{Name: "2n1tb3vairuobl6rkdvii42n9tfmialp.com."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP", }, name: "asd.com.", covers: false, }, { // empty interval wildcard rr: &NSEC3{ Hdr: RR_Header{Name: "2n1tb3vairuobl6rkdvii42n9tfmialp.com."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP", }, name: "*.asd.com.", covers: true, }, { // name hash is before owner hash, not covered rr: &NSEC3{ Hdr: RR_Header{Name: "3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB.com."}, Hash: 1, Flags: 1, Iterations: 5, Salt: "F10E9F7EA83FC8F3", NextDomain: "PT3RON8N7PM3A0OE989IB84OOSADP7O8", }, name: "asd.com.", covers: false, }, } { covers := tc.rr.Cover(tc.name) if tc.covers != covers { t.Fatalf("cover failed for %s: expected %t, got %t [record: %s]", tc.name, tc.covers, covers, tc.rr) } } } func TestNsec3EmptySalt(t *testing.T) { rr, _ := NewRR("CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A NS SOA RRSIG DNSKEY NSEC3PARAM") if !rr.(*NSEC3).Match("com.") { t.Fatalf("expected record to match com. label") } } func BenchmarkHashName(b *testing.B) { for _, iter := range []uint16{ 150, 2500, 5000, 10000, ^uint16(0), } { b.Run(strconv.Itoa(int(iter)), func(b *testing.B) { for n := 0; n < b.N; n++ { if HashName("some.example.org.", SHA1, iter, "deadbeef") == "" { b.Fatalf("HashName failed") } } }) } } golang-github-miekg-dns-1.1.64/parse_test.go000066400000000000000000002410211476742671700207330ustar00rootroot00000000000000package dns import ( "bytes" "crypto/rsa" "encoding/hex" "fmt" "math/rand" "net" "reflect" "regexp" "strconv" "strings" "testing" "testing/quick" ) func TestDotInName(t *testing.T) { buf := make([]byte, 20) PackDomainName("aa\\.bb.nl.", buf, 0, nil, false) // index 3 must be a real dot if buf[3] != '.' { t.Error("dot should be a real dot") } if buf[6] != 2 { t.Error("this must have the value 2") } dom, _, _ := UnpackDomainName(buf, 0) // printing it should yield the backspace again if dom != "aa\\.bb.nl." { t.Error("dot should have been escaped: ", dom) } } func TestDotLastInLabel(t *testing.T) { sample := "aa\\..au." buf := make([]byte, 20) _, err := PackDomainName(sample, buf, 0, nil, false) if err != nil { t.Fatalf("unexpected error packing domain: %v", err) } dom, _, _ := UnpackDomainName(buf, 0) if dom != sample { t.Fatalf("unpacked domain `%s' doesn't match packed domain", dom) } } func TestTooLongDomainName(t *testing.T) { l := "aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt." dom := l + l + l + l + l + l + l _, err := NewRR(dom + " IN A 127.0.0.1") if err == nil { t.Error("should be too long") } _, err = NewRR("..com. IN A 127.0.0.1") if err == nil { t.Error("should fail") } } func TestDomainName(t *testing.T) { tests := []string{"r\\.gieben.miek.nl.", "www\\.www.miek.nl.", "www.*.miek.nl.", "www.*.miek.nl.", } dbuff := make([]byte, 40) for _, ts := range tests { if _, err := PackDomainName(ts, dbuff, 0, nil, false); err != nil { t.Error("not a valid domain name") continue } n, _, err := UnpackDomainName(dbuff, 0) if err != nil { t.Error("failed to unpack packed domain name") continue } if ts != n { t.Errorf("must be equal: in: %s, out: %s", ts, n) } } } func TestDomainNameAndTXTEscapes(t *testing.T) { tests := []byte{'.', '(', ')', ';', ' ', '@', '"', '\\', 9, 13, 10, 0, 255} for _, b := range tests { rrbytes := []byte{ 1, b, 0, // owner byte(TypeTXT >> 8), byte(TypeTXT), byte(ClassINET >> 8), byte(ClassINET), 0, 0, 0, 1, // TTL 0, 2, 1, b, // Data } rr1, _, err := UnpackRR(rrbytes, 0) if err != nil { panic(err) } s := rr1.String() rr2, err := NewRR(s) if err != nil { t.Errorf("error parsing unpacked RR's string: %v", err) } repacked := make([]byte, len(rrbytes)) if _, err := PackRR(rr2, repacked, 0, nil, false); err != nil { t.Errorf("error packing parsed RR: %v", err) } if !bytes.Equal(repacked, rrbytes) { t.Error("packed bytes don't match original bytes") } } } func TestTXTEscapeParsing(t *testing.T) { test := [][]string{ {`";"`, `";"`}, {`\;`, `";"`}, {`"\t"`, `"t"`}, {`"\r"`, `"r"`}, {`"\ "`, `" "`}, {`"\;"`, `";"`}, {`"\;\""`, `";\""`}, {`"\(a\)"`, `"(a)"`}, {`"\(a)"`, `"(a)"`}, {`"(a\)"`, `"(a)"`}, {`"(a)"`, `"(a)"`}, {`"\048"`, `"0"`}, {`"\` + "\t" + `"`, `"\009"`}, {`"\` + "\n" + `"`, `"\010"`}, {`"\` + "\r" + `"`, `"\013"`}, {`"\` + "\x11" + `"`, `"\017"`}, {`"\'"`, `"'"`}, } for _, s := range test { rr, err := NewRR(fmt.Sprintf("example.com. IN TXT %v", s[0])) if err != nil { t.Errorf("could not parse %v TXT: %s", s[0], err) continue } txt := sprintTxt(rr.(*TXT).Txt) if txt != s[1] { t.Errorf("mismatch after parsing `%v` TXT record: `%v` != `%v`", s[0], txt, s[1]) } } } func GenerateDomain(r *rand.Rand, size int) []byte { dnLen := size % 70 // artificially limit size so there's less to interpret if a failure occurs var dn []byte done := false for i := 0; i < dnLen && !done; { max := dnLen - i if max > 63 { max = 63 } lLen := max if lLen != 0 { lLen = int(r.Int31()) % max } done = lLen == 0 if done { continue } l := make([]byte, lLen+1) l[0] = byte(lLen) for j := 0; j < lLen; j++ { l[j+1] = byte(rand.Int31()) } dn = append(dn, l...) i += 1 + lLen } return append(dn, 0) } func TestDomainQuick(t *testing.T) { r := rand.New(rand.NewSource(0)) f := func(l int) bool { db := GenerateDomain(r, l) ds, _, err := UnpackDomainName(db, 0) if err != nil { panic(err) } buf := make([]byte, 255) off, err := PackDomainName(ds, buf, 0, nil, false) if err != nil { t.Errorf("error packing domain: %v", err) t.Errorf(" bytes: %v", db) t.Errorf("string: %v", ds) return false } if !bytes.Equal(db, buf[:off]) { t.Errorf("repacked domain doesn't match original:") t.Errorf("src bytes: %v", db) t.Errorf(" string: %v", ds) t.Errorf("out bytes: %v", buf[:off]) return false } return true } if err := quick.Check(f, nil); err != nil { t.Error(err) } } func GenerateTXT(r *rand.Rand, size int) []byte { rdLen := size % 300 // artificially limit size so there's less to interpret if a failure occurs var rd []byte for i := 0; i < rdLen; { max := rdLen - 1 if max > 255 { max = 255 } sLen := max if max != 0 { sLen = int(r.Int31()) % max } s := make([]byte, sLen+1) s[0] = byte(sLen) for j := 0; j < sLen; j++ { s[j+1] = byte(rand.Int31()) } rd = append(rd, s...) i += 1 + sLen } return rd } func TestParseDirectiveMisc(t *testing.T) { tests := map[string]string{ "$ORIGIN miek.nl.\na IN NS b": "a.miek.nl.\t3600\tIN\tNS\tb.miek.nl.", "$TTL 2H\nmiek.nl. IN NS b.": "miek.nl.\t7200\tIN\tNS\tb.", "miek.nl. 1D IN NS b.": "miek.nl.\t86400\tIN\tNS\tb.", `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) )`: "name.\t3600\tIN\tSOA\ta6.nstld.com. hostmaster.nic.name. 203362132 300 300 1209600 300", ". 3600000 IN NS ONE.MY-ROOTS.NET.": ".\t3600000\tIN\tNS\tONE.MY-ROOTS.NET.", "ONE.MY-ROOTS.NET. 3600000 IN A 192.168.1.1": "ONE.MY-ROOTS.NET.\t3600000\tIN\tA\t192.168.1.1", } for i, o := range tests { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestNSEC(t *testing.T) { nsectests := map[string]string{ "nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F": "nl.\t3600\tIN\tNSEC3PARAM\t1 0 5 30923C44C6CBBB8F", "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", "localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC", "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", "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", "44ohaq2njb0idnvolt9ggthvsk1e1uv8.skydns.test. NSEC3 1 0 0 - 44OHAQ2NJB0IDNVOLT9GGTHVSK1E1UVA": "44ohaq2njb0idnvolt9ggthvsk1e1uv8.skydns.test.\t3600\tIN\tNSEC3\t1 0 0 - 44OHAQ2NJB0IDNVOLT9GGTHVSK1E1UVA", } for i, o := range nsectests { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } rr, err := NewRR("nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F") if err != nil { t.Fatal("failed to parse RR: ", err) } if nsec3param, ok := rr.(*NSEC3PARAM); ok { if nsec3param.SaltLength != 8 { t.Fatalf("nsec3param saltlen %d != 8", nsec3param.SaltLength) } } else { t.Fatal("not nsec3 param: ", err) } } func TestParseLOC(t *testing.T) { lt := map[string]string{ "SW1A2AA.find.me.uk. LOC 51 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", "SW1A2AA.find.me.uk. LOC 51 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", "SW1A2AA.find.me.uk. LOC 51 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", // Exercise boundary cases "SW1A2AA.find.me.uk. LOC 90 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", "SW1A2AA.find.me.uk. LOC 89 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", // use float64 to have enough precision. "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", } for i, o := range lt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } // Invalid cases (out of range values) lt = map[string]string{ // Pair of (invalid) RDATA and the bad field name // One of the subfields is out of range. "91 0 0.0 N 00 07 39.611 W 0m": "Latitude", "89 60 0.0 N 00 07 39.611 W 0m": "Latitude", "89 00 60.0 N 00 07 39.611 W 0m": "Latitude", "1 00 -1 N 00 07 39.611 W 0m": "Latitude", "0 0 0.0 N 181 00 0.0 W 0m": "Longitude", "0 0 0.0 N 179 60 0.0 W 0m": "Longitude", "0 0 0.0 N 179 00 60.0 W 0m": "Longitude", "0 0 0.0 N 1 00 -1 W 0m": "Longitude", // Each subfield is valid, but resulting latitude would be out of range. "90 01 00.0 N 00 07 39.611 W 0m": "Latitude", "0 0 0.0 N 180 01 0.0 W 0m": "Longitude", } for rdata, field := range lt { _, err := NewRR(fmt.Sprintf("example.com. LOC %s", rdata)) if err == nil || !strings.Contains(err.Error(), field) { t.Errorf("expected error to contain %q, but got %v", field, err) } } } // this tests a subroutine for the LOC RR parser. It's complicated enough to test separately. func TestStringToCm(t *testing.T) { tests := []struct { // Test description: the input token and the expected return values from stringToCm. token string e uint8 m uint8 ok bool }{ {"100", 4, 1, true}, {"0100", 4, 1, true}, // leading 0 (allowed) {"100.99", 4, 1, true}, {"90000000", 9, 9, true}, {"90000000.00", 9, 9, true}, {"0", 0, 0, true}, {"0.00", 0, 0, true}, {"0.01", 0, 1, true}, {".01", 0, 1, true}, // empty 'meter' part (allowed) {"0.1", 1, 1, true}, // out of range (too large) {"90000001", 0, 0, false}, {"90000000.01", 0, 0, false}, // more than 2 digits in 'cmeter' part {"0.000", 0, 0, false}, {"0.001", 0, 0, false}, {"0.999", 0, 0, false}, // with plus or minus sign (disallowed) {"-100", 0, 0, false}, {"+100", 0, 0, false}, {"0.-10", 0, 0, false}, {"0.+10", 0, 0, false}, {"0a.00", 0, 0, false}, // invalid string for 'meter' part {".1x", 0, 0, false}, // invalid string for 'cmeter' part {".", 0, 0, false}, // empty 'cmeter' part (disallowed) {"1.", 0, 0, false}, // ditto {"m", 0, 0, false}, // only the "m" suffix } for _, tc := range tests { tc := tc t.Run(tc.token, func(t *testing.T) { // In all cases the expected result is the same with or without the 'm' suffix. // So we test both cases using the same test code. for _, sfx := range []string{"", "m"} { token := tc.token + sfx e, m, ok := stringToCm(token) if ok != tc.ok { t.Fatal("unexpected validation result") } if m != tc.m { t.Fatalf("Expected %d, got %d", tc.m, m) } if e != tc.e { t.Fatalf("Expected %d, got %d", tc.e, e) } } }) } } func TestParseDS(t *testing.T) { dt := map[string]string{ "example.net. 3600 IN DS 40692 12 3 22261A8B0E0D799183E35E24E2AD6BB58533CBA7E3B14D659E9CA09B 2071398F": "example.net.\t3600\tIN\tDS\t40692 12 3 22261A8B0E0D799183E35E24E2AD6BB58533CBA7E3B14D659E9CA09B2071398F", } for i, o := range dt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestQuotes(t *testing.T) { tests := map[string]string{ `t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"", `t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\010 bc\"", `t.example.com. IN TXT ""`: "t.example.com.\t3600\tIN\tTXT\t\"\"", `t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"", `t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"", `t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", `t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"", `t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"", `t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", `t.example.com. IN TXT aaa aaa;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"", `t.example.com. IN TXT aaa aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"", `t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", "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.", "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.", "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.", "cid.urn.arpa. NAPTR 100 10 \"\" \"\" \"/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\" .": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 10 \"\" \"\" \"/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\" .", } for i, o := range tests { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is\n`%s'", i, o, rr.String()) } } } func TestParseClass(t *testing.T) { tests := map[string]string{ "t.example.com. IN A 127.0.0.1": "t.example.com. 3600 IN A 127.0.0.1", "t.example.com. CS A 127.0.0.1": "t.example.com. 3600 CS A 127.0.0.1", "t.example.com. CH A 127.0.0.1": "t.example.com. 3600 CH A 127.0.0.1", // ClassANY can not occur in zone files // "t.example.com. ANY A 127.0.0.1": "t.example.com. 3600 ANY A 127.0.0.1", "t.example.com. NONE A 127.0.0.1": "t.example.com. 3600 NONE A 127.0.0.1", "t.example.com. CLASS255 A 127.0.0.1": "t.example.com. 3600 CLASS255 A 127.0.0.1", } for i, o := range tests { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is\n`%s'", i, o, rr.String()) } } } func TestBrace(t *testing.T) { tests := map[string]string{ "(miek.nl.) 3600 IN A 127.0.1.1": "miek.nl.\t3600\tIN\tA\t127.0.1.1", "miek.nl. (3600) IN MX (10) elektron.atoom.net.": "miek.nl.\t3600\tIN\tMX\t10 elektron.atoom.net.", `miek.nl. IN ( 3600 A 127.0.0.1)`: "miek.nl.\t3600\tIN\tA\t127.0.0.1", "(miek.nl.) (A) (127.0.2.1)": "miek.nl.\t3600\tIN\tA\t127.0.2.1", "miek.nl A 127.0.3.1": "miek.nl.\t3600\tIN\tA\t127.0.3.1", "_ssh._tcp.local. 60 IN (PTR) stora._ssh._tcp.local.": "_ssh._tcp.local.\t60\tIN\tPTR\tstora._ssh._tcp.local.", "miek.nl. NS ns.miek.nl": "miek.nl.\t3600\tIN\tNS\tns.miek.nl.", `(miek.nl.) ( (IN) (AAAA) (::1) )`: "miek.nl.\t3600\tIN\tAAAA\t::1", `(miek.nl.) ( (IN) (AAAA) (::1))`: "miek.nl.\t3600\tIN\tAAAA\t::1", "miek.nl. IN AAAA ::2": "miek.nl.\t3600\tIN\tAAAA\t::2", `((m)(i)ek.(n)l.) (SOA) (soa.) (soa.) ( 2009032802 ; serial 21600 ; refresh (6 hours) 7(2)00 ; retry (2 hours) 604()800 ; expire (1 week) 3600 ; minimum (1 hour) )`: "miek.nl.\t3600\tIN\tSOA\tsoa. soa. 2009032802 21600 7200 604800 3600", "miek\\.nl. IN A 127.0.0.10": "miek\\.nl.\t3600\tIN\tA\t127.0.0.10", "miek.nl. IN A 127.0.0.11": "miek.nl.\t3600\tIN\tA\t127.0.0.11", "miek.nl. A 127.0.0.12": "miek.nl.\t3600\tIN\tA\t127.0.0.12", `miek.nl. 86400 IN SOA elektron.atoom.net. miekg.atoom.net. ( 2009032802 ; serial 21600 ; refresh (6 hours) 7200 ; retry (2 hours) 604800 ; expire (1 week) 3600 ; minimum (1 hour) )`: "miek.nl.\t86400\tIN\tSOA\telektron.atoom.net. miekg.atoom.net. 2009032802 21600 7200 604800 3600", } for i, o := range tests { rr, err := NewRR(i) if err != nil { t.Errorf("failed to parse RR: %v\n\t%s", err, i) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestParseFailure(t *testing.T) { tests := []string{"miek.nl. IN A 327.0.0.1", "miek.nl. IN AAAA ::x", "miek.nl. IN MX a0 miek.nl.", "miek.nl aap IN MX mx.miek.nl.", "miek.nl 200 IN mxx 10 mx.miek.nl.", "miek.nl. inn MX 10 mx.miek.nl.", // "miek.nl. IN CNAME ", // actually valid nowadays, zero size rdata "miek.nl. IN CNAME ..", "miek.nl. PA MX 10 miek.nl.", "miek.nl. ) IN MX 10 miek.nl.", } for _, s := range tests { _, err := NewRR(s) if err == nil { t.Errorf("should have triggered an error: \"%s\"", s) } } } func TestOmittedTTL(t *testing.T) { zone := ` $ORIGIN example.com. example.com. 42 IN SOA ns1.example.com. hostmaster.example.com. 1 86400 60 86400 3600 ; TTL=42 SOA example.com. NS 2 ; TTL=42 absolute owner name @ MD 3 ; TTL=42 current-origin owner name MF 4 ; TTL=42 leading-space implied owner name 43 TYPE65280 \# 1 05 ; TTL=43 implied owner name explicit TTL MB 6 ; TTL=43 leading-tab implied owner name $TTL 1337 example.com. 88 MG 7 ; TTL=88 explicit TTL example.com. MR 8 ; TTL=1337 after first $TTL $TTL 314 1 TXT 9 ; TTL=1 implied owner name explicit TTL example.com. DNAME 10 ; TTL=314 after second $TTL ` reCaseFromComment := regexp.MustCompile(`TTL=(\d+)\s+(.*)`) z := NewZoneParser(strings.NewReader(zone), "", "") var i int for rr, ok := z.Next(); ok; rr, ok = z.Next() { i++ expected := reCaseFromComment.FindStringSubmatch(z.Comment()) if len(expected) != 3 { t.Errorf("regexp didn't match for record %d", i) continue } expectedTTL, _ := strconv.ParseUint(expected[1], 10, 32) ttl := rr.Header().Ttl if ttl != uint32(expectedTTL) { t.Errorf("%s: expected TTL %d, got %d", expected[2], expectedTTL, ttl) } } if err := z.Err(); err != nil { t.Error(err) } if i != 10 { t.Errorf("expected %d records, got %d", 5, i) } } func TestRelativeNameErrors(t *testing.T) { var badZones = []struct { label string zoneContents string expectedErr string }{ { "relative owner name without origin", "example.com 3600 IN SOA ns.example.com. hostmaster.example.com. 1 86400 60 86400 3600", "bad owner name", }, { "relative owner name in RDATA", "example.com. 3600 IN SOA ns hostmaster 1 86400 60 86400 3600", "bad SOA Ns", }, { "origin reference without origin", "@ 3600 IN SOA ns.example.com. hostmaster.example.com. 1 86400 60 86400 3600", "bad owner name", }, { "relative owner name in $INCLUDE", "$INCLUDE file.db example.com", "bad origin name", }, { "relative owner name in $ORIGIN", "$ORIGIN example.com", "bad origin name", }, } for _, errorCase := range badZones { z := NewZoneParser(strings.NewReader(errorCase.zoneContents), "", "") z.Next() if err := z.Err(); err == nil { t.Errorf("%s: expected error, got nil", errorCase.label) } else if !strings.Contains(err.Error(), errorCase.expectedErr) { t.Errorf("%s: expected error `%s`, got `%s`", errorCase.label, errorCase.expectedErr, err) } } } func TestHIP(t *testing.T) { h := `www.example.com. IN HIP ( 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p 9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQ b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. )` rr, err := NewRR(h) if err != nil { t.Fatalf("failed to parse RR: %v", err) } msg := new(Msg) msg.Answer = []RR{rr, rr} bytes, err := msg.Pack() if err != nil { t.Fatalf("failed to pack msg: %v", err) } if err := msg.Unpack(bytes); err != nil { t.Fatalf("failed to unpack msg: %v", err) } if len(msg.Answer) != 2 { t.Fatalf("2 answers expected: %v", msg) } for i, rr := range msg.Answer { rr := rr.(*HIP) if l := len(rr.RendezvousServers); l != 2 { t.Fatalf("2 servers expected, only %d in record %d:\n%v", l, i, msg) } for j, s := range []string{"rvs1.example.com.", "rvs2.example.com."} { if rr.RendezvousServers[j] != s { t.Fatalf("expected server %d of record %d to be %s:\n%v", j, i, s, msg) } } } } // Test with no known RR on the line func TestLineNumberError2(t *testing.T) { tests := map[string]string{ "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", "example.com 1000 IN TALINK a.example.com. b..example.com.": "dns: bad TALINK NextName: \"b..example.com.\" at line: 1:57", "example.com 1000 IN TALINK ( a.example.com. b..example.com. )": "dns: bad TALINK NextName: \"b..example.com.\" at line: 1:60", `example.com 1000 IN TALINK ( a.example.com. bb..example.com. )`: "dns: bad TALINK NextName: \"bb..example.com.\" at line: 2:18", // This is a bug, it should report an error on line 1, but the new is already processed. `example.com 1000 IN TALINK ( a.example.com. b...example.com. )`: "dns: bad TALINK NextName: \"b...example.com.\" at line: 2:1"} for in, errStr := range tests { _, err := NewRR(in) if err == nil { t.Error("err is nil") } else { if err.Error() != errStr { t.Errorf("%s: error should be %s is %v", in, errStr, err) } } } } // Test if the calculations are correct func TestRfc1982(t *testing.T) { // If the current time and the timestamp are more than 68 years apart // it means the date has wrapped. 0 is 1970 // fall in the current 68 year span strtests := []string{"20120525134203", "19700101000000", "20380119031408"} for _, v := range strtests { if x, _ := StringToTime(v); v != TimeToString(x) { t.Errorf("1982 arithmetic string failure %s (%s:%d)", v, TimeToString(x), x) } } inttests := map[uint32]string{0: "19700101000000", 1 << 31: "20380119031408", 1<<32 - 1: "21060207062815", } for i, v := range inttests { if TimeToString(i) != v { t.Errorf("1982 arithmetic int failure %d:%s (%s)", i, v, TimeToString(i)) } } // Future tests, these dates get parsed to a date within the current 136 year span future := map[string]string{"22680119031408": "20631123173144", "19010101121212": "20370206184028", "19210101121212": "20570206184028", "19500101121212": "20860206184028", "19700101000000": "19700101000000", "19690101000000": "21050207062816", "29210101121212": "21040522212236", } for from, to := range future { x, _ := StringToTime(from) y := TimeToString(x) if y != to { t.Errorf("1982 arithmetic future failure %s:%s (%s)", from, to, y) } } } func TestEmpty(t *testing.T) { z := NewZoneParser(strings.NewReader(""), "", "") for _, ok := z.Next(); ok; _, ok = z.Next() { t.Errorf("should be empty") } if err := z.Err(); err != nil { t.Error("got an error when it shouldn't") } } func TestLowercaseTokens(t *testing.T) { var testrecords = []string{ "example.org. 300 IN a 1.2.3.4", "example.org. 300 in A 1.2.3.4", "example.org. 300 in a 1.2.3.4", "example.org. 300 a 1.2.3.4", "example.org. 300 A 1.2.3.4", "example.org. IN a 1.2.3.4", "example.org. in A 1.2.3.4", "example.org. in a 1.2.3.4", "example.org. a 1.2.3.4", "example.org. A 1.2.3.4", "example.org. a 1.2.3.4", "$ORIGIN example.org.\n a 1.2.3.4", "$Origin example.org.\n a 1.2.3.4", "$origin example.org.\n a 1.2.3.4", "example.org. Class1 Type1 1.2.3.4", } for _, testrr := range testrecords { _, err := NewRR(testrr) if err != nil { t.Errorf("failed to parse %#v, got %v", testrr, err) } } } func TestSRVPacking(t *testing.T) { msg := Msg{} things := []string{"1.2.3.4:8484", "45.45.45.45:8484", "84.84.84.84:8484", } for i, n := range things { h, p, err := net.SplitHostPort(n) if err != nil { continue } port, _ := strconv.ParseUint(p, 10, 16) rr := &SRV{ Hdr: RR_Header{Name: "somename.", Rrtype: TypeSRV, Class: ClassINET, Ttl: 5}, Priority: uint16(i), Weight: 5, Port: uint16(port), Target: h + ".", } msg.Answer = append(msg.Answer, rr) } _, err := msg.Pack() if err != nil { t.Fatalf("couldn't pack %v: %v", msg, err) } } func TestParseBackslash(t *testing.T) { if _, err := NewRR("nul\\000gap.test.globnix.net. 600 IN A 192.0.2.10"); err != nil { t.Errorf("could not create RR with \\000 in it") } if _, err := NewRR(`nul\000gap.test.globnix.net. 600 IN TXT "Hello\123"`); err != nil { t.Errorf("could not create RR with \\000 in it") } if _, err := NewRR(`m\ @\ iek.nl. IN 3600 A 127.0.0.1`); err != nil { t.Errorf("could not create RR with \\ and \\@ in it") } } func TestILNP(t *testing.T) { tests := []string{ "host1.example.com.\t3600\tIN\tNID\t10 0014:4fff:ff20:ee64", "host1.example.com.\t3600\tIN\tNID\t20 0015:5fff:ff21:ee65", "host2.example.com.\t3600\tIN\tNID\t10 0016:6fff:ff22:ee66", "host1.example.com.\t3600\tIN\tL32\t10 10.1.2.0", "host1.example.com.\t3600\tIN\tL32\t20 10.1.4.0", "host2.example.com.\t3600\tIN\tL32\t10 10.1.8.0", "host1.example.com.\t3600\tIN\tL64\t10 2001:0DB8:1140:1000", "host1.example.com.\t3600\tIN\tL64\t20 2001:0DB8:2140:2000", "host2.example.com.\t3600\tIN\tL64\t10 2001:0DB8:4140:4000", "host1.example.com.\t3600\tIN\tLP\t10 l64-subnet1.example.com.", "host1.example.com.\t3600\tIN\tLP\t10 l64-subnet2.example.com.", "host1.example.com.\t3600\tIN\tLP\t20 l32-subnet1.example.com.", } for _, t1 := range tests { r, err := NewRR(t1) if err != nil { t.Fatalf("an error occurred: %v", err) } else { if t1 != r.String() { t.Fatalf("strings should be equal %s %s", t1, r.String()) } } } } func TestGposEidNimloc(t *testing.T) { dt := map[string]string{ "444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.", "lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0", "hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0", "VENERA. IN NIMLOC 75234159EAC457800920": "VENERA.\t3600\tIN\tNIMLOC\t75234159EAC457800920", "VAXA. IN EID 3141592653589793": "VAXA.\t3600\tIN\tEID\t3141592653589793", } for i, o := range dt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestPX(t *testing.T) { dt := map[string]string{ "*.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.", "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.", } for i, o := range dt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestComment(t *testing.T) { // Comments we must see comments := map[string]bool{ "; this is comment 1": true, "; this is comment 2": true, "; this is comment 4": true, "; this is comment 6": true, "; this is comment 7": true, "; this is comment 8": true, } zone := ` foo. IN A 10.0.0.1 ; this is comment 1 foo. IN A ( 10.0.0.2 ; this is comment 2 ) ; this is comment 3 foo. IN A 10.0.0.3 foo. IN A ( 10.0.0.4 ); this is comment 4 foo. IN A 10.0.0.5 ; this is comment 5 foo. IN A 10.0.0.6 foo. IN DNSKEY 256 3 5 AwEAAb+8l ; this is comment 6 foo. IN NSEC miek.nl. TXT RRSIG NSEC; this is comment 7 foo. IN TXT "THIS IS TEXT MAN"; this is comment 8 ` z := NewZoneParser(strings.NewReader(zone), ".", "") for _, ok := z.Next(); ok; _, ok = z.Next() { if z.Comment() != "" { if _, okC := comments[z.Comment()]; !okC { t.Errorf("wrong comment %q", z.Comment()) } } } if err := z.Err(); err != nil { t.Error("got an error when it shouldn't") } } func TestZoneParserComments(t *testing.T) { for i, test := range []struct { zone string comments []string }{ { `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) ) ; y . 3600000 IN NS ONE.MY-ROOTS.NET. ; x`, []string{"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes) ; y", "; x"}, }, { `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) ) ; y . 3600000 IN NS ONE.MY-ROOTS.NET.`, []string{"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes) ; y", ""}, }, { `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) ) . 3600000 IN NS ONE.MY-ROOTS.NET.`, []string{"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes)", ""}, }, { `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) ) . 3600000 IN NS ONE.MY-ROOTS.NET. ; x`, []string{"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes)", "; x"}, }, { `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) )`, []string{"; serial ; refresh (5 minutes) ; retry (5 minutes) ; expire (2 weeks) ; minimum (5 minutes)"}, }, { `. 3600000 IN NS ONE.MY-ROOTS.NET. ; x`, []string{"; x"}, }, { `. 3600000 IN NS ONE.MY-ROOTS.NET.`, []string{""}, }, { `. 3600000 IN NS ONE.MY-ROOTS.NET. ;;x`, []string{";;x"}, }, } { r := strings.NewReader(test.zone) var j int z := NewZoneParser(r, "", "") for rr, ok := z.Next(); ok; rr, ok = z.Next() { if j >= len(test.comments) { t.Fatalf("too many records for zone %d at %d record, expected %d", i, j+1, len(test.comments)) } if z.Comment() != test.comments[j] { t.Errorf("invalid comment for record %d:%d %v", i, j, rr) t.Logf("expected %q", test.comments[j]) t.Logf("got %q", z.Comment()) } j++ } if err := z.Err(); err != nil { t.Fatal(err) } if j != len(test.comments) { t.Errorf("too few records for zone %d, got %d, expected %d", i, j, len(test.comments)) } } } func TestEUIxx(t *testing.T) { tests := map[string]string{ "host.example. IN EUI48 00-00-5e-90-01-2a": "host.example.\t3600\tIN\tEUI48\t00-00-5e-90-01-2a", "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", } for i, o := range tests { r, err := NewRR(i) if err != nil { t.Errorf("failed to parse %s: %v", i, err) } if r.String() != o { t.Errorf("want %s, got %s", o, r.String()) } } } func TestUserRR(t *testing.T) { tests := map[string]string{ "host.example. IN UID 1234": "host.example.\t3600\tIN\tUID\t1234", "host.example. IN GID 1234556": "host.example.\t3600\tIN\tGID\t1234556", "host.example. IN UINFO \"Miek Gieben\"": "host.example.\t3600\tIN\tUINFO\t\"Miek Gieben\"", } for i, o := range tests { r, err := NewRR(i) if err != nil { t.Errorf("failed to parse %s: %v", i, err) } if r.String() != o { t.Errorf("want %s, got %s", o, r.String()) } } } func TestTXT(t *testing.T) { // Test single entry TXT record rr, err := NewRR(`_raop._tcp.local. 60 IN TXT "single value"`) if err != nil { t.Error("failed to parse single value TXT record", err) } else if rr, ok := rr.(*TXT); !ok { t.Error("wrong type, record should be of type TXT") } else { if len(rr.Txt) != 1 { t.Error("bad size of TXT value:", len(rr.Txt)) } else if rr.Txt[0] != "single value" { t.Error("bad single value") } if rr.String() != `_raop._tcp.local. 60 IN TXT "single value"` { t.Error("bad representation of TXT record:", rr.String()) } if Len(rr) != 28+1+12 { t.Error("bad size of serialized record:", Len(rr)) } } // Test multi entries TXT record rr, err = NewRR(`_raop._tcp.local. 60 IN TXT "a=1" "b=2" "c=3" "d=4"`) if err != nil { t.Error("failed to parse multi-values TXT record", err) } else if rr, ok := rr.(*TXT); !ok { t.Error("wrong type, record should be of type TXT") } else { if len(rr.Txt) != 4 { t.Error("bad size of TXT multi-value:", len(rr.Txt)) } else if rr.Txt[0] != "a=1" || rr.Txt[1] != "b=2" || rr.Txt[2] != "c=3" || rr.Txt[3] != "d=4" { t.Error("bad values in TXT records") } if rr.String() != `_raop._tcp.local. 60 IN TXT "a=1" "b=2" "c=3" "d=4"` { t.Error("bad representation of TXT multi value record:", rr.String()) } if Len(rr) != 28+1+3+1+3+1+3+1+3 { t.Error("bad size of serialized multi value record:", Len(rr)) } } // Test empty-string in TXT record rr, err = NewRR(`_raop._tcp.local. 60 IN TXT ""`) if err != nil { t.Error("failed to parse empty-string TXT record", err) } else if rr, ok := rr.(*TXT); !ok { t.Error("wrong type, record should be of type TXT") } else { if len(rr.Txt) != 1 { t.Error("bad size of TXT empty-string value:", len(rr.Txt)) } else if rr.Txt[0] != "" { t.Error("bad value for empty-string TXT record") } if rr.String() != `_raop._tcp.local. 60 IN TXT ""` { t.Error("bad representation of empty-string TXT record:", rr.String()) } if Len(rr) != 28+1 { t.Error("bad size of serialized record:", Len(rr)) } } // Test TXT record with string larger than 255 bytes that should be split // up by the parser. Add some escape sequences too to ensure their length // is counted correctly. s := `"\;\\\120` + strings.Repeat("a", 255) + `b"` rr, err = NewRR(`test.local. 60 IN TXT ` + s) if err != nil { t.Error("failed to parse empty-string TXT record", err) } if rr.(*TXT).Txt[1] != "aaab" { t.Errorf("Txt should have two strings, last one must be 'aaab', but is %s", rr.(*TXT).Txt[1]) } rrContent := strings.Replace(rr.String(), rr.Header().String(), "", 1) expectedRRContent := `";\\x` + strings.Repeat("a", 252) + `" "aaab"` if expectedRRContent != rrContent { t.Errorf("Expected TXT RR content to be %#q but got %#q", expectedRRContent, rrContent) } // Test TXT record that is already split up into strings of len <= 255. s = fmt.Sprintf( "%q %q %q %q %q %q", strings.Repeat(`a`, 255), strings.Repeat("b", 255), strings.Repeat("c", 255), strings.Repeat("d", 0), strings.Repeat("e", 1), strings.Repeat("f", 123), ) rr, err = NewRR(`test.local. 60 IN TXT ` + s) if err != nil { t.Error("failed to parse empty-string TXT record", err) } rrContent = strings.Replace(rr.String(), rr.Header().String(), "", 1) expectedRRContent = s // same as input if expectedRRContent != rrContent { t.Errorf("Expected TXT RR content to be %#q but got %#q", expectedRRContent, rrContent) } } func TestTypeXXXX(t *testing.T) { _, err := NewRR("example.com IN TYPE1234 \\# 4 aabbccdd") if err != nil { t.Errorf("failed to parse TYPE1234 RR: %v", err) } _, err = NewRR("example.com IN TYPE655341 \\# 8 aabbccddaabbccdd") if err == nil { t.Errorf("this should not work, for TYPE655341") } _, err = NewRR("example.com IN TYPE1 \\# 4 0a000001") if err != nil { t.Errorf("failed to parse TYPE1 RR: %v", err) } } func TestPTR(t *testing.T) { _, err := NewRR("144.2.0.192.in-addr.arpa. 900 IN PTR ilouse03146p0\\(.example.com.") if err != nil { t.Error("failed to parse ", err) } } func TestDigit(t *testing.T) { tests := map[string]byte{ "miek\\000.nl. 100 IN TXT \"A\"": 0, "miek\\001.nl. 100 IN TXT \"A\"": 1, "miek\\254.nl. 100 IN TXT \"A\"": 254, "miek\\255.nl. 100 IN TXT \"A\"": 255, "miek\\256.nl. 100 IN TXT \"A\"": 0, "miek\\257.nl. 100 IN TXT \"A\"": 1, "miek\\004.nl. 100 IN TXT \"A\"": 4, } for s, i := range tests { r, err := NewRR(s) buf := make([]byte, 40) if err != nil { t.Fatalf("failed to parse %v", err) } PackRR(r, buf, 0, nil, false) if buf[5] != i { t.Fatalf("5 pos must be %d, is %d", i, buf[5]) } r1, _, _ := UnpackRR(buf, 0) if r1.Header().Ttl != 100 { t.Fatalf("TTL should %d, is %d", 100, r1.Header().Ttl) } } } func TestParseRRSIGTimestamp(t *testing.T) { tests := map[string]bool{ `miek.nl. IN RRSIG SOA 8 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2BvuNiUJjSYlJAgzyAE6CF875BMvvZa+Sb0 RlSCL7WODQSQHhCx/fegHhVVF+Iz8N8kOLrmXD1+jO3Bm6Prl5UhcsPx WTBsg/kmxbp8sR1kvH4oZJtVfakG3iDerrxNaf0sQwhZzyfJQAqpC7pcBoc=`: true, `miek.nl. IN RRSIG SOA 8 2 43200 315565800 4102477800 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2BvuNiUJjSYlJAgzyAE6CF875BMvvZa+Sb0 RlSCL7WODQSQHhCx/fegHhVVF+Iz8N8kOLrmXD1+jO3Bm6Prl5UhcsPx WTBsg/kmxbp8sR1kvH4oZJtVfakG3iDerrxNaf0sQwhZzyfJQAqpC7pcBoc=`: true, } for r := range tests { _, err := NewRR(r) if err != nil { t.Error(err) } } } func TestTxtEqual(t *testing.T) { rr1 := new(TXT) rr1.Hdr = RR_Header{Name: ".", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0} rr1.Txt = []string{"a\"a", "\"", "b"} rr2, _ := NewRR(rr1.String()) if rr1.String() != rr2.String() { // This is not an error, but keep this test. t.Errorf("these two TXT records should match:\n%s\n%s", rr1.String(), rr2.String()) } } func TestTxtLong(t *testing.T) { rr1 := new(TXT) rr1.Hdr = RR_Header{Name: ".", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0} // Make a long txt record, this breaks when sending the packet, // but not earlier. rr1.Txt = []string{"start-"} for i := 0; i < 200; i++ { rr1.Txt[0] += "start-" } str := rr1.String() if len(str) < len(rr1.Txt[0]) { t.Error("string conversion should work") } } // Basically, don't crash. func TestMalformedPackets(t *testing.T) { var packets = []string{ "0021641c0000000100000000000078787878787878787878787303636f6d0000100001", } // com = 63 6f 6d for _, packet := range packets { data, _ := hex.DecodeString(packet) var msg Msg msg.Unpack(data) } } type algorithm struct { name uint8 bits int } func TestNewPrivateKey(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } algorithms := []algorithm{ {ECDSAP256SHA256, 256}, {ECDSAP384SHA384, 384}, {RSASHA1, 512}, {RSASHA256, 512}, {ED25519, 256}, } for _, algo := range algorithms { key := new(DNSKEY) key.Hdr.Rrtype = TypeDNSKEY key.Hdr.Name = "miek.nl." key.Hdr.Class = ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 key.Algorithm = algo.name privkey, err := key.Generate(algo.bits) if err != nil { t.Fatal(err) } newPrivKey, err := key.NewPrivateKey(key.PrivateKeyString(privkey)) if err != nil { t.Error(key.String()) t.Error(key.PrivateKeyString(privkey)) t.Fatal(err) } switch newPrivKey := newPrivKey.(type) { case *rsa.PrivateKey: newPrivKey.Precompute() } if !reflect.DeepEqual(privkey, newPrivKey) { t.Errorf("[%v] Private keys differ:\n%#v\n%#v", AlgorithmToString[algo.name], privkey, newPrivKey) } } } // special input test func TestNewRRSpecial(t *testing.T) { var ( rr RR err error expect string ) rr, err = NewRR("; comment") expect = "" if err != nil { t.Errorf("unexpected err: %v", err) } if rr != nil { t.Errorf("unexpected result: [%s] != [%s]", rr, expect) } rr, err = NewRR("") expect = "" if err != nil { t.Errorf("unexpected err: %v", err) } if rr != nil { t.Errorf("unexpected result: [%s] != [%s]", rr, expect) } rr, err = NewRR("$ORIGIN foo.") expect = "" if err != nil { t.Errorf("unexpected err: %v", err) } if rr != nil { t.Errorf("unexpected result: [%s] != [%s]", rr, expect) } rr, err = NewRR(" ") expect = "" if err != nil { t.Errorf("unexpected err: %v", err) } if rr != nil { t.Errorf("unexpected result: [%s] != [%s]", rr, expect) } rr, err = NewRR("\n") expect = "" if err != nil { t.Errorf("unexpected err: %v", err) } if rr != nil { t.Errorf("unexpected result: [%s] != [%s]", rr, expect) } rr, err = NewRR("foo. A 1.1.1.1\nbar. A 2.2.2.2") expect = "foo.\t3600\tIN\tA\t1.1.1.1" if err != nil { t.Errorf("unexpected err: %v", err) } if rr == nil || rr.String() != expect { t.Errorf("unexpected result: [%s] != [%s]", rr, expect) } } func TestPrintfVerbsRdata(t *testing.T) { x, _ := NewRR("www.miek.nl. IN MX 20 mx.miek.nl.") if Field(x, 1) != "20" { t.Errorf("should be 20") } if Field(x, 2) != "mx.miek.nl." { t.Errorf("should be mx.miek.nl.") } x, _ = NewRR("www.miek.nl. IN A 127.0.0.1") if Field(x, 1) != "127.0.0.1" { t.Errorf("should be 127.0.0.1") } x, _ = NewRR("www.miek.nl. IN AAAA ::1") if Field(x, 1) != "::1" { t.Errorf("should be ::1") } x, _ = NewRR("www.miek.nl. IN NSEC a.miek.nl. A NS SOA MX AAAA") if Field(x, 1) != "a.miek.nl." { t.Errorf("should be a.miek.nl.") } if Field(x, 2) != "A NS SOA MX AAAA" { t.Errorf("should be A NS SOA MX AAAA") } x, _ = NewRR("www.miek.nl. IN TXT \"first\" \"second\"") if Field(x, 1) != "first second" { t.Errorf("should be first second") } if Field(x, 0) != "" { t.Errorf("should be empty") } } func TestParseTokenOverflow(t *testing.T) { _, err := NewRR("_443._tcp.example.org. IN TLSA 0 0 0 308205e8308204d0a00302010202100411de8f53b462f6a5a861b712ec6b59300d06092a864886f70d01010b05003070310b300906035504061302555331153013060355040a130c446967694365727420496e6331193017060355040b13107777772e64696769636572742e636f6d312f302d06035504031326446967694365727420534841322048696768204173737572616e636520536572766572204341301e170d3134313130363030303030305a170d3135313131333132303030305a3081a5310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b4c6f7320416e67656c6573313c303a060355040a1333496e7465726e657420436f72706f726174696f6e20666f722041737369676e6564204e616d657320616e64204e756d6265727331133011060355040b130a546563686e6f6c6f6779311830160603550403130f7777772e6578616d706c652e6f726730820122300d06092a864886f70d01010105000382010f003082010a02820101009e663f52a3d18cb67cdfed547408a4e47e4036538988da2798da3b6655f7240d693ed1cb3fe6d6ad3a9e657ff6efa86b83b0cad24e5d31ff2bf70ec3b78b213f1b4bf61bdc669cbbc07d67154128ca92a9b3cbb4213a836fb823ddd4d7cc04918314d25f06086fa9970ba17e357cca9b458c27eb71760ab95e3f9bc898ae89050ae4d09ba2f7e4259d9ff1e072a6971b18355a8b9e53670c3d5dbdbd283f93a764e71b3a4140ca0746090c08510e2e21078d7d07844bf9c03865b531a0bf2ee766bc401f6451c5a1e6f6fb5d5c1d6a97a0abe91ae8b02e89241e07353909ccd5b41c46de207c06801e08f20713603827f2ae3e68cf15ef881d7e0608f70742e30203010001a382024630820242301f0603551d230418301680145168ff90af0207753cccd9656462a212b859723b301d0603551d0e04160414b000a7f422e9b1ce216117c4c46e7164c8e60c553081810603551d11047a3078820f7777772e6578616d706c652e6f7267820b6578616d706c652e636f6d820b6578616d706c652e656475820b6578616d706c652e6e6574820b6578616d706c652e6f7267820f7777772e6578616d706c652e636f6d820f7777772e6578616d706c652e656475820f7777772e6578616d706c652e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b0601050507030230750603551d1f046e306c3034a032a030862e687474703a2f2f63726c332e64696769636572742e636f6d2f736861322d68612d7365727665722d67332e63726c3034a032a030862e687474703a2f2f63726c342e64696769636572742e636f6d2f736861322d68612d7365727665722d67332e63726c30420603551d20043b3039303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f43505330818306082b0601050507010104773075302406082b060105050730018618687474703a2f2f6f6373702e64696769636572742e636f6d304d06082b060105050730028641687474703a2f2f636163657274732e64696769636572742e636f6d2f446967694365727453484132486967684173737572616e636553657276657243412e637274300c0603551d130101ff04023000300d06092a864886f70d01010b050003820101005eac2124dedb3978a86ff3608406acb542d3cb54cb83facd63aec88144d6a1bf15dbf1f215c4a73e241e582365cba9ea50dd306541653b3513af1a0756c1b2720e8d112b34fb67181efad9c4609bdc670fb025fa6e6d42188161b026cf3089a08369c2f3609fc84bcc3479140c1922ede430ca8dbac2b2a3cdacb305ba15dc7361c4c3a5e6daa99cb446cb221b28078a7a944efba70d96f31ac143d959bccd2fd50e30c325ea2624fb6b6dbe9344dbcf133bfbd5b4e892d635dbf31596451672c6b65ba5ac9b3cddea92b35dab1065cae3c8cb6bb450a62ea2f72ea7c6bdc7b65fa09b012392543734083c7687d243f8d0375304d99ccd2e148966a8637a6797") if err != nil { t.Fatalf("long token should not return an error") } } func TestParseTLSA(t *testing.T) { lt := []string{ "_443._tcp.example.org.\t3600\tIN\tTLSA\t1 1 1 c22be239f483c08957bc106219cc2d3ac1a308dfbbdd0a365f17b9351234cf00", "_443._tcp.example.org.\t3600\tIN\tTLSA\t2 1 2 4e85f45179e9cd6e0e68e2eb5be2e85ec9b92d91c609caf3ef0315213e3f92ece92c38397a607214de95c7fadc0ad0f1c604a469a0387959745032c0d51492f3", "_443._tcp.example.org.\t3600\tIN\tTLSA\t3 0 2 69ec8d2277360b215d0cd956b0e2747108dff34b27d461a41c800629e38ee6c2d1230cc9e8e36711330adc6766e6ff7c5fbb37f106f248337c1a20ad682888d2", } for _, o := range lt { rr, err := NewRR(o) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", o, o, rr.String()) } } } func TestParseSMIMEA(t *testing.T) { lt := map[string]string{ "2e85e1db3e62be6ea._smimecert.example.com.\t3600\tIN\tSMIMEA\t1 1 2 bd80f334566928fc18f58df7e4928c1886f48f71ca3fd41cd9b1854aca7c2180aaacad2819612ed68e7bd3701cc39be7f2529b017c0bc6a53e8fb3f0c7d48070": "2e85e1db3e62be6ea._smimecert.example.com.\t3600\tIN\tSMIMEA\t1 1 2 bd80f334566928fc18f58df7e4928c1886f48f71ca3fd41cd9b1854aca7c2180aaacad2819612ed68e7bd3701cc39be7f2529b017c0bc6a53e8fb3f0c7d48070", "2e85e1db3e62be6ea._smimecert.example.com.\t3600\tIN\tSMIMEA\t0 0 1 cdcf0fc66b182928c5217ddd42c826983f5a4b94160ee6c1c9be62d38199f710": "2e85e1db3e62be6ea._smimecert.example.com.\t3600\tIN\tSMIMEA\t0 0 1 cdcf0fc66b182928c5217ddd42c826983f5a4b94160ee6c1c9be62d38199f710", "2e85e1db3e62be6ea._smimecert.example.com.\t3600\tIN\tSMIMEA\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8c26b251fa0c887ba4869f011a65f7e79967c2eb729f5b": "2e85e1db3e62be6ea._smimecert.example.com.\t3600\tIN\tSMIMEA\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8c26b251fa0c887ba4869f011a65f7e79967c2eb729f5b", "2e85e1db3e62be6eb._smimecert.example.com.\t3600\tIN\tSMIMEA\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8 c26b251fa0c887ba4869f01 1a65f7e79967c2eb729f5b": "2e85e1db3e62be6eb._smimecert.example.com.\t3600\tIN\tSMIMEA\t3 0 2 499a1eda2af8828b552cdb9d80c3744a25872fddd73f3898d8e4afa3549595d2dd4340126e759566fe8c26b251fa0c887ba4869f011a65f7e79967c2eb729f5b", } for i, o := range lt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", o, o, rr.String()) } } } func TestParseSSHFP(t *testing.T) { lt := []string{ "test.example.org.\t300\tSSHFP\t1 2 (\n" + "\t\t\t\t\tBC6533CDC95A79078A39A56EA7635984ED655318ADA9\n" + "\t\t\t\t\tB6159E30723665DA95BB )", "test.example.org.\t300\tSSHFP\t1 2 ( BC6533CDC 95A79078A39A56EA7635984ED655318AD A9B6159E3072366 5DA95BB )", } result := "test.example.org.\t300\tIN\tSSHFP\t1 2 BC6533CDC95A79078A39A56EA7635984ED655318ADA9B6159E30723665DA95BB" for _, o := range lt { rr, err := NewRR(o) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != result { t.Errorf("`%s' should be equal to\n\n`%s', but is \n`%s'", o, result, rr.String()) } } } func TestParseHINFO(t *testing.T) { dt := map[string]string{ "example.net. HINFO A B": "example.net. 3600 IN HINFO \"A\" \"B\"", "example.net. HINFO \"A\" \"B\"": "example.net. 3600 IN HINFO \"A\" \"B\"", "example.net. HINFO A B C D E F": "example.net. 3600 IN HINFO \"A\" \"B C D E F\"", "example.net. HINFO AB": "example.net. 3600 IN HINFO \"AB\" \"\"", // "example.net. HINFO PC-Intel-700mhz \"Redhat Linux 7.1\"": "example.net. 3600 IN HINFO \"PC-Intel-700mhz\" \"Redhat Linux 7.1\"", // This one is recommended in Pro Bind book http://www.zytrax.com/books/dns/ch8/hinfo.html // but effectively, even Bind would replace it to correctly formed text when you AXFR // TODO: remove this set of comments or figure support for quoted/unquoted combinations in endingToTxtSlice function } for i, o := range dt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestParseISDN(t *testing.T) { dt := map[string]string{ "example.net. ISDN A B": "example.net. 3600 IN ISDN \"A\" \"B\"", "example.net. ISDN \"A\" \"B\"": "example.net. 3600 IN ISDN \"A\" \"B\"", } for i, o := range dt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestParseCAA(t *testing.T) { lt := map[string]string{ "example.net. CAA 0 issue \"symantec.com\"": "example.net.\t3600\tIN\tCAA\t0 issue \"symantec.com\"", "example.net. CAA 0 issuewild \"symantec.com; stuff\"": "example.net.\t3600\tIN\tCAA\t0 issuewild \"symantec.com; stuff\"", "example.net. CAA 128 tbs \"critical\"": "example.net.\t3600\tIN\tCAA\t128 tbs \"critical\"", "example.net. CAA 2 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\"", "example.net. TYPE257 0 issue \"symantec.com\"": "example.net.\t3600\tIN\tCAA\t0 issue \"symantec.com\"", } for i, o := range lt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestPackCAA(t *testing.T) { m := new(Msg) record := new(CAA) record.Hdr = RR_Header{Name: "example.com.", Rrtype: TypeCAA, Class: ClassINET, Ttl: 0} record.Tag = "issue" record.Value = "symantec.com" record.Flag = 1 m.Answer = append(m.Answer, record) bytes, err := m.Pack() if err != nil { t.Fatalf("failed to pack msg: %v", err) } if err := m.Unpack(bytes); err != nil { t.Fatalf("failed to unpack msg: %v", err) } if len(m.Answer) != 1 { t.Fatalf("incorrect number of answers unpacked") } rr := m.Answer[0].(*CAA) if rr.Tag != "issue" { t.Fatalf("invalid tag for unpacked answer") } else if rr.Value != "symantec.com" { t.Fatalf("invalid value for unpacked answer") } else if rr.Flag != 1 { t.Fatalf("invalid flag for unpacked answer") } } func TestParseURI(t *testing.T) { lt := map[string]string{ "_http._tcp. IN URI 10 1 \"http://www.example.com/path\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"http://www.example.com/path\"", "_http._tcp. IN URI 10 1 \"\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"\"", } for i, o := range lt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestParseAVC(t *testing.T) { avcs := map[string]string{ `example.org. IN AVC "app-name:WOLFGANG|app-class:OAM|business=yes"`: `example.org. 3600 IN AVC "app-name:WOLFGANG|app-class:OAM|business=yes"`, } for avc, o := range avcs { rr, err := NewRR(avc) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", avc, o, rr.String()) } } } func TestParseCSYNC(t *testing.T) { syncs := map[string]string{ `example.com. 3600 IN CSYNC 66 3 A NS AAAA`: `example.com. 3600 IN CSYNC 66 3 A NS AAAA`, } for s, o := range syncs { rr, err := NewRR(s) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", s, o, rr.String()) } } } func TestParseSVCB(t *testing.T) { svcbs := map[string]string{ `example.com. 3600 IN SVCB 0 cloudflare.com.`: `example.com. 3600 IN SVCB 0 cloudflare.com.`, `example.com. 3600 IN SVCB 65000 cloudflare.com. alpn=h2 ipv4hint=3.4.3.2`: `example.com. 3600 IN SVCB 65000 cloudflare.com. alpn="h2" ipv4hint="3.4.3.2"`, `example.com. 3600 IN SVCB 65000 cloudflare.com. key65000=4\ 3 key65001="\" " key65002 key65003= key65004="" key65005== key65006==\"\" key65007=\254 key65008=\032`: `example.com. 3600 IN SVCB 65000 cloudflare.com. key65000="4\ 3" key65001="\"\ " key65002="" key65003="" key65004="" key65005="=" key65006="=\"\"" key65007="\254" key65008="\ "`, // Explained in svcb.go "In AliasMode, records SHOULD NOT include any SvcParams," `example.com. 3600 IN SVCB 0 no-default-alpn`: `example.com. 3600 IN SVCB 0 no-default-alpn.`, // From the specification `example.com. HTTPS 0 foo.example.com.`: `example.com. 3600 IN HTTPS 0 foo.example.com.`, `example.com. SVCB 1 .`: `example.com. 3600 IN SVCB 1 .`, `example.com. SVCB 16 foo.example.com. port=53`: `example.com. 3600 IN SVCB 16 foo.example.com. port="53"`, `example.com. SVCB 1 foo.example.com. key667=hello`: `example.com. 3600 IN SVCB 1 foo.example.com. key667="hello"`, `example.com. SVCB 1 foo.example.com. key667="hello\210qoo"`: `example.com. 3600 IN SVCB 1 foo.example.com. key667="hello\210qoo"`, `example.com. SVCB 1 foo.example.com. ipv6hint="2001:db8::1,2001:db8::53:1"`: `example.com. 3600 IN SVCB 1 foo.example.com. ipv6hint="2001:db8::1,2001:db8::53:1"`, `example.com. SVCB 1 example.com. ipv6hint="2001:db8::198.51.100.100"`: `example.com. 3600 IN SVCB 1 example.com. ipv6hint="2001:db8::c633:6464"`, `example.com. SVCB 16 foo.example.org. alpn=h2,h3-19 mandatory=ipv4hint,alpn ipv4hint=192.0.2.1`: `example.com. 3600 IN SVCB 16 foo.example.org. alpn="h2,h3-19" mandatory="ipv4hint,alpn" ipv4hint="192.0.2.1"`, `example.com. SVCB 16 foo.example.org. alpn="f\\\\oo\\,bar,h2"`: `example.com. 3600 IN SVCB 16 foo.example.org. alpn="f\\\092oo\\\044bar,h2"`, `example.com. SVCB 16 foo.example.org. alpn=f\\\092oo\092,bar,h2`: `example.com. 3600 IN SVCB 16 foo.example.org. alpn="f\\\092oo\\\044bar,h2"`, // From draft-ietf-add-ddr-06 `_dns.example.net. SVCB 1 example.net. alpn=h2 dohpath=/dns-query{?dns}`: `_dns.example.net. 3600 IN SVCB 1 example.net. alpn="h2" dohpath="/dns-query{?dns}"`, `_dns.example.net. SVCB 1 example.net. alpn=h2 dohpath=/dns\045query{\?dns}`: `_dns.example.net. 3600 IN SVCB 1 example.net. alpn="h2" dohpath="/dns-query{?dns}"`, // From RFC9461 Section 7 (https://datatracker.ietf.org/doc/html/rfc9461#section-7) `_dns.simple.example. 7200 IN SVCB 1 simple.example. alpn=dot`: `_dns.simple.example. 7200 IN SVCB 1 simple.example. alpn="dot"`, `_dns.doh.example. 7200 IN SVCB 1 doh.example. alpn=h2 dohpath=/dns-query{?dns}`: `_dns.doh.example. 7200 IN SVCB 1 doh.example. alpn="h2" dohpath="/dns-query{?dns}"`, `_dns.resolver.example. 7200 IN SVCB 1 resolver.example. alpn=dot,doq,h2,h3 dohpath=/q{?dns}`: `_dns.resolver.example. 7200 IN SVCB 1 resolver.example. alpn="dot,doq,h2,h3" dohpath="/q{?dns}"`, `_dns.resolver.example. 7200 IN SVCB 2 resolver.example. alpn=dot port=8530`: `_dns.resolver.example. 7200 IN SVCB 2 resolver.example. alpn="dot" port="8530"`, // From RFC 9540 Section 4.2.1 (https://www.rfc-editor.org/rfc/rfc9540.html#name-the-ohttp-svcparamkey) `_dns.resolver.arpa 7200 IN SVCB 1 doh.example.net alpn=h2 dohpath=/dns-query{?dns} ohttp`: `_dns.resolver.arpa. 7200 IN SVCB 1 doh.example.net. alpn="h2" dohpath="/dns-query{?dns}" ohttp=""`, // From RFC 9540 Section 4.1 (HTTPS RR) (https://www.rfc-editor.org/rfc/rfc9540.html#name-use-in-https-service-rrs) `svc.example.com. 7200 IN HTTPS 1 . alpn=h2 ohttp`: `svc.example.com. 7200 IN HTTPS 1 . alpn="h2" ohttp=""`, `svc.example.com. 7200 IN HTTPS 1 . mandatory=ohttp ohttp`: `svc.example.com. 7200 IN HTTPS 1 . mandatory="ohttp" ohttp=""`, } for s, o := range svcbs { rr, err := NewRR(s) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", s, o, rr.String()) } } } func TestParseBadSVCB(t *testing.T) { header := `example.com. 3600 IN HTTPS ` evils := []string{ `65536 . no-default-alpn`, // bad priority `1 ..`, // bad domain `1 . no-default-alpn=1`, // value illegal `1 . key`, // invalid key `1 . key=`, // invalid key `1 . =`, // invalid key `1 . ==`, // invalid key `1 . =a`, // invalid key `1 . ""`, // invalid key `1 . ""=`, // invalid key `1 . "a"`, // invalid key `1 . "a"=`, // invalid key `1 . key1=`, // we know that key `1 . key65535`, // key reserved `1 . key065534`, // key can't be padded `1 . key65534="f`, // unterminated value `1 . key65534="`, // unterminated value `1 . key65534=\2`, // invalid numeric escape `1 . key65534=\24`, // invalid numeric escape `1 . key65534=\256`, // invalid numeric escape `1 . key65534=\`, // invalid numeric escape `1 . key65534=""alpn`, // zQuote ending needs whitespace `1 . key65534="a"alpn`, // zQuote ending needs whitespace `1 . ipv6hint=1.1.1.1`, // not ipv6 `1 . ipv6hint=1:1:1:1`, // not ipv6 `1 . ipv6hint=a`, // not ipv6 `1 . ipv6hint=`, // empty ipv6 `1 . ipv4hint=1.1.1.1.1`, // not ipv4 `1 . ipv4hint=::fc`, // not ipv4 `1 . ipv4hint=..11`, // not ipv4 `1 . ipv4hint=a`, // not ipv4 `1 . ipv4hint=`, // empty ipv4 `1 . port=`, // empty port `1 . echconfig=YUd`, // bad base64 `1 . alpn=h\`, // unterminated escape `1 . alpn=h2\\.h3`, // comma-separated list with bad character `1 . alpn=h2,,h3`, // empty protocol identifier `1 . alpn=h3,`, // final protocol identifier empty } for _, o := range evils { _, err := NewRR(header + o) if err == nil { t.Error("failed to reject invalid RR: ", header+o) continue } } } func TestParseBadNAPTR(t *testing.T) { // Should look like: mplus.ims.vodafone.com. 3600 IN NAPTR 10 100 "S" "SIP+D2U" "" _sip._udp.mplus.ims.vodafone.com. naptr := `mplus.ims.vodafone.com. 3600 IN NAPTR 10 100 S SIP+D2U _sip._udp.mplus.ims.vodafone.com.` _, err := NewRR(naptr) // parse fails, we should not have leaked a goroutine. if err == nil { t.Fatalf("parsing NAPTR should have failed: %s", naptr) } if err := goroutineLeaked(); err != nil { t.Errorf("leaked goroutines: %s", err) } } func TestUnbalancedParens(t *testing.T) { sig := `example.com. 3600 IN RRSIG MX 15 2 3600 ( 1440021600 1438207200 3613 example.com. ( oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f x8A4M3e23mRZ9VrbpMngwcrqNAg== )` _, err := NewRR(sig) if err == nil { t.Fatalf("failed to detect extra opening brace") } } func TestBad(t *testing.T) { tests := []string{ `" TYPE257 9 1E12\x00\x105"`, `" TYPE256 9 5"`, `" TYPE257 0\"00000000000000400000000000000000000\x00\x10000000000000000000000000000000000 9 l\x16\x01\x005266"`, } for i := range tests { s, err := strconv.Unquote(tests[i]) if err != nil { t.Fatalf("failed to unquote: %q: %s", tests[i], err) } if _, err = NewRR(s); err == nil { t.Errorf("correctly parsed %q", s) } } } func TestNULLRecord(t *testing.T) { // packet captured from iodine packet := `8116840000010001000000000569627a6c700474657374046d69656b026e6c00000a0001c00c000a0001000000000005497f000001` data, _ := hex.DecodeString(packet) msg := new(Msg) err := msg.Unpack(data) if err != nil { t.Fatalf("Failed to unpack NULL record") } if _, ok := msg.Answer[0].(*NULL); !ok { t.Fatalf("Expected packet to contain NULL record") } } func TestAAAAParsing(t *testing.T) { tests := []string{ "2001:db8::1", "1::1", "2001:db81:d2b4:b6ba:50db:49cc:a8d1:5bb1", "::ffff:192.0.2.0", } rrPrefix := ".\t1\tIN\tAAAA\t" for num, tc := range tests { t.Run(fmt.Sprintf("Test %d", num), func(t *testing.T) { rr, err := NewRR(rrPrefix + tc) if err != nil { t.Fatalf("failed to parse RR: %s", err) } // Output presentation format and try to parse again reparseRR, err := NewRR(rr.String()) if err != nil { t.Fatalf("failed to reparse RR: %s", err) } if reparseRR.String() != rrPrefix+tc { t.Errorf("expected %s,got %s", rrPrefix+tc, reparseRR.String()) } }) } } func TestParseAPL(t *testing.T) { tests := []struct { name string in string expect string }{ { "v4", ". APL 1:192.0.2.0/24", ".\t3600\tIN\tAPL\t1:192.0.2.0/24", }, { "v6", ". APL 2:2001:db8::/32", ".\t3600\tIN\tAPL\t2:2001:db8::/32", }, { "null v6", ". APL 2:::/0", ".\t3600\tIN\tAPL\t2:::/0", }, { "null v4", ". APL 1:0.0.0.0/0", ".\t3600\tIN\tAPL\t1:0.0.0.0/0", }, { "full v6", ". APL 2:::/0", ".\t3600\tIN\tAPL\t2:::/0", }, { "full v4", ". APL 1:192.0.2.1/32", ".\t3600\tIN\tAPL\t1:192.0.2.1/32", }, { "full v6", ". APL 2:2001:0db8:d2b4:b6ba:50db:49cc:a8d1:5bb1/128", ".\t3600\tIN\tAPL\t2:2001:db8:d2b4:b6ba:50db:49cc:a8d1:5bb1/128", }, { "v4in6", ". APL 2:::ffff:192.0.2.0/120", ".\t3600\tIN\tAPL\t2:::ffff:192.0.2.0/120", }, { "v4in6 v6 syntax", ". APL 2:::ffff:c000:0200/120", ".\t3600\tIN\tAPL\t2:::ffff:192.0.2.0/120", }, { "negate", ". APL !1:192.0.2.0/24", ".\t3600\tIN\tAPL\t!1:192.0.2.0/24", }, { "multiple", ". APL 1:192.0.2.0/24 !1:192.0.2.1/32 2:2001:db8::/32 !2:2001:db8:1::0/48", ".\t3600\tIN\tAPL\t1:192.0.2.0/24 !1:192.0.2.1/32 2:2001:db8::/32 !2:2001:db8:1::/48", }, { "no address", ". APL", ".\t3600\tIN\tAPL\t", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { rr, err := NewRR(tc.in) if err != nil { t.Fatalf("failed to parse RR: %s", err) } got := rr.String() if got != tc.expect { t.Errorf("expected %q, got %q", tc.expect, got) } }) } } func TestParseAPLErrors(t *testing.T) { tests := []struct { name string in string }{ { "unexpected", `. APL ""`, }, { "unrecognized family", ". APL 3:0.0.0.0/0", }, { "malformed family", ". APL foo:0.0.0.0/0", }, { "malformed address", ". APL 1:192.0.2/16", }, { "extra bits", ". APL 2:2001:db8::/0", }, { "address mismatch v2", ". APL 1:2001:db8::/64", }, { "address mismatch v6", ". APL 2:192.0.2.1/32", }, { "no prefix", ". APL 1:192.0.2.1", }, { "no family", ". APL 0.0.0.0/0", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { _, err := NewRR(tc.in) if err == nil { t.Fatal("expected error, got none") } }) } } func TestUnpackRRWithHeaderInvalidLengths(t *testing.T) { rr, err := NewRR("test.example.org. 300 IN SSHFP 1 2 BC6533CDC95A79078A39A56EA7635984ED655318ADA9B6159E30723665DA95BB") if err != nil { t.Fatalf("failed to parse SSHFP record: %v", err) } buf := make([]byte, Len(rr)) headerEnd, end, err := packRR(rr, buf, 0, compressionMap{}, false) if err != nil { t.Fatalf("failed to pack A record: %v", err) } rr.Header().Rdlength = uint16(end - headerEnd) for _, off := range []int{ -1, end + 1, 1<<16 - 1, } { _, _, err := UnpackRRWithHeader(*rr.Header(), buf, off) if de, ok := err.(*Error); !ok || de.err != "bad off" { t.Errorf("UnpackRRWithHeader with bad offset (%d) returned wrong or no error: %v", off, err) } } for _, rdlength := range []uint16{ uint16(end - headerEnd + 1), uint16(end), 1<<16 - 1, } { rr.Header().Rdlength = rdlength _, _, err := UnpackRRWithHeader(*rr.Header(), buf, headerEnd) if de, ok := err.(*Error); !ok || de.err != "bad rdlength" { t.Errorf("UnpackRRWithHeader with bad rdlength (%d) returned wrong or no error: %v", rdlength, err) } } } func TestParseZONEMD(t *testing.T) { // Uses examples from https://tools.ietf.org/html/rfc8976 dt := map[string]string{ // Simple Zone `example. 86400 IN ZONEMD 2018031900 1 1 ( c68090d90a7aed71 6bc459f9340e3d7c 1370d4d24b7e2fc3 a1ddc0b9a87153b9 a9713b3c9ae5cc27 777f98b8e730044c ) `: "example.\t86400\tIN\tZONEMD\t2018031900 1 1 c68090d90a7aed716bc459f9340e3d7c1370d4d24b7e2fc3a1ddc0b9a87153b9a9713b3c9ae5cc27777f98b8e730044c", // Complex Zone `example. 86400 IN ZONEMD 2018031900 1 1 ( a3b69bad980a3504 e1cffcb0fd6397f9 3848071c93151f55 2ae2f6b1711d4bd2 d8b39808226d7b9d b71e34b72077f8fe ) `: "example.\t86400\tIN\tZONEMD\t2018031900 1 1 a3b69bad980a3504e1cffcb0fd6397f93848071c93151f552ae2f6b1711d4bd2d8b39808226d7b9db71e34b72077f8fe", // Multiple Digests Zone `example. 86400 IN ZONEMD 2018031900 1 1 ( 62e6cf51b02e54b9 b5f967d547ce4313 6792901f9f88e637 493daaf401c92c27 9dd10f0edb1c56f8 080211f8480ee306 ) `: "example.\t86400\tIN\tZONEMD\t2018031900 1 1 62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306", `example. 86400 IN ZONEMD 2018031900 1 2 ( 08cfa1115c7b948c 4163a901270395ea 226a930cd2cbcf2f a9a5e6eb85f37c8a 4e114d884e66f176 eab121cb02db7d65 2e0cc4827e7a3204 f166b47e5613fd27 ) `: "example.\t86400\tIN\tZONEMD\t2018031900 1 2 08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27", `example. 86400 IN ZONEMD 2018031900 1 240 ( e2d523f654b9422a 96c5a8f44607bbee ) `: "example. 86400 IN ZONEMD 2018031900 1 240 e2d523f654b9422a96c5a8f44607bbee", `example. 86400 IN ZONEMD 2018031900 241 1 ( e1846540e33a9e41 89792d18d5d131f6 05fc283e ) `: "example. 86400 IN ZONEMD 2018031900 241 1 e1846540e33a9e4189792d18d5d131f605fc283e", // URI.ARPA zone `uri.arpa. 3600 IN ZONEMD 2018100702 1 1 ( 0dbc3c4dbfd75777c12ca19c337854b1577799901307c482e9d91d5d15 cd934d16319d98e30c4201cf25a1d5a0254960 )`: "uri.arpa.\t3600\tIN\tZONEMD\t2018100702 1 1 0dbc3c4dbfd75777c12ca19c337854b1577799901307c482e9d91d5d15cd934d16319d98e30c4201cf25a1d5a0254960", // ROOT-SERVERS.NET Zone `root-servers.net. 3600000 IN ZONEMD 2018091100 1 1 ( f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a97 8a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79 ) `: "root-servers.net.\t3600000\tIN\tZONEMD\t2018091100 1 1 f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a978a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79", } for i, o := range dt { rr, err := NewRR(i) if err != nil { t.Error("failed to parse RR: ", err) continue } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) } } } func TestParseIPSECKEY(t *testing.T) { dt := map[string]string{ "ipseckey. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==": "ipseckey.\t3600\tIN\tIPSECKEY\t10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", "ipseckey. 3600 IN IPSECKEY 10 1 2 1.2.3.4 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==": "ipseckey.\t3600\tIN\tIPSECKEY\t10 1 2 1.2.3.4 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", "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==", "ipseckey. 3600 IN IPSECKEY 10 3 2 ipseckey2. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==": "ipseckey.\t3600\tIN\tIPSECKEY\t10 3 2 ipseckey2. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", } for i, o := range dt { rr := testRR(i).(*IPSECKEY) if s := rr.String(); s != o { t.Errorf("input %#v does not match expected output %#v", s, o) } } } func TestParseAMTRELAY(t *testing.T) { dt := map[string]string{ "amtrelay. 3600 IN AMTRELAY 10 0 0 .": "amtrelay.\t3600\tIN\tAMTRELAY\t10 0 0 .", "amtrelay. 3600 IN AMTRELAY 10 0 1 1.2.3.4": "amtrelay.\t3600\tIN\tAMTRELAY\t10 0 1 1.2.3.4", "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", "amtrelay. 3600 IN AMTRELAY 10 1 3 amtrelay2.": "amtrelay.\t3600\tIN\tAMTRELAY\t10 1 3 amtrelay2.", } for i, o := range dt { rr := testRR(i).(*AMTRELAY) if s := rr.String(); s != o { t.Errorf("input %#v does not match expected output %#v", s, o) } } } func TestParseOPENPGPKEY(t *testing.T) { dt := map[string]string{ "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db._openpgpkey 3600 IN OPENPGPKEY mDMEZCMu8xYJKwYBBAHaRw8BAQdAH4FTbN/H5SoMBl9Ez2cFQ1NuzymK894fq2ffsYDvRkG0EWFsaWNlQGV4YW1wbGUuY29tiJYEExYKAD4CGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AWIQRjw8oAQytQxDz5Q/Io7xpohfeBngUCZCMv5gUJAAk7ZgAKCRAo7xpohfeBnlmVAP9k0slIpLwddCD1bZ9qVjqzNcS743OIDny7XuH6x02L2wEAwxqAotO7/oUm0L4wyYR6hvGlhuGMSZXc9xMwZ1wVcA8=": "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db._openpgpkey.\t3600\tIN\tOPENPGPKEY\tmDMEZCMu8xYJKwYBBAHaRw8BAQdAH4FTbN/H5SoMBl9Ez2cFQ1NuzymK894fq2ffsYDvRkG0EWFsaWNlQGV4YW1wbGUuY29tiJYEExYKAD4CGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AWIQRjw8oAQytQxDz5Q/Io7xpohfeBngUCZCMv5gUJAAk7ZgAKCRAo7xpohfeBnlmVAP9k0slIpLwddCD1bZ9qVjqzNcS743OIDny7XuH6x02L2wEAwxqAotO7/oUm0L4wyYR6hvGlhuGMSZXc9xMwZ1wVcA8=", "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", "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", } for i, o := range dt { rr := testRR(i).(*OPENPGPKEY) if s := rr.String(); s != o { t.Errorf("input %#v does not match expected output %#v", s, o) } } } func TestParseRRSIGAlgNames(t *testing.T) { tests := map[string]uint8{ `miek.nl. IN RRSIG SOA RSASHA1 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: RSASHA1, `miek.nl. IN RRSIG SOA RSAMD5 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: RSAMD5, `miek.nl. IN RRSIG SOA ECC-GOST 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: ECCGOST, `miek.nl. IN RRSIG SOA ED448 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: ED448, `miek.nl. IN RRSIG SOA ECDSAP256SHA256 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: ECDSAP256SHA256, `miek.nl. IN RRSIG SOA INDIRECT 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: INDIRECT, `miek.nl. IN RRSIG SOA BLA 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: 0, `miek.nl. IN RRSIG SOA - 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2Bvu7pcBoc=`: 0, } for r, alg := range tests { rr, err := NewRR(r) if alg != 0 && err != nil { t.Error(err) continue } if alg != 0 && rr.(*RRSIG).Algorithm != alg { t.Errorf("expecting alg %d, got %d", alg, rr.(*RRSIG).Algorithm) } } } golang-github-miekg-dns-1.1.64/privaterr.go000066400000000000000000000063531476742671700206070ustar00rootroot00000000000000package dns import "strings" // PrivateRdata is an interface used for implementing "Private Use" RR types, see // RFC 6895. This allows one to experiment with new RR types, without requesting an // official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove. type PrivateRdata interface { // String returns the text presentation of the Rdata of the Private RR. String() string // Parse parses the Rdata of the private RR. Parse([]string) error // Pack is used when packing a private RR into a buffer. Pack([]byte) (int, error) // Unpack is used when unpacking a private RR from a buffer. Unpack([]byte) (int, error) // Copy copies the Rdata into the PrivateRdata argument. Copy(PrivateRdata) error // Len returns the length in octets of the Rdata. Len() int } // PrivateRR represents an RR that uses a PrivateRdata user-defined type. // It mocks normal RRs and implements dns.RR interface. type PrivateRR struct { Hdr RR_Header Data PrivateRdata generator func() PrivateRdata // for copy } // Header return the RR header of r. func (r *PrivateRR) Header() *RR_Header { return &r.Hdr } func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() } // Private len and copy parts to satisfy RR interface. func (r *PrivateRR) len(off int, compression map[string]struct{}) int { l := r.Hdr.len(off, compression) l += r.Data.Len() return l } func (r *PrivateRR) copy() RR { // make new RR like this: rr := &PrivateRR{r.Hdr, r.generator(), r.generator} if err := r.Data.Copy(rr.Data); err != nil { panic("dns: got value that could not be used to copy Private rdata: " + err.Error()) } return rr } func (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { n, err := r.Data.Pack(msg[off:]) if err != nil { return len(msg), err } off += n return off, nil } func (r *PrivateRR) unpack(msg []byte, off int) (int, error) { off1, err := r.Data.Unpack(msg[off:]) off += off1 return off, err } func (r *PrivateRR) parse(c *zlexer, origin string) *ParseError { var l lex text := make([]string, 0, 2) // could be 0..N elements, median is probably 1 Fetch: for { // TODO(miek): we could also be returning _QUOTE, this might or might not // be an issue (basically parsing TXT becomes hard) switch l, _ = c.Next(); l.value { case zNewline, zEOF: break Fetch case zString: text = append(text, l.token) } } err := r.Data.Parse(text) if err != nil { return &ParseError{wrappedErr: err, lex: l} } return nil } func (r *PrivateRR) isDuplicate(r2 RR) bool { return false } // PrivateHandle registers a private resource record type. It requires // string and numeric representation of private RR type and generator function as argument. func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) { rtypestr = strings.ToUpper(rtypestr) TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator(), generator} } TypeToString[rtype] = rtypestr StringToType[rtypestr] = rtype } // PrivateHandleRemove removes definitions required to support private RR type. func PrivateHandleRemove(rtype uint16) { rtypestr, ok := TypeToString[rtype] if ok { delete(TypeToRR, rtype) delete(TypeToString, rtype) delete(StringToType, rtypestr) } } golang-github-miekg-dns-1.1.64/privaterr_test.go000066400000000000000000000070161476742671700216430ustar00rootroot00000000000000package dns_test import ( "strings" "testing" "github.com/miekg/dns" ) const TypeISBN uint16 = 0xFF00 // A crazy new RR type :) type ISBN struct { x string // rdata with 10 or 13 numbers, dashes or spaces allowed } func NewISBN() dns.PrivateRdata { return &ISBN{""} } func (rd *ISBN) Len() int { return len([]byte(rd.x)) } func (rd *ISBN) String() string { return rd.x } func (rd *ISBN) Parse(txt []string) error { rd.x = strings.TrimSpace(strings.Join(txt, " ")) return nil } func (rd *ISBN) Pack(buf []byte) (int, error) { b := []byte(rd.x) n := copy(buf, b) if n != len(b) { return n, dns.ErrBuf } return n, nil } func (rd *ISBN) Unpack(buf []byte) (int, error) { rd.x = string(buf) return len(buf), nil } func (rd *ISBN) Copy(dest dns.PrivateRdata) error { isbn, ok := dest.(*ISBN) if !ok { return dns.ErrRdata } isbn.x = rd.x return nil } var testrecord = strings.Join([]string{"example.org.", "3600", "IN", "ISBN", "12-3 456789-0-123"}, "\t") func TestPrivateText(t *testing.T) { dns.PrivateHandle("ISBN", TypeISBN, NewISBN) defer dns.PrivateHandleRemove(TypeISBN) rr, err := dns.NewRR(testrecord) if err != nil { t.Fatal(err) } if rr.String() != testrecord { t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord) } } func TestPrivateByteSlice(t *testing.T) { dns.PrivateHandle("ISBN", TypeISBN, NewISBN) defer dns.PrivateHandleRemove(TypeISBN) rr, err := dns.NewRR(testrecord) if err != nil { t.Fatal(err) } buf := make([]byte, 100) off, err := dns.PackRR(rr, buf, 0, nil, false) if err != nil { t.Errorf("got error packing ISBN: %v", err) } custrr := rr.(*dns.PrivateRR) if ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off { t.Errorf("offset is not matching to length of Private RR: %d!=%d", off, ln) } rr1, off1, err := dns.UnpackRR(buf[:off], 0) if err != nil { t.Errorf("got error unpacking ISBN: %v", err) return } if off1 != off { t.Errorf("offset after unpacking differs: %d != %d", off1, off) } if rr1.String() != testrecord { t.Errorf("record string representation did not match original %#v != %#v", rr1.String(), testrecord) } } const TypeVERSION uint16 = 0xFF01 type VERSION struct { x string } func NewVersion() dns.PrivateRdata { return &VERSION{""} } func (rd *VERSION) String() string { return rd.x } func (rd *VERSION) Parse(txt []string) error { rd.x = strings.TrimSpace(strings.Join(txt, " ")) return nil } func (rd *VERSION) Pack(buf []byte) (int, error) { b := []byte(rd.x) n := copy(buf, b) if n != len(b) { return n, dns.ErrBuf } return n, nil } func (rd *VERSION) Unpack(buf []byte) (int, error) { rd.x = string(buf) return len(buf), nil } func (rd *VERSION) Copy(dest dns.PrivateRdata) error { isbn, ok := dest.(*VERSION) if !ok { return dns.ErrRdata } isbn.x = rd.x return nil } func (rd *VERSION) Len() int { return len([]byte(rd.x)) } var smallzone = `$ORIGIN example.org. @ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( 2014091518 7200 3600 1209600 3600 ) A 1.2.3.4 ok ISBN 1231-92110-12 go VERSION ( 1.3.1 ; comment ) www ISBN 1231-92110-16 * CNAME @ ` func TestPrivateZoneParser(t *testing.T) { dns.PrivateHandle("ISBN", TypeISBN, NewISBN) dns.PrivateHandle("VERSION", TypeVERSION, NewVersion) defer dns.PrivateHandleRemove(TypeISBN) defer dns.PrivateHandleRemove(TypeVERSION) r := strings.NewReader(smallzone) z := dns.NewZoneParser(r, ".", "") for _, ok := z.Next(); ok; _, ok = z.Next() { } if err := z.Err(); err != nil { t.Fatal(err) } } golang-github-miekg-dns-1.1.64/reverse.go000066400000000000000000000025071476742671700202410ustar00rootroot00000000000000package dns // StringToType is the reverse of TypeToString, needed for string parsing. var StringToType = reverseInt16(TypeToString) // StringToClass is the reverse of ClassToString, needed for string parsing. var StringToClass = reverseInt16(ClassToString) // StringToOpcode is a map of opcodes to strings. var StringToOpcode = reverseInt(OpcodeToString) // StringToRcode is a map of rcodes to strings. var StringToRcode = reverseInt(RcodeToString) func init() { // Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733. StringToRcode["NOTIMPL"] = RcodeNotImplemented } // StringToAlgorithm is the reverse of AlgorithmToString. var StringToAlgorithm = reverseInt8(AlgorithmToString) // StringToHash is a map of names to hash IDs. var StringToHash = reverseInt8(HashToString) // StringToCertType is the reverseof CertTypeToString. var StringToCertType = reverseInt16(CertTypeToString) // Reverse a map func reverseInt8(m map[uint8]string) map[string]uint8 { n := make(map[string]uint8, len(m)) for u, s := range m { n[s] = u } return n } func reverseInt16(m map[uint16]string) map[string]uint16 { n := make(map[string]uint16, len(m)) for u, s := range m { n[s] = u } return n } func reverseInt(m map[int]string) map[string]int { n := make(map[string]int, len(m)) for u, s := range m { n[s] = u } return n } golang-github-miekg-dns-1.1.64/rr_test.go000066400000000000000000000002771476742671700202520ustar00rootroot00000000000000package dns // testRR is a helper that wraps a call to NewRR and panics if the error is non-nil. func testRR(s string) RR { r, err := NewRR(s) if err != nil { panic(err) } return r } golang-github-miekg-dns-1.1.64/sanitize.go000066400000000000000000000037541476742671700204210ustar00rootroot00000000000000package dns // Dedup removes identical RRs from rrs. It preserves the original ordering. // The lowest TTL of any duplicates is used in the remaining one. Dedup modifies // rrs. // m is used to store the RRs temporary. If it is nil a new map will be allocated. func Dedup(rrs []RR, m map[string]RR) []RR { if m == nil { m = make(map[string]RR) } // Save the keys, so we don't have to call normalizedString twice. keys := make([]*string, 0, len(rrs)) for _, r := range rrs { key := normalizedString(r) keys = append(keys, &key) if mr, ok := m[key]; ok { // Shortest TTL wins. rh, mrh := r.Header(), mr.Header() if mrh.Ttl > rh.Ttl { mrh.Ttl = rh.Ttl } continue } m[key] = r } // If the length of the result map equals the amount of RRs we got, // it means they were all different. We can then just return the original rrset. if len(m) == len(rrs) { return rrs } j := 0 for i, r := range rrs { // If keys[i] lives in the map, we should copy and remove it. if _, ok := m[*keys[i]]; ok { delete(m, *keys[i]) rrs[j] = r j++ } if len(m) == 0 { break } } return rrs[:j] } // normalizedString returns a normalized string from r. The TTL // is removed and the domain name is lowercased. We go from this: // DomainNameTTLCLASSTYPERDATA to: // lowercasenameCLASSTYPE... func normalizedString(r RR) string { // A string Go DNS makes has: domainnameTTL... b := []byte(r.String()) // find the first non-escaped tab, then another, so we capture where the TTL lives. esc := false ttlStart, ttlEnd := 0, 0 for i := 0; i < len(b) && ttlEnd == 0; i++ { switch { case b[i] == '\\': esc = !esc case b[i] == '\t' && !esc: if ttlStart == 0 { ttlStart = i continue } if ttlEnd == 0 { ttlEnd = i } case b[i] >= 'A' && b[i] <= 'Z' && !esc: b[i] += 32 default: esc = false } } // remove TTL. copy(b[ttlStart:], b[ttlEnd:]) cut := ttlEnd - ttlStart return string(b[:len(b)-cut]) } golang-github-miekg-dns-1.1.64/sanitize_test.go000066400000000000000000000040031476742671700214440ustar00rootroot00000000000000package dns import "testing" func TestDedup(t *testing.T) { testcases := map[[3]RR][]string{ [...]RR{ testRR("mIek.nl. IN A 127.0.0.1"), testRR("mieK.nl. IN A 127.0.0.1"), testRR("miek.Nl. IN A 127.0.0.1"), }: {"mIek.nl.\t3600\tIN\tA\t127.0.0.1"}, [...]RR{ testRR("miEk.nl. 2000 IN A 127.0.0.1"), testRR("mieK.Nl. 1000 IN A 127.0.0.1"), testRR("Miek.nL. 500 IN A 127.0.0.1"), }: {"miEk.nl.\t500\tIN\tA\t127.0.0.1"}, [...]RR{ testRR("miek.nl. IN A 127.0.0.1"), testRR("miek.nl. CH A 127.0.0.1"), testRR("miek.nl. IN A 127.0.0.1"), }: {"miek.nl.\t3600\tIN\tA\t127.0.0.1", "miek.nl.\t3600\tCH\tA\t127.0.0.1", }, [...]RR{ testRR("miek.nl. CH A 127.0.0.1"), testRR("miek.nl. IN A 127.0.0.1"), testRR("miek.de. IN A 127.0.0.1"), }: {"miek.nl.\t3600\tCH\tA\t127.0.0.1", "miek.nl.\t3600\tIN\tA\t127.0.0.1", "miek.de.\t3600\tIN\tA\t127.0.0.1", }, [...]RR{ testRR("miek.de. IN A 127.0.0.1"), testRR("miek.nl. 200 IN A 127.0.0.1"), testRR("miek.nl. 300 IN A 127.0.0.1"), }: {"miek.de.\t3600\tIN\tA\t127.0.0.1", "miek.nl.\t200\tIN\tA\t127.0.0.1", }, } for rr, expected := range testcases { out := Dedup([]RR{rr[0], rr[1], rr[2]}, nil) for i, o := range out { if o.String() != expected[i] { t.Fatalf("expected %v, got %v", expected[i], o.String()) } } } } func BenchmarkDedup(b *testing.B) { rrs := []RR{ testRR("miEk.nl. 2000 IN A 127.0.0.1"), testRR("mieK.Nl. 1000 IN A 127.0.0.1"), testRR("Miek.nL. 500 IN A 127.0.0.1"), } m := make(map[string]RR) for i := 0; i < b.N; i++ { Dedup(rrs, m) } } func TestNormalizedString(t *testing.T) { tests := map[RR]string{ testRR("mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1", testRR("m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1", testRR("m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\009iek.nl.\tIN\tA\t127.0.0.1", } for tc, expected := range tests { n := normalizedString(tc) if n != expected { t.Errorf("expected %s, got %s", expected, n) } } } golang-github-miekg-dns-1.1.64/scan.go000066400000000000000000000760421476742671700175170ustar00rootroot00000000000000package dns import ( "bufio" "fmt" "io" "io/fs" "os" "path" "path/filepath" "strconv" "strings" ) const maxTok = 512 // Token buffer start size, and growth size amount. // The maximum depth of $INCLUDE directives supported by the // ZoneParser API. const maxIncludeDepth = 7 // Tokenize a RFC 1035 zone file. The tokenizer will normalize it: // * Add ownernames if they are left blank; // * Suppress sequences of spaces; // * Make each RR fit on one line (_NEWLINE is send as last) // * Handle comments: ; // * Handle braces - anywhere. const ( // Zonefile zEOF = iota zString zBlank zQuote zNewline zRrtpe zOwner zClass zDirOrigin // $ORIGIN zDirTTL // $TTL zDirInclude // $INCLUDE zDirGenerate // $GENERATE // Privatekey file zValue zKey zExpectOwnerDir // Ownername zExpectOwnerBl // Whitespace after the ownername zExpectAny // Expect rrtype, ttl or class zExpectAnyNoClass // Expect rrtype or ttl zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS zExpectAnyNoTTL // Expect rrtype or class zExpectAnyNoTTLBl // Whitespace after _EXPECT_ANY_NOTTL zExpectRrtype // Expect rrtype zExpectRrtypeBl // Whitespace BEFORE rrtype zExpectRdata // The first element of the rdata zExpectDirTTLBl // Space after directive $TTL zExpectDirTTL // Directive $TTL zExpectDirOriginBl // Space after directive $ORIGIN zExpectDirOrigin // Directive $ORIGIN zExpectDirIncludeBl // Space after directive $INCLUDE zExpectDirInclude // Directive $INCLUDE zExpectDirGenerate // Directive $GENERATE zExpectDirGenerateBl // Space after directive $GENERATE ) // ParseError is a parsing error. It contains the parse error and the location in the io.Reader // where the error occurred. type ParseError struct { file string err string wrappedErr error lex lex } func (e *ParseError) Error() (s string) { if e.file != "" { s = e.file + ": " } if e.err == "" && e.wrappedErr != nil { e.err = e.wrappedErr.Error() } s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " + strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column) return } func (e *ParseError) Unwrap() error { return e.wrappedErr } type lex struct { token string // text of the token err bool // when true, token text has lexer error value uint8 // value: zString, _BLANK, etc. torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar line int // line in the file column int // column in the file } // ttlState describes the state necessary to fill in an omitted RR TTL type ttlState struct { ttl uint32 // ttl is the current default TTL isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive } // NewRR reads a string s and returns the first RR. // If s contains no records, NewRR will return nil with no error. // // The class defaults to IN, TTL defaults to 3600, and // origin for resolving relative domain names defaults to the DNS root (.). // Full zone file syntax is supported, including directives like $TTL and $ORIGIN. // All fields of the returned RR are set from the read data, except RR.Header().Rdlength which is set to 0. func NewRR(s string) (RR, error) { if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline return ReadRR(strings.NewReader(s+"\n"), "") } return ReadRR(strings.NewReader(s), "") } // ReadRR reads the RR contained in r. // // The string file is used in error reporting and to resolve relative // $INCLUDE directives. // // See NewRR for more documentation. func ReadRR(r io.Reader, file string) (RR, error) { zp := NewZoneParser(r, ".", file) zp.SetDefaultTTL(defaultTtl) zp.SetIncludeAllowed(true) rr, _ := zp.Next() return rr, zp.Err() } // ZoneParser is a parser for an RFC 1035 style zonefile. // // Each parsed RR in the zone is returned sequentially from Next. An // optional comment can be retrieved with Comment. // // The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all // supported. Although $INCLUDE is disabled by default. // Note that $GENERATE's range support up to a maximum of 65535 steps. // // Basic usage pattern when reading from a string (z) containing the // zone data: // // zp := NewZoneParser(strings.NewReader(z), "", "") // // for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { // // Do something with rr // } // // if err := zp.Err(); err != nil { // // log.Println(err) // } // // Comments specified after an RR (and on the same line!) are // returned too: // // foo. IN A 10.0.0.1 ; this is a comment // // The text "; this is comment" is returned from Comment. Comments inside // the RR are returned concatenated along with the RR. Comments on a line // by themselves are discarded. // // Callers should not assume all returned data in an Resource Record is // syntactically correct, e.g. illegal base64 in RRSIGs will be returned as-is. type ZoneParser struct { c *zlexer parseErr *ParseError origin string file string defttl *ttlState h RR_Header // sub is used to parse $INCLUDE files and $GENERATE directives. // Next, by calling subNext, forwards the resulting RRs from this // sub parser to the calling code. sub *ZoneParser r io.Reader fsys fs.FS includeDepth uint8 includeAllowed bool generateDisallowed bool } // NewZoneParser returns an RFC 1035 style zonefile parser that reads // from r. // // The string file is used in error reporting and to resolve relative // $INCLUDE directives. The string origin is used as the initial // origin, as if the file would start with an $ORIGIN directive. func NewZoneParser(r io.Reader, origin, file string) *ZoneParser { var pe *ParseError if origin != "" { origin = Fqdn(origin) if _, ok := IsDomainName(origin); !ok { pe = &ParseError{file: file, err: "bad initial origin name"} } } return &ZoneParser{ c: newZLexer(r), parseErr: pe, origin: origin, file: file, } } // SetDefaultTTL sets the parsers default TTL to ttl. func (zp *ZoneParser) SetDefaultTTL(ttl uint32) { zp.defttl = &ttlState{ttl, false} } // SetIncludeAllowed controls whether $INCLUDE directives are // allowed. $INCLUDE directives are not supported by default. // // The $INCLUDE directive will open and read from a user controlled // file on the system. Even if the file is not a valid zonefile, the // contents of the file may be revealed in error messages, such as: // // /etc/passwd: dns: not a TTL: "root:x:0:0:root:/root:/bin/bash" at line: 1:31 // /etc/shadow: dns: not a TTL: "root:$6$::0:99999:7:::" at line: 1:125 func (zp *ZoneParser) SetIncludeAllowed(v bool) { zp.includeAllowed = v } // SetIncludeFS provides an [fs.FS] to use when looking for the target of // $INCLUDE directives. ($INCLUDE must still be enabled separately by calling // [ZoneParser.SetIncludeAllowed].) If fsys is nil, [os.Open] will be used. // // When fsys is an on-disk FS, the ability of $INCLUDE to reach files from // outside its root directory depends upon the FS implementation. For // instance, [os.DirFS] will refuse to open paths like "../../etc/passwd", // however it will still follow links which may point anywhere on the system. // // FS paths are slash-separated on all systems, even Windows. $INCLUDE paths // containing other characters such as backslash and colon may be accepted as // valid, but those characters will never be interpreted by an FS // implementation as path element separators. See [fs.ValidPath] for more // details. func (zp *ZoneParser) SetIncludeFS(fsys fs.FS) { zp.fsys = fsys } // Err returns the first non-EOF error that was encountered by the // ZoneParser. func (zp *ZoneParser) Err() error { if zp.parseErr != nil { return zp.parseErr } if zp.sub != nil { if err := zp.sub.Err(); err != nil { return err } } return zp.c.Err() } func (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) { zp.parseErr = &ParseError{file: zp.file, err: err, lex: l} return nil, false } // Comment returns an optional text comment that occurred alongside // the RR. func (zp *ZoneParser) Comment() string { if zp.parseErr != nil { return "" } if zp.sub != nil { return zp.sub.Comment() } return zp.c.Comment() } func (zp *ZoneParser) subNext() (RR, bool) { if rr, ok := zp.sub.Next(); ok { return rr, true } if zp.sub.r != nil { if c, ok := zp.sub.r.(io.Closer); ok { c.Close() } zp.sub.r = nil } if zp.sub.Err() != nil { // We have errors to surface. return nil, false } zp.sub = nil return zp.Next() } // Next advances the parser to the next RR in the zonefile and // returns the (RR, true). It will return (nil, false) when the // parsing stops, either by reaching the end of the input or an // error. After Next returns (nil, false), the Err method will return // any error that occurred during parsing. func (zp *ZoneParser) Next() (RR, bool) { if zp.parseErr != nil { return nil, false } if zp.sub != nil { return zp.subNext() } // 6 possible beginnings of a line (_ is a space): // // 0. zRRTYPE -> all omitted until the rrtype // 1. zOwner _ zRrtype -> class/ttl omitted // 2. zOwner _ zString _ zRrtype -> class omitted // 3. zOwner _ zString _ zClass _ zRrtype -> ttl/class // 4. zOwner _ zClass _ zRrtype -> ttl omitted // 5. zOwner _ zClass _ zString _ zRrtype -> class/ttl (reversed) // // After detecting these, we know the zRrtype so we can jump to functions // handling the rdata for each of these types. st := zExpectOwnerDir // initial state h := &zp.h for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() { // zlexer spotted an error already if l.err { return zp.setParseError(l.token, l) } switch st { case zExpectOwnerDir: // We can also expect a directive, like $TTL or $ORIGIN if zp.defttl != nil { h.Ttl = zp.defttl.ttl } h.Class = ClassINET switch l.value { case zNewline: st = zExpectOwnerDir case zOwner: name, ok := toAbsoluteName(l.token, zp.origin) if !ok { return zp.setParseError("bad owner name", l) } h.Name = name st = zExpectOwnerBl case zDirTTL: st = zExpectDirTTLBl case zDirOrigin: st = zExpectDirOriginBl case zDirInclude: st = zExpectDirIncludeBl case zDirGenerate: st = zExpectDirGenerateBl case zRrtpe: h.Rrtype = l.torc st = zExpectRdata case zClass: h.Class = l.torc st = zExpectAnyNoClassBl case zBlank: // Discard, can happen when there is nothing on the // line except the RR type case zString: ttl, ok := stringToTTL(l.token) if !ok { return zp.setParseError("not a TTL", l) } h.Ttl = ttl if zp.defttl == nil || !zp.defttl.isByDirective { zp.defttl = &ttlState{ttl, false} } st = zExpectAnyNoTTLBl default: return zp.setParseError("syntax error at beginning", l) } case zExpectDirIncludeBl: if l.value != zBlank { return zp.setParseError("no blank after $INCLUDE-directive", l) } st = zExpectDirInclude case zExpectDirInclude: if l.value != zString { return zp.setParseError("expecting $INCLUDE value, not this...", l) } neworigin := zp.origin // There may be optionally a new origin set after the filename, if not use current one switch l, _ := zp.c.Next(); l.value { case zBlank: l, _ := zp.c.Next() if l.value == zString { name, ok := toAbsoluteName(l.token, zp.origin) if !ok { return zp.setParseError("bad origin name", l) } neworigin = name } case zNewline, zEOF: // Ok default: return zp.setParseError("garbage after $INCLUDE", l) } if !zp.includeAllowed { return zp.setParseError("$INCLUDE directive not allowed", l) } if zp.includeDepth >= maxIncludeDepth { return zp.setParseError("too deeply nested $INCLUDE", l) } // Start with the new file includePath := l.token var r1 io.Reader var e1 error if zp.fsys != nil { // fs.FS always uses / as separator, even on Windows, so use // path instead of filepath here: if !path.IsAbs(includePath) { includePath = path.Join(path.Dir(zp.file), includePath) } // os.DirFS, and probably others, expect all paths to be // relative, so clean the path and remove leading / if // present: includePath = strings.TrimLeft(path.Clean(includePath), "/") r1, e1 = zp.fsys.Open(includePath) } else { if !filepath.IsAbs(includePath) { includePath = filepath.Join(filepath.Dir(zp.file), includePath) } r1, e1 = os.Open(includePath) } if e1 != nil { var as string if includePath != l.token { as = fmt.Sprintf(" as `%s'", includePath) } zp.parseErr = &ParseError{ file: zp.file, wrappedErr: fmt.Errorf("failed to open `%s'%s: %w", l.token, as, e1), lex: l, } return nil, false } zp.sub = NewZoneParser(r1, neworigin, includePath) zp.sub.defttl, zp.sub.includeDepth, zp.sub.r = zp.defttl, zp.includeDepth+1, r1 zp.sub.SetIncludeAllowed(true) zp.sub.SetIncludeFS(zp.fsys) return zp.subNext() case zExpectDirTTLBl: if l.value != zBlank { return zp.setParseError("no blank after $TTL-directive", l) } st = zExpectDirTTL case zExpectDirTTL: if l.value != zString { return zp.setParseError("expecting $TTL value, not this...", l) } if err := slurpRemainder(zp.c); err != nil { return zp.setParseError(err.err, err.lex) } ttl, ok := stringToTTL(l.token) if !ok { return zp.setParseError("expecting $TTL value, not this...", l) } zp.defttl = &ttlState{ttl, true} st = zExpectOwnerDir case zExpectDirOriginBl: if l.value != zBlank { return zp.setParseError("no blank after $ORIGIN-directive", l) } st = zExpectDirOrigin case zExpectDirOrigin: if l.value != zString { return zp.setParseError("expecting $ORIGIN value, not this...", l) } if err := slurpRemainder(zp.c); err != nil { return zp.setParseError(err.err, err.lex) } name, ok := toAbsoluteName(l.token, zp.origin) if !ok { return zp.setParseError("bad origin name", l) } zp.origin = name st = zExpectOwnerDir case zExpectDirGenerateBl: if l.value != zBlank { return zp.setParseError("no blank after $GENERATE-directive", l) } st = zExpectDirGenerate case zExpectDirGenerate: if zp.generateDisallowed { return zp.setParseError("nested $GENERATE directive not allowed", l) } if l.value != zString { return zp.setParseError("expecting $GENERATE value, not this...", l) } return zp.generate(l) case zExpectOwnerBl: if l.value != zBlank { return zp.setParseError("no blank after owner", l) } st = zExpectAny case zExpectAny: switch l.value { case zRrtpe: if zp.defttl == nil { return zp.setParseError("missing TTL with no previous value", l) } h.Rrtype = l.torc st = zExpectRdata case zClass: h.Class = l.torc st = zExpectAnyNoClassBl case zString: ttl, ok := stringToTTL(l.token) if !ok { return zp.setParseError("not a TTL", l) } h.Ttl = ttl if zp.defttl == nil || !zp.defttl.isByDirective { zp.defttl = &ttlState{ttl, false} } st = zExpectAnyNoTTLBl default: return zp.setParseError("expecting RR type, TTL or class, not this...", l) } case zExpectAnyNoClassBl: if l.value != zBlank { return zp.setParseError("no blank before class", l) } st = zExpectAnyNoClass case zExpectAnyNoTTLBl: if l.value != zBlank { return zp.setParseError("no blank before TTL", l) } st = zExpectAnyNoTTL case zExpectAnyNoTTL: switch l.value { case zClass: h.Class = l.torc st = zExpectRrtypeBl case zRrtpe: h.Rrtype = l.torc st = zExpectRdata default: return zp.setParseError("expecting RR type or class, not this...", l) } case zExpectAnyNoClass: switch l.value { case zString: ttl, ok := stringToTTL(l.token) if !ok { return zp.setParseError("not a TTL", l) } h.Ttl = ttl if zp.defttl == nil || !zp.defttl.isByDirective { zp.defttl = &ttlState{ttl, false} } st = zExpectRrtypeBl case zRrtpe: h.Rrtype = l.torc st = zExpectRdata default: return zp.setParseError("expecting RR type or TTL, not this...", l) } case zExpectRrtypeBl: if l.value != zBlank { return zp.setParseError("no blank before RR type", l) } st = zExpectRrtype case zExpectRrtype: if l.value != zRrtpe { return zp.setParseError("unknown RR type", l) } h.Rrtype = l.torc st = zExpectRdata case zExpectRdata: var ( rr RR parseAsRFC3597 bool ) if newFn, ok := TypeToRR[h.Rrtype]; ok { rr = newFn() *rr.Header() = *h // We may be parsing a known RR type using the RFC3597 format. // If so, we handle that here in a generic way. // // This is also true for PrivateRR types which will have the // RFC3597 parsing done for them and the Unpack method called // to populate the RR instead of simply deferring to Parse. if zp.c.Peek().token == "\\#" { parseAsRFC3597 = true } } else { rr = &RFC3597{Hdr: *h} } _, isPrivate := rr.(*PrivateRR) if !isPrivate && zp.c.Peek().token == "" { // This is a dynamic update rr. if err := slurpRemainder(zp.c); err != nil { return zp.setParseError(err.err, err.lex) } return rr, true } else if l.value == zNewline { return zp.setParseError("unexpected newline", l) } parseAsRR := rr if parseAsRFC3597 { parseAsRR = &RFC3597{Hdr: *h} } if err := parseAsRR.parse(zp.c, zp.origin); err != nil { // err is a concrete *ParseError without the file field set. // The setParseError call below will construct a new // *ParseError with file set to zp.file. // err.lex may be nil in which case we substitute our current // lex token. if err.lex == (lex{}) { return zp.setParseError(err.err, l) } return zp.setParseError(err.err, err.lex) } if parseAsRFC3597 { err := parseAsRR.(*RFC3597).fromRFC3597(rr) if err != nil { return zp.setParseError(err.Error(), l) } } return rr, true } } // If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this // is not an error, because an empty zone file is still a zone file. return nil, false } type zlexer struct { br io.ByteReader readErr error line int column int comBuf string comment string l lex cachedL *lex brace int quote bool space bool commt bool rrtype bool owner bool nextL bool eol bool // end-of-line } func newZLexer(r io.Reader) *zlexer { br, ok := r.(io.ByteReader) if !ok { br = bufio.NewReaderSize(r, 1024) } return &zlexer{ br: br, line: 1, owner: true, } } func (zl *zlexer) Err() error { if zl.readErr == io.EOF { return nil } return zl.readErr } // readByte returns the next byte from the input func (zl *zlexer) readByte() (byte, bool) { if zl.readErr != nil { return 0, false } c, err := zl.br.ReadByte() if err != nil { zl.readErr = err return 0, false } // delay the newline handling until the next token is delivered, // fixes off-by-one errors when reporting a parse error. if zl.eol { zl.line++ zl.column = 0 zl.eol = false } if c == '\n' { zl.eol = true } else { zl.column++ } return c, true } func (zl *zlexer) Peek() lex { if zl.nextL { return zl.l } l, ok := zl.Next() if !ok { return l } if zl.nextL { // Cache l. Next returns zl.cachedL then zl.l. zl.cachedL = &l } else { // In this case l == zl.l, so we just tell Next to return zl.l. zl.nextL = true } return l } func (zl *zlexer) Next() (lex, bool) { l := &zl.l switch { case zl.cachedL != nil: l, zl.cachedL = zl.cachedL, nil return *l, true case zl.nextL: zl.nextL = false return *l, true case l.err: // Parsing errors should be sticky. return lex{value: zEOF}, false } var ( str = make([]byte, maxTok) // Hold string text com = make([]byte, maxTok) // Hold comment text stri int // Offset in str (0 means empty) comi int // Offset in com (0 means empty) escape bool ) if zl.comBuf != "" { comi = copy(com[:], zl.comBuf) zl.comBuf = "" } zl.comment = "" for x, ok := zl.readByte(); ok; x, ok = zl.readByte() { l.line, l.column = zl.line, zl.column if stri >= len(str) { // if buffer length is insufficient, increase it. str = append(str[:], make([]byte, maxTok)...) } if comi >= len(com) { // if buffer length is insufficient, increase it. com = append(com[:], make([]byte, maxTok)...) } switch x { case ' ', '\t': if escape || zl.quote { // Inside quotes or escaped this is legal. str[stri] = x stri++ escape = false break } if zl.commt { com[comi] = x comi++ break } var retL lex if stri == 0 { // Space directly in the beginning, handled in the grammar } else if zl.owner { // If we have a string and it's the first, make it an owner l.value = zOwner l.token = string(str[:stri]) // escape $... start with a \ not a $, so this will work switch strings.ToUpper(l.token) { case "$TTL": l.value = zDirTTL case "$ORIGIN": l.value = zDirOrigin case "$INCLUDE": l.value = zDirInclude case "$GENERATE": l.value = zDirGenerate } retL = *l } else { l.value = zString l.token = string(str[:stri]) if !zl.rrtype { tokenUpper := strings.ToUpper(l.token) if t, ok := StringToType[tokenUpper]; ok { l.value = zRrtpe l.torc = t zl.rrtype = true } else if strings.HasPrefix(tokenUpper, "TYPE") { t, ok := typeToInt(l.token) if !ok { l.token = "unknown RR type" l.err = true return *l, true } l.value = zRrtpe l.torc = t zl.rrtype = true } if t, ok := StringToClass[tokenUpper]; ok { l.value = zClass l.torc = t } else if strings.HasPrefix(tokenUpper, "CLASS") { t, ok := classToInt(l.token) if !ok { l.token = "unknown class" l.err = true return *l, true } l.value = zClass l.torc = t } } retL = *l } zl.owner = false if !zl.space { zl.space = true l.value = zBlank l.token = " " if retL == (lex{}) { return *l, true } zl.nextL = true } if retL != (lex{}) { return retL, true } case ';': if escape || zl.quote { // Inside quotes or escaped this is legal. str[stri] = x stri++ escape = false break } zl.commt = true zl.comBuf = "" if comi > 1 { // A newline was previously seen inside a comment that // was inside braces and we delayed adding it until now. com[comi] = ' ' // convert newline to space comi++ if comi >= len(com) { l.token = "comment length insufficient for parsing" l.err = true return *l, true } } com[comi] = ';' comi++ if stri > 0 { zl.comBuf = string(com[:comi]) l.value = zString l.token = string(str[:stri]) return *l, true } case '\r': escape = false if zl.quote { str[stri] = x stri++ } // discard if outside of quotes case '\n': escape = false // Escaped newline if zl.quote { str[stri] = x stri++ break } if zl.commt { // Reset a comment zl.commt = false zl.rrtype = false // If not in a brace this ends the comment AND the RR if zl.brace == 0 { zl.owner = true l.value = zNewline l.token = "\n" zl.comment = string(com[:comi]) return *l, true } zl.comBuf = string(com[:comi]) break } if zl.brace == 0 { // If there is previous text, we should output it here var retL lex if stri != 0 { l.value = zString l.token = string(str[:stri]) if !zl.rrtype { tokenUpper := strings.ToUpper(l.token) if t, ok := StringToType[tokenUpper]; ok { zl.rrtype = true l.value = zRrtpe l.torc = t } } retL = *l } l.value = zNewline l.token = "\n" zl.comment = zl.comBuf zl.comBuf = "" zl.rrtype = false zl.owner = true if retL != (lex{}) { zl.nextL = true return retL, true } return *l, true } case '\\': // comments do not get escaped chars, everything is copied if zl.commt { com[comi] = x comi++ break } // something already escaped must be in string if escape { str[stri] = x stri++ escape = false break } // something escaped outside of string gets added to string str[stri] = x stri++ escape = true case '"': if zl.commt { com[comi] = x comi++ break } if escape { str[stri] = x stri++ escape = false break } zl.space = false // send previous gathered text and the quote var retL lex if stri != 0 { l.value = zString l.token = string(str[:stri]) retL = *l } // send quote itself as separate token l.value = zQuote l.token = "\"" zl.quote = !zl.quote if retL != (lex{}) { zl.nextL = true return retL, true } return *l, true case '(', ')': if zl.commt { com[comi] = x comi++ break } if escape || zl.quote { // Inside quotes or escaped this is legal. str[stri] = x stri++ escape = false break } switch x { case ')': zl.brace-- if zl.brace < 0 { l.token = "extra closing brace" l.err = true return *l, true } case '(': zl.brace++ } default: escape = false if zl.commt { com[comi] = x comi++ break } str[stri] = x stri++ zl.space = false } } if zl.readErr != nil && zl.readErr != io.EOF { // Don't return any tokens after a read error occurs. return lex{value: zEOF}, false } var retL lex if stri > 0 { // Send remainder of str l.value = zString l.token = string(str[:stri]) retL = *l if comi <= 0 { return retL, true } } if comi > 0 { // Send remainder of com l.value = zNewline l.token = "\n" zl.comment = string(com[:comi]) if retL != (lex{}) { zl.nextL = true return retL, true } return *l, true } if zl.brace != 0 { l.token = "unbalanced brace" l.err = true return *l, true } return lex{value: zEOF}, false } func (zl *zlexer) Comment() string { if zl.l.err { return "" } return zl.comment } // Extract the class number from CLASSxx func classToInt(token string) (uint16, bool) { offset := 5 if len(token) < offset+1 { return 0, false } class, err := strconv.ParseUint(token[offset:], 10, 16) if err != nil { return 0, false } return uint16(class), true } // Extract the rr number from TYPExxx func typeToInt(token string) (uint16, bool) { offset := 4 if len(token) < offset+1 { return 0, false } typ, err := strconv.ParseUint(token[offset:], 10, 16) if err != nil { return 0, false } return uint16(typ), true } // stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds. func stringToTTL(token string) (uint32, bool) { var s, i uint32 for _, c := range token { switch c { case 's', 'S': s += i i = 0 case 'm', 'M': s += i * 60 i = 0 case 'h', 'H': s += i * 60 * 60 i = 0 case 'd', 'D': s += i * 60 * 60 * 24 i = 0 case 'w', 'W': s += i * 60 * 60 * 24 * 7 i = 0 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': i *= 10 i += uint32(c) - '0' default: return 0, false } } return s + i, true } // Parse LOC records' [.][mM] into a // mantissa exponent format. Token should contain the entire // string (i.e. no spaces allowed) func stringToCm(token string) (e, m uint8, ok bool) { if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' { token = token[0 : len(token)-1] } var ( meters, cmeters, val int err error ) mStr, cmStr, hasCM := strings.Cut(token, ".") if hasCM { // 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'). // So we simply reject it. // We also make sure the first character is a digit to reject '+-' signs. cmeters, err = strconv.Atoi(cmStr) if err != nil || len(cmStr) > 2 || cmStr[0] < '0' || cmStr[0] > '9' { return } if len(cmStr) == 1 { // 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm. cmeters *= 10 } } // This slightly ugly condition will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). if !hasCM || mStr != "" { meters, err = strconv.Atoi(mStr) // RFC1876 states the max value is 90000000.00. The latter two conditions enforce it. if err != nil || mStr[0] < '0' || mStr[0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) { return } } if meters > 0 { e = 2 val = meters } else { e = 0 val = cmeters } for val >= 10 { e++ val /= 10 } return e, uint8(val), true } func toAbsoluteName(name, origin string) (absolute string, ok bool) { // check for an explicit origin reference if name == "@" { // require a nonempty origin if origin == "" { return "", false } return origin, true } // require a valid domain name _, ok = IsDomainName(name) if !ok || name == "" { return "", false } // check if name is already absolute if IsFqdn(name) { return name, true } // require a nonempty origin if origin == "" { return "", false } return appendOrigin(name, origin), true } func appendOrigin(name, origin string) string { if origin == "." { return name + origin } return name + "." + origin } // LOC record helper function func locCheckNorth(token string, latitude uint32) (uint32, bool) { if latitude > 90*1000*60*60 { return latitude, false } switch token { case "n", "N": return LOC_EQUATOR + latitude, true case "s", "S": return LOC_EQUATOR - latitude, true } return latitude, false } // LOC record helper function func locCheckEast(token string, longitude uint32) (uint32, bool) { if longitude > 180*1000*60*60 { return longitude, false } switch token { case "e", "E": return LOC_EQUATOR + longitude, true case "w", "W": return LOC_EQUATOR - longitude, true } return longitude, false } // "Eat" the rest of the "line" func slurpRemainder(c *zlexer) *ParseError { l, _ := c.Next() switch l.value { case zBlank: l, _ = c.Next() if l.value != zNewline && l.value != zEOF { return &ParseError{err: "garbage after rdata", lex: l} } case zNewline: case zEOF: default: return &ParseError{err: "garbage after rdata", lex: l} } return nil } // Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64" // Used for NID and L64 record. func stringToNodeID(l lex) (uint64, *ParseError) { if len(l.token) < 19 { return 0, &ParseError{file: l.token, err: "bad NID/L64 NodeID/Locator64", lex: l} } // There must be three colons at fixes positions, if not its a parse error if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { return 0, &ParseError{file: l.token, err: "bad NID/L64 NodeID/Locator64", lex: l} } s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19] u, err := strconv.ParseUint(s, 16, 64) if err != nil { return 0, &ParseError{file: l.token, err: "bad NID/L64 NodeID/Locator64", lex: l} } return u, nil } golang-github-miekg-dns-1.1.64/scan_rr.go000066400000000000000000001344441476742671700202230ustar00rootroot00000000000000package dns import ( "encoding/base64" "errors" "fmt" "net" "strconv" "strings" ) // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error func endingToString(c *zlexer, errstr string) (string, *ParseError) { var s strings.Builder l, _ := c.Next() // zString for l.value != zNewline && l.value != zEOF { if l.err { return s.String(), &ParseError{err: errstr, lex: l} } switch l.value { case zString: s.WriteString(l.token) case zBlank: // Ok default: return "", &ParseError{err: errstr, lex: l} } l, _ = c.Next() } return s.String(), nil } // A remainder of the rdata with embedded spaces, split on unquoted whitespace // and return the parsed string slice or an error func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) { // Get the remaining data until we see a zNewline l, _ := c.Next() if l.err { return nil, &ParseError{err: errstr, lex: l} } // Build the slice s := make([]string, 0) quote := false empty := false for l.value != zNewline && l.value != zEOF { if l.err { return nil, &ParseError{err: errstr, lex: l} } switch l.value { case zString: empty = false // split up tokens that are larger than 255 into 255-chunks sx := []string{} p := 0 for { i, ok := escapedStringOffset(l.token[p:], 255) if !ok { return nil, &ParseError{err: errstr, lex: l} } if i != -1 && p+i != len(l.token) { sx = append(sx, l.token[p:p+i]) } else { sx = append(sx, l.token[p:]) break } p += i } s = append(s, sx...) case zBlank: if quote { // zBlank can only be seen in between txt parts. return nil, &ParseError{err: errstr, lex: l} } case zQuote: if empty && quote { s = append(s, "") } quote = !quote empty = true default: return nil, &ParseError{err: errstr, lex: l} } l, _ = c.Next() } if quote { return nil, &ParseError{err: errstr, lex: l} } return s, nil } func (rr *A) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() rr.A = net.ParseIP(l.token) // IPv4 addresses cannot include ":". // We do this rather than use net.IP's To4() because // To4() treats IPv4-mapped IPv6 addresses as being // IPv4. isIPv4 := !strings.Contains(l.token, ":") if rr.A == nil || !isIPv4 || l.err { return &ParseError{err: "bad A A", lex: l} } return slurpRemainder(c) } func (rr *AAAA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() rr.AAAA = net.ParseIP(l.token) // IPv6 addresses must include ":", and IPv4 // addresses cannot include ":". isIPv6 := strings.Contains(l.token, ":") if rr.AAAA == nil || !isIPv6 || l.err { return &ParseError{err: "bad AAAA AAAA", lex: l} } return slurpRemainder(c) } func (rr *NS) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad NS Ns", lex: l} } rr.Ns = name return slurpRemainder(c) } func (rr *PTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad PTR Ptr", lex: l} } rr.Ptr = name return slurpRemainder(c) } func (rr *NSAPPTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad NSAP-PTR Ptr", lex: l} } rr.Ptr = name return slurpRemainder(c) } func (rr *RP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() mbox, mboxOk := toAbsoluteName(l.token, o) if l.err || !mboxOk { return &ParseError{err: "bad RP Mbox", lex: l} } rr.Mbox = mbox c.Next() // zBlank l, _ = c.Next() rr.Txt = l.token txt, txtOk := toAbsoluteName(l.token, o) if l.err || !txtOk { return &ParseError{err: "bad RP Txt", lex: l} } rr.Txt = txt return slurpRemainder(c) } func (rr *MR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad MR Mr", lex: l} } rr.Mr = name return slurpRemainder(c) } func (rr *MB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad MB Mb", lex: l} } rr.Mb = name return slurpRemainder(c) } func (rr *MG) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad MG Mg", lex: l} } rr.Mg = name return slurpRemainder(c) } func (rr *HINFO) parse(c *zlexer, o string) *ParseError { chunks, e := endingToTxtSlice(c, "bad HINFO Fields") if e != nil { return e } if ln := len(chunks); ln == 0 { return nil } else if ln == 1 { // Can we split it? if out := strings.Fields(chunks[0]); len(out) > 1 { chunks = out } else { chunks = append(chunks, "") } } rr.Cpu = chunks[0] rr.Os = strings.Join(chunks[1:], " ") return nil } // according to RFC 1183 the parsing is identical to HINFO, so just use that code. func (rr *ISDN) parse(c *zlexer, o string) *ParseError { chunks, e := endingToTxtSlice(c, "bad ISDN Fields") if e != nil { return e } if ln := len(chunks); ln == 0 { return nil } else if ln == 1 { // Can we split it? if out := strings.Fields(chunks[0]); len(out) > 1 { chunks = out } else { chunks = append(chunks, "") } } rr.Address = chunks[0] rr.SubAddress = strings.Join(chunks[1:], " ") return nil } func (rr *MINFO) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() rmail, rmailOk := toAbsoluteName(l.token, o) if l.err || !rmailOk { return &ParseError{err: "bad MINFO Rmail", lex: l} } rr.Rmail = rmail c.Next() // zBlank l, _ = c.Next() rr.Email = l.token email, emailOk := toAbsoluteName(l.token, o) if l.err || !emailOk { return &ParseError{err: "bad MINFO Email", lex: l} } rr.Email = email return slurpRemainder(c) } func (rr *MF) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad MF Mf", lex: l} } rr.Mf = name return slurpRemainder(c) } func (rr *MD) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad MD Md", lex: l} } rr.Md = name return slurpRemainder(c) } func (rr *MX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad MX Pref", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Mx = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad MX Mx", lex: l} } rr.Mx = name return slurpRemainder(c) } func (rr *RT) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil { return &ParseError{err: "bad RT Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Host = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad RT Host", lex: l} } rr.Host = name return slurpRemainder(c) } func (rr *AFSDB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad AFSDB Subtype", lex: l} } rr.Subtype = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Hostname = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad AFSDB Hostname", lex: l} } rr.Hostname = name return slurpRemainder(c) } func (rr *X25) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if l.err { return &ParseError{err: "bad X25 PSDNAddress", lex: l} } rr.PSDNAddress = l.token return slurpRemainder(c) } func (rr *KX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad KX Pref", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Exchanger = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad KX Exchanger", lex: l} } rr.Exchanger = name return slurpRemainder(c) } func (rr *CNAME) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad CNAME Target", lex: l} } rr.Target = name return slurpRemainder(c) } func (rr *DNAME) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad DNAME Target", lex: l} } rr.Target = name return slurpRemainder(c) } func (rr *SOA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() ns, nsOk := toAbsoluteName(l.token, o) if l.err || !nsOk { return &ParseError{err: "bad SOA Ns", lex: l} } rr.Ns = ns c.Next() // zBlank l, _ = c.Next() rr.Mbox = l.token mbox, mboxOk := toAbsoluteName(l.token, o) if l.err || !mboxOk { return &ParseError{err: "bad SOA Mbox", lex: l} } rr.Mbox = mbox c.Next() // zBlank var ( v uint32 ok bool ) for i := 0; i < 5; i++ { l, _ = c.Next() if l.err { return &ParseError{err: "bad SOA zone parameter", lex: l} } if j, err := strconv.ParseUint(l.token, 10, 32); err != nil { if i == 0 { // Serial must be a number return &ParseError{err: "bad SOA zone parameter", lex: l} } // We allow other fields to be unitful duration strings if v, ok = stringToTTL(l.token); !ok { return &ParseError{err: "bad SOA zone parameter", lex: l} } } else { v = uint32(j) } switch i { case 0: rr.Serial = v c.Next() // zBlank case 1: rr.Refresh = v c.Next() // zBlank case 2: rr.Retry = v c.Next() // zBlank case 3: rr.Expire = v c.Next() // zBlank case 4: rr.Minttl = v } } return slurpRemainder(c) } func (rr *SRV) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad SRV Priority", lex: l} } rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil || l.err { return &ParseError{err: "bad SRV Weight", lex: l} } rr.Weight = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e2 := strconv.ParseUint(l.token, 10, 16) if e2 != nil || l.err { return &ParseError{err: "bad SRV Port", lex: l} } rr.Port = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Target = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad SRV Target", lex: l} } rr.Target = name return slurpRemainder(c) } func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad NAPTR Order", lex: l} } rr.Order = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil || l.err { return &ParseError{err: "bad NAPTR Preference", lex: l} } rr.Preference = uint16(i) // Flags c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { return &ParseError{err: "bad NAPTR Flags", lex: l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Flags = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { return &ParseError{err: "bad NAPTR Flags", lex: l} } } else if l.value == zQuote { rr.Flags = "" } else { return &ParseError{err: "bad NAPTR Flags", lex: l} } // Service c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { return &ParseError{err: "bad NAPTR Service", lex: l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Service = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { return &ParseError{err: "bad NAPTR Service", lex: l} } } else if l.value == zQuote { rr.Service = "" } else { return &ParseError{err: "bad NAPTR Service", lex: l} } // Regexp c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { return &ParseError{err: "bad NAPTR Regexp", lex: l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Regexp = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { return &ParseError{err: "bad NAPTR Regexp", lex: l} } } else if l.value == zQuote { rr.Regexp = "" } else { return &ParseError{err: "bad NAPTR Regexp", lex: l} } // After quote no space?? c.Next() // zBlank l, _ = c.Next() // zString rr.Replacement = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad NAPTR Replacement", lex: l} } rr.Replacement = name return slurpRemainder(c) } func (rr *TALINK) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() previousName, previousNameOk := toAbsoluteName(l.token, o) if l.err || !previousNameOk { return &ParseError{err: "bad TALINK PreviousName", lex: l} } rr.PreviousName = previousName c.Next() // zBlank l, _ = c.Next() rr.NextName = l.token nextName, nextNameOk := toAbsoluteName(l.token, o) if l.err || !nextNameOk { return &ParseError{err: "bad TALINK NextName", lex: l} } rr.NextName = nextName return slurpRemainder(c) } func (rr *LOC) parse(c *zlexer, o string) *ParseError { // Non zero defaults for LOC record, see RFC 1876, Section 3. rr.Size = 0x12 // 1e2 cm (1m) rr.HorizPre = 0x16 // 1e6 cm (10000m) rr.VertPre = 0x13 // 1e3 cm (10m) ok := false // North l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err || i > 90 { return &ParseError{err: "bad LOC Latitude", lex: l} } rr.Latitude = 1000 * 60 * 60 * uint32(i) c.Next() // zBlank // Either number, 'N' or 'S' l, _ = c.Next() if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 { return &ParseError{err: "bad LOC Latitude minutes", lex: l} } else { rr.Latitude += 1000 * 60 * uint32(i) } c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { return &ParseError{err: "bad LOC Latitude seconds", lex: l} } else { rr.Latitude += uint32(1000 * i) } c.Next() // zBlank // Either number, 'N' or 'S' l, _ = c.Next() if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } // If still alive, flag an error return &ParseError{err: "bad LOC Latitude North/South", lex: l} East: // East c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 180 { return &ParseError{err: "bad LOC Longitude", lex: l} } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) } c.Next() // zBlank // Either number, 'E' or 'W' l, _ = c.Next() if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 { return &ParseError{err: "bad LOC Longitude minutes", lex: l} } else { rr.Longitude += 1000 * 60 * uint32(i) } c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { return &ParseError{err: "bad LOC Longitude seconds", lex: l} } else { rr.Longitude += uint32(1000 * i) } c.Next() // zBlank // Either number, 'E' or 'W' l, _ = c.Next() if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } // If still alive, flag an error return &ParseError{err: "bad LOC Longitude East/West", lex: l} Altitude: c.Next() // zBlank l, _ = c.Next() if l.token == "" || l.err { return &ParseError{err: "bad LOC Altitude", lex: l} } if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { l.token = l.token[0 : len(l.token)-1] } if i, err := strconv.ParseFloat(l.token, 64); err != nil { return &ParseError{err: "bad LOC Altitude", lex: l} } else { rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5) } // And now optionally the other values l, _ = c.Next() count := 0 for l.value != zNewline && l.value != zEOF { switch l.value { case zString: switch count { case 0: // Size exp, m, ok := stringToCm(l.token) if !ok { return &ParseError{err: "bad LOC Size", lex: l} } rr.Size = exp&0x0f | m<<4&0xf0 case 1: // HorizPre exp, m, ok := stringToCm(l.token) if !ok { return &ParseError{err: "bad LOC HorizPre", lex: l} } rr.HorizPre = exp&0x0f | m<<4&0xf0 case 2: // VertPre exp, m, ok := stringToCm(l.token) if !ok { return &ParseError{err: "bad LOC VertPre", lex: l} } rr.VertPre = exp&0x0f | m<<4&0xf0 } count++ case zBlank: // Ok default: return &ParseError{err: "bad LOC Size, HorizPre or VertPre", lex: l} } l, _ = c.Next() } return nil } func (rr *HIP) parse(c *zlexer, o string) *ParseError { // HitLength is not represented l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad HIP PublicKeyAlgorithm", lex: l} } rr.PublicKeyAlgorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString if l.token == "" || l.err { return &ParseError{err: "bad HIP Hit", lex: l} } rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.HitLength = uint8(len(rr.Hit)) / 2 c.Next() // zBlank l, _ = c.Next() // zString if l.token == "" || l.err { return &ParseError{err: "bad HIP PublicKey", lex: l} } rr.PublicKey = l.token // This cannot contain spaces decodedPK, decodedPKerr := base64.StdEncoding.DecodeString(rr.PublicKey) if decodedPKerr != nil { return &ParseError{err: "bad HIP PublicKey", lex: l} } rr.PublicKeyLength = uint16(len(decodedPK)) // RendezvousServers (if any) l, _ = c.Next() var xs []string for l.value != zNewline && l.value != zEOF { switch l.value { case zString: name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad HIP RendezvousServers", lex: l} } xs = append(xs, name) case zBlank: // Ok default: return &ParseError{err: "bad HIP RendezvousServers", lex: l} } l, _ = c.Next() } rr.RendezvousServers = xs return nil } func (rr *CERT) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if v, ok := StringToCertType[l.token]; ok { rr.Type = v } else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil { return &ParseError{err: "bad CERT Type", lex: l} } else { rr.Type = uint16(i) } c.Next() // zBlank l, _ = c.Next() // zString i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad CERT KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString if v, ok := StringToAlgorithm[l.token]; ok { rr.Algorithm = v } else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { return &ParseError{err: "bad CERT Algorithm", lex: l} } else { rr.Algorithm = uint8(i) } s, e1 := endingToString(c, "bad CERT Certificate") if e1 != nil { return e1 } rr.Certificate = s return nil } func (rr *OPENPGPKEY) parse(c *zlexer, o string) *ParseError { s, e := endingToString(c, "bad OPENPGPKEY PublicKey") if e != nil { return e } rr.PublicKey = s return nil } func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() j, e := strconv.ParseUint(l.token, 10, 32) if e != nil { // Serial must be a number return &ParseError{err: "bad CSYNC serial", lex: l} } rr.Serial = uint32(j) c.Next() // zBlank l, _ = c.Next() j, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil { // Serial must be a number return &ParseError{err: "bad CSYNC flags", lex: l} } rr.Flags = uint16(j) rr.TypeBitMap = make([]uint16, 0) var ( k uint16 ok bool ) l, _ = c.Next() for l.value != zNewline && l.value != zEOF { switch l.value { case zBlank: // Ok case zString: tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { return &ParseError{err: "bad CSYNC TypeBitMap", lex: l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: return &ParseError{err: "bad CSYNC TypeBitMap", lex: l} } l, _ = c.Next() } return nil } func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { return &ParseError{err: "bad ZONEMD Serial", lex: l} } rr.Serial = uint32(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad ZONEMD Scheme", lex: l} } rr.Scheme = uint8(i) c.Next() // zBlank l, _ = c.Next() i, err := strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return &ParseError{err: "bad ZONEMD Hash Algorithm", lex: l} } rr.Hash = uint8(i) s, e2 := endingToString(c, "bad ZONEMD Digest") if e2 != nil { return e2 } rr.Digest = s return nil } func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) } func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() tokenUpper := strings.ToUpper(l.token) if t, ok := StringToType[tokenUpper]; !ok { if strings.HasPrefix(tokenUpper, "TYPE") { t, ok = typeToInt(l.token) if !ok { return &ParseError{err: "bad RRSIG Typecovered", lex: l} } rr.TypeCovered = t } else { return &ParseError{err: "bad RRSIG Typecovered", lex: l} } } else { rr.TypeCovered = t } c.Next() // zBlank l, _ = c.Next() if l.err { return &ParseError{err: "bad RRSIG Algorithm", lex: l} } i, e := strconv.ParseUint(l.token, 10, 8) rr.Algorithm = uint8(i) // if 0 we'll check the mnemonic in the if if e != nil { v, ok := StringToAlgorithm[l.token] if !ok { return &ParseError{err: "bad RRSIG Algorithm", lex: l} } rr.Algorithm = v } c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad RRSIG Labels", lex: l} } rr.Labels = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 32) if e2 != nil || l.err { return &ParseError{err: "bad RRSIG OrigTtl", lex: l} } rr.OrigTtl = uint32(i) c.Next() // zBlank l, _ = c.Next() if i, err := StringToTime(l.token); err != nil { // Try to see if all numeric and use it as epoch if i, err := strconv.ParseUint(l.token, 10, 32); err == nil { rr.Expiration = uint32(i) } else { return &ParseError{err: "bad RRSIG Expiration", lex: l} } } else { rr.Expiration = i } c.Next() // zBlank l, _ = c.Next() if i, err := StringToTime(l.token); err != nil { if i, err := strconv.ParseUint(l.token, 10, 32); err == nil { rr.Inception = uint32(i) } else { return &ParseError{err: "bad RRSIG Inception", lex: l} } } else { rr.Inception = i } c.Next() // zBlank l, _ = c.Next() i, e3 := strconv.ParseUint(l.token, 10, 16) if e3 != nil || l.err { return &ParseError{err: "bad RRSIG KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() rr.SignerName = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad RRSIG SignerName", lex: l} } rr.SignerName = name s, e4 := endingToString(c, "bad RRSIG Signature") if e4 != nil { return e4 } rr.Signature = s return nil } func (rr *NXT) parse(c *zlexer, o string) *ParseError { return rr.NSEC.parse(c, o) } func (rr *NSEC) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad NSEC NextDomain", lex: l} } rr.NextDomain = name rr.TypeBitMap = make([]uint16, 0) var ( k uint16 ok bool ) l, _ = c.Next() for l.value != zNewline && l.value != zEOF { switch l.value { case zBlank: // Ok case zString: tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { return &ParseError{err: "bad NSEC TypeBitMap", lex: l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: return &ParseError{err: "bad NSEC TypeBitMap", lex: l} } l, _ = c.Next() } return nil } func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad NSEC3 Hash", lex: l} } rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad NSEC3 Flags", lex: l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 16) if e2 != nil || l.err { return &ParseError{err: "bad NSEC3 Iterations", lex: l} } rr.Iterations = uint16(i) c.Next() l, _ = c.Next() if l.token == "" || l.err { return &ParseError{err: "bad NSEC3 Salt", lex: l} } if l.token != "-" { rr.SaltLength = uint8(len(l.token)) / 2 rr.Salt = l.token } c.Next() l, _ = c.Next() if l.token == "" || l.err { return &ParseError{err: "bad NSEC3 NextDomain", lex: l} } rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) rr.NextDomain = l.token rr.TypeBitMap = make([]uint16, 0) var ( k uint16 ok bool ) l, _ = c.Next() for l.value != zNewline && l.value != zEOF { switch l.value { case zBlank: // Ok case zString: tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { return &ParseError{err: "bad NSEC3 TypeBitMap", lex: l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: return &ParseError{err: "bad NSEC3 TypeBitMap", lex: l} } l, _ = c.Next() } return nil } func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad NSEC3PARAM Hash", lex: l} } rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad NSEC3PARAM Flags", lex: l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 16) if e2 != nil || l.err { return &ParseError{err: "bad NSEC3PARAM Iterations", lex: l} } rr.Iterations = uint16(i) c.Next() l, _ = c.Next() if l.token != "-" { rr.SaltLength = uint8(len(l.token) / 2) rr.Salt = l.token } return slurpRemainder(c) } func (rr *EUI48) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if len(l.token) != 17 || l.err { return &ParseError{err: "bad EUI48 Address", lex: l} } addr := make([]byte, 12) dash := 0 for i := 0; i < 10; i += 2 { addr[i] = l.token[i+dash] addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { return &ParseError{err: "bad EUI48 Address", lex: l} } } addr[10] = l.token[15] addr[11] = l.token[16] i, e := strconv.ParseUint(string(addr), 16, 48) if e != nil { return &ParseError{err: "bad EUI48 Address", lex: l} } rr.Address = i return slurpRemainder(c) } func (rr *EUI64) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if len(l.token) != 23 || l.err { return &ParseError{err: "bad EUI64 Address", lex: l} } addr := make([]byte, 16) dash := 0 for i := 0; i < 14; i += 2 { addr[i] = l.token[i+dash] addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { return &ParseError{err: "bad EUI64 Address", lex: l} } } addr[14] = l.token[21] addr[15] = l.token[22] i, e := strconv.ParseUint(string(addr), 16, 64) if e != nil { return &ParseError{err: "bad EUI68 Address", lex: l} } rr.Address = i return slurpRemainder(c) } func (rr *SSHFP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad SSHFP Algorithm", lex: l} } rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad SSHFP Type", lex: l} } rr.Type = uint8(i) c.Next() // zBlank s, e2 := endingToString(c, "bad SSHFP Fingerprint") if e2 != nil { return e2 } rr.FingerPrint = s return nil } func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad " + typ + " Flags", lex: l} } rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad " + typ + " Protocol", lex: l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { return &ParseError{err: "bad " + typ + " Algorithm", lex: l} } rr.Algorithm = uint8(i) s, e3 := endingToString(c, "bad "+typ+" PublicKey") if e3 != nil { return e3 } rr.PublicKey = s return nil } func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "DNSKEY") } func (rr *KEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "KEY") } func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "CDNSKEY") } func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DS") } func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") } func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") } func (rr *IPSECKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() num, err := strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return &ParseError{err: "bad IPSECKEY value", lex: l} } rr.Precedence = uint8(num) c.Next() // zBlank l, _ = c.Next() num, err = strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return &ParseError{err: "bad IPSECKEY value", lex: l} } rr.GatewayType = uint8(num) c.Next() // zBlank l, _ = c.Next() num, err = strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return &ParseError{err: "bad IPSECKEY value", lex: l} } rr.Algorithm = uint8(num) c.Next() // zBlank l, _ = c.Next() if l.err { return &ParseError{err: "bad IPSECKEY gateway", lex: l} } rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType) if err != nil { return &ParseError{wrappedErr: fmt.Errorf("IPSECKEY %w", err), lex: l} } c.Next() // zBlank s, pErr := endingToString(c, "bad IPSECKEY PublicKey") if pErr != nil { return pErr } rr.PublicKey = s return slurpRemainder(c) } func (rr *AMTRELAY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() num, err := strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return &ParseError{err: "bad AMTRELAY value", lex: l} } rr.Precedence = uint8(num) c.Next() // zBlank l, _ = c.Next() if l.err || !(l.token == "0" || l.token == "1") { return &ParseError{err: "bad discovery value", lex: l} } if l.token == "1" { rr.GatewayType = 0x80 } c.Next() // zBlank l, _ = c.Next() num, err = strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return &ParseError{err: "bad AMTRELAY value", lex: l} } rr.GatewayType |= uint8(num) c.Next() // zBlank l, _ = c.Next() if l.err { return &ParseError{err: "bad AMTRELAY gateway", lex: l} } rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType&0x7f) if err != nil { return &ParseError{wrappedErr: fmt.Errorf("AMTRELAY %w", err), lex: l} } return slurpRemainder(c) } // same constants and parsing between IPSECKEY and AMTRELAY func parseAddrHostUnion(token, o string, gatewayType uint8) (addr net.IP, host string, err error) { switch gatewayType { case IPSECGatewayNone: if token != "." { return addr, host, errors.New("gateway type none with gateway set") } case IPSECGatewayIPv4, IPSECGatewayIPv6: addr = net.ParseIP(token) if addr == nil { return addr, host, errors.New("gateway IP invalid") } if (addr.To4() == nil) == (gatewayType == IPSECGatewayIPv4) { return addr, host, errors.New("gateway IP family mismatch") } case IPSECGatewayHost: var ok bool host, ok = toAbsoluteName(token, o) if !ok { return addr, host, errors.New("invalid gateway host") } } return addr, host, nil } func (rr *RKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad RKEY Flags", lex: l} } rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad RKEY Protocol", lex: l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { return &ParseError{err: "bad RKEY Algorithm", lex: l} } rr.Algorithm = uint8(i) s, e3 := endingToString(c, "bad RKEY PublicKey") if e3 != nil { return e3 } rr.PublicKey = s return nil } func (rr *EID) parse(c *zlexer, o string) *ParseError { s, e := endingToString(c, "bad EID Endpoint") if e != nil { return e } rr.Endpoint = s return nil } func (rr *NIMLOC) parse(c *zlexer, o string) *ParseError { s, e := endingToString(c, "bad NIMLOC Locator") if e != nil { return e } rr.Locator = s return nil } func (rr *GPOS) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() _, e := strconv.ParseFloat(l.token, 64) if e != nil || l.err { return &ParseError{err: "bad GPOS Longitude", lex: l} } rr.Longitude = l.token c.Next() // zBlank l, _ = c.Next() _, e1 := strconv.ParseFloat(l.token, 64) if e1 != nil || l.err { return &ParseError{err: "bad GPOS Latitude", lex: l} } rr.Latitude = l.token c.Next() // zBlank l, _ = c.Next() _, e2 := strconv.ParseFloat(l.token, 64) if e2 != nil || l.err { return &ParseError{err: "bad GPOS Altitude", lex: l} } rr.Altitude = l.token return slurpRemainder(c) } func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad " + typ + " KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { return &ParseError{err: "bad " + typ + " Algorithm", lex: l} } rr.Algorithm = i } else { rr.Algorithm = uint8(i) } c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad " + typ + " DigestType", lex: l} } rr.DigestType = uint8(i) s, e2 := endingToString(c, "bad "+typ+" Digest") if e2 != nil { return e2 } rr.Digest = s return nil } func (rr *TA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad TA KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { return &ParseError{err: "bad TA Algorithm", lex: l} } rr.Algorithm = i } else { rr.Algorithm = uint8(i) } c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad TA DigestType", lex: l} } rr.DigestType = uint8(i) s, e2 := endingToString(c, "bad TA Digest") if e2 != nil { return e2 } rr.Digest = s return nil } func (rr *TLSA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad TLSA Usage", lex: l} } rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad TLSA Selector", lex: l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { return &ParseError{err: "bad TLSA MatchingType", lex: l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t s, e3 := endingToString(c, "bad TLSA Certificate") if e3 != nil { return e3 } rr.Certificate = s return nil } func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad SMIMEA Usage", lex: l} } rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad SMIMEA Selector", lex: l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { return &ParseError{err: "bad SMIMEA MatchingType", lex: l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t s, e3 := endingToString(c, "bad SMIMEA Certificate") if e3 != nil { return e3 } rr.Certificate = s return nil } func (rr *RFC3597) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if l.token != "\\#" { return &ParseError{err: "bad RFC3597 Rdata", lex: l} } c.Next() // zBlank l, _ = c.Next() rdlength, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad RFC3597 Rdata ", lex: l} } s, e1 := endingToString(c, "bad RFC3597 Rdata") if e1 != nil { return e1 } if int(rdlength)*2 != len(s) { return &ParseError{err: "bad RFC3597 Rdata", lex: l} } rr.Rdata = s return nil } func (rr *SPF) parse(c *zlexer, o string) *ParseError { s, e := endingToTxtSlice(c, "bad SPF Txt") if e != nil { return e } rr.Txt = s return nil } func (rr *AVC) parse(c *zlexer, o string) *ParseError { s, e := endingToTxtSlice(c, "bad AVC Txt") if e != nil { return e } rr.Txt = s return nil } func (rr *TXT) parse(c *zlexer, o string) *ParseError { // no zBlank reading here, because all this rdata is TXT s, e := endingToTxtSlice(c, "bad TXT Txt") if e != nil { return e } rr.Txt = s return nil } // identical to setTXT func (rr *NINFO) parse(c *zlexer, o string) *ParseError { s, e := endingToTxtSlice(c, "bad NINFO ZSData") if e != nil { return e } rr.ZSData = s return nil } // Uses the same format as TXT func (rr *RESINFO) parse(c *zlexer, o string) *ParseError { s, e := endingToTxtSlice(c, "bad RESINFO Resinfo") if e != nil { return e } rr.Txt = s return nil } func (rr *URI) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad URI Priority", lex: l} } rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil || l.err { return &ParseError{err: "bad URI Weight", lex: l} } rr.Weight = uint16(i) c.Next() // zBlank s, e2 := endingToTxtSlice(c, "bad URI Target") if e2 != nil { return e2 } if len(s) != 1 { return &ParseError{err: "bad URI Target", lex: l} } rr.Target = s[0] return nil } func (rr *DHCID) parse(c *zlexer, o string) *ParseError { // awesome record to parse! s, e := endingToString(c, "bad DHCID Digest") if e != nil { return e } rr.Digest = s return nil } func (rr *NID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad NID Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString u, e1 := stringToNodeID(l) if e1 != nil || l.err { return e1 } rr.NodeID = u return slurpRemainder(c) } func (rr *L32) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad L32 Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Locator32 = net.ParseIP(l.token) if rr.Locator32 == nil || l.err { return &ParseError{err: "bad L32 Locator", lex: l} } return slurpRemainder(c) } func (rr *LP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad LP Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Fqdn = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{err: "bad LP Fqdn", lex: l} } rr.Fqdn = name return slurpRemainder(c) } func (rr *L64) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad L64 Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString u, e1 := stringToNodeID(l) if e1 != nil || l.err { return e1 } rr.Locator64 = u return slurpRemainder(c) } func (rr *UID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { return &ParseError{err: "bad UID Uid", lex: l} } rr.Uid = uint32(i) return slurpRemainder(c) } func (rr *GID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { return &ParseError{err: "bad GID Gid", lex: l} } rr.Gid = uint32(i) return slurpRemainder(c) } func (rr *UINFO) parse(c *zlexer, o string) *ParseError { s, e := endingToTxtSlice(c, "bad UINFO Uinfo") if e != nil { return e } if ln := len(s); ln == 0 { return nil } rr.Uinfo = s[0] // silently discard anything after the first character-string return nil } func (rr *PX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{err: "bad PX Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Map822 = l.token map822, map822Ok := toAbsoluteName(l.token, o) if l.err || !map822Ok { return &ParseError{err: "bad PX Map822", lex: l} } rr.Map822 = map822 c.Next() // zBlank l, _ = c.Next() // zString rr.Mapx400 = l.token mapx400, mapx400Ok := toAbsoluteName(l.token, o) if l.err || !mapx400Ok { return &ParseError{err: "bad PX Mapx400", lex: l} } rr.Mapx400 = mapx400 return slurpRemainder(c) } func (rr *CAA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad CAA Flag", lex: l} } rr.Flag = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString if l.value != zString { return &ParseError{err: "bad CAA Tag", lex: l} } rr.Tag = l.token c.Next() // zBlank s, e1 := endingToTxtSlice(c, "bad CAA Value") if e1 != nil { return e1 } if len(s) != 1 { return &ParseError{err: "bad CAA Value", lex: l} } rr.Value = s[0] return nil } func (rr *TKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() // Algorithm if l.value != zString { return &ParseError{err: "bad TKEY algorithm", lex: l} } rr.Algorithm = l.token c.Next() // zBlank // Get the key length and key values l, _ = c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return &ParseError{err: "bad TKEY key length", lex: l} } rr.KeySize = uint16(i) c.Next() // zBlank l, _ = c.Next() if l.value != zString { return &ParseError{err: "bad TKEY key", lex: l} } rr.Key = l.token c.Next() // zBlank // Get the otherdata length and string data l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { return &ParseError{err: "bad TKEY otherdata length", lex: l} } rr.OtherLen = uint16(i) c.Next() // zBlank l, _ = c.Next() if l.value != zString { return &ParseError{err: "bad TKEY otherday", lex: l} } rr.OtherData = l.token return nil } func (rr *APL) parse(c *zlexer, o string) *ParseError { var prefixes []APLPrefix for { l, _ := c.Next() if l.value == zNewline || l.value == zEOF { break } if l.value == zBlank && prefixes != nil { continue } if l.value != zString { return &ParseError{err: "unexpected APL field", lex: l} } // Expected format: [!]afi:address/prefix colon := strings.IndexByte(l.token, ':') if colon == -1 { return &ParseError{err: "missing colon in APL field", lex: l} } family, cidr := l.token[:colon], l.token[colon+1:] var negation bool if family != "" && family[0] == '!' { negation = true family = family[1:] } afi, e := strconv.ParseUint(family, 10, 16) if e != nil { return &ParseError{wrappedErr: fmt.Errorf("failed to parse APL family: %w", e), lex: l} } var addrLen int switch afi { case 1: addrLen = net.IPv4len case 2: addrLen = net.IPv6len default: return &ParseError{err: "unrecognized APL family", lex: l} } ip, subnet, e1 := net.ParseCIDR(cidr) if e1 != nil { return &ParseError{wrappedErr: fmt.Errorf("failed to parse APL address: %w", e1), lex: l} } if !ip.Equal(subnet.IP) { return &ParseError{err: "extra bits in APL address", lex: l} } if len(subnet.IP) != addrLen { return &ParseError{err: "address mismatch with the APL family", lex: l} } prefixes = append(prefixes, APLPrefix{ Negation: negation, Network: *subnet, }) } rr.Prefixes = prefixes return nil } // escapedStringOffset finds the offset within a string (which may contain escape // sequences) that corresponds to a certain byte offset. If the input offset is // out of bounds, -1 is returned (which is *not* considered an error). func escapedStringOffset(s string, desiredByteOffset int) (int, bool) { if desiredByteOffset == 0 { return 0, true } currentByteOffset, i := 0, 0 for i < len(s) { currentByteOffset += 1 // Skip escape sequences if s[i] != '\\' { // Single plain byte, not an escape sequence. i++ } else if isDDD(s[i+1:]) { // Skip backslash and DDD. i += 4 } else if len(s[i+1:]) < 1 { // No character following the backslash; that's an error. return 0, false } else { // Skip backslash and following byte. i += 2 } if currentByteOffset >= desiredByteOffset { return i, true } } return -1, true } golang-github-miekg-dns-1.1.64/scan_test.go000066400000000000000000000305341476742671700205520ustar00rootroot00000000000000package dns import ( "errors" "io" "io/fs" "net" "os" "strings" "testing" "testing/fstest" ) func TestZoneParserGenerate(t *testing.T) { zone := "$ORIGIN example.org.\n$GENERATE 10-12 foo${2,3,d} IN A 127.0.0.$" wantRRs := []RR{ &A{Hdr: RR_Header{Name: "foo012.example.org."}, A: net.ParseIP("127.0.0.10")}, &A{Hdr: RR_Header{Name: "foo013.example.org."}, A: net.ParseIP("127.0.0.11")}, &A{Hdr: RR_Header{Name: "foo014.example.org."}, A: net.ParseIP("127.0.0.12")}, } wantIdx := 0 z := NewZoneParser(strings.NewReader(zone), "", "") for rr, ok := z.Next(); ok; rr, ok = z.Next() { if wantIdx >= len(wantRRs) { t.Fatalf("expected %d RRs, but got more", len(wantRRs)) } if got, want := rr.Header().Name, wantRRs[wantIdx].Header().Name; got != want { t.Fatalf("expected name %s, but got %s", want, got) } a, okA := rr.(*A) if !okA { t.Fatalf("expected *A RR, but got %T", rr) } if got, want := a.A, wantRRs[wantIdx].(*A).A; !got.Equal(want) { t.Fatalf("expected A with IP %v, but got %v", got, want) } wantIdx++ } if err := z.Err(); err != nil { t.Fatalf("expected no error, but got %s", err) } if wantIdx != len(wantRRs) { t.Errorf("too few records, expected %d, got %d", len(wantRRs), wantIdx) } } func TestZoneParserInclude(t *testing.T) { tmpfile, err := os.CreateTemp("", "dns") if err != nil { t.Fatalf("could not create tmpfile for test: %s", err) } defer os.Remove(tmpfile.Name()) if _, err := tmpfile.WriteString("foo\tIN\tA\t127.0.0.1"); err != nil { t.Fatalf("unable to write content to tmpfile %q: %s", tmpfile.Name(), err) } if err := tmpfile.Close(); err != nil { t.Fatalf("could not close tmpfile %q: %s", tmpfile.Name(), err) } zone := "$ORIGIN example.org.\n$INCLUDE " + tmpfile.Name() + "\nbar\tIN\tA\t127.0.0.2" var got int z := NewZoneParser(strings.NewReader(zone), "", "") z.SetIncludeAllowed(true) for rr, ok := z.Next(); ok; _, ok = z.Next() { switch rr.Header().Name { case "foo.example.org.", "bar.example.org.": default: t.Fatalf("expected foo.example.org. or bar.example.org., but got %s", rr.Header().Name) } got++ } if err := z.Err(); err != nil { t.Fatalf("expected no error, but got %s", err) } if expected := 2; got != expected { t.Errorf("failed to parse zone after include, expected %d records, got %d", expected, got) } os.Remove(tmpfile.Name()) z = NewZoneParser(strings.NewReader(zone), "", "") z.SetIncludeAllowed(true) z.Next() if err := z.Err(); err == nil || !strings.Contains(err.Error(), "failed to open") || !strings.Contains(err.Error(), tmpfile.Name()) || !strings.Contains(err.Error(), "no such file or directory") { t.Fatalf(`expected error to contain: "failed to open", %q and "no such file or directory" but got: %s`, tmpfile.Name(), err) } } func TestZoneParserIncludeFS(t *testing.T) { fsys := fstest.MapFS{ "db.foo": &fstest.MapFile{ Data: []byte("foo\tIN\tA\t127.0.0.1"), }, } zone := "$ORIGIN example.org.\n$INCLUDE db.foo\nbar\tIN\tA\t127.0.0.2" var got int z := NewZoneParser(strings.NewReader(zone), "", "") z.SetIncludeAllowed(true) z.SetIncludeFS(fsys) for rr, ok := z.Next(); ok; _, ok = z.Next() { switch rr.Header().Name { case "foo.example.org.", "bar.example.org.": default: t.Fatalf("expected foo.example.org. or bar.example.org., but got %s", rr.Header().Name) } got++ } if err := z.Err(); err != nil { t.Fatalf("expected no error, but got %s", err) } if expected := 2; got != expected { t.Errorf("failed to parse zone after include, expected %d records, got %d", expected, got) } fsys = fstest.MapFS{} z = NewZoneParser(strings.NewReader(zone), "", "") z.SetIncludeAllowed(true) z.SetIncludeFS(fsys) z.Next() if err := z.Err(); !errors.Is(err, fs.ErrNotExist) { t.Fatalf(`expected fs.ErrNotExist but got: %T %v`, err, err) } } func TestZoneParserIncludeFSPaths(t *testing.T) { fsys := fstest.MapFS{ "baz/bat/db.foo": &fstest.MapFile{ Data: []byte("foo\tIN\tA\t127.0.0.1"), }, } for _, p := range []string{ "../bat/db.foo", "/baz/bat/db.foo", } { zone := "$ORIGIN example.org.\n$INCLUDE " + p + "\nbar\tIN\tA\t127.0.0.2" var got int z := NewZoneParser(strings.NewReader(zone), "", "baz/quux/db.bar") z.SetIncludeAllowed(true) z.SetIncludeFS(fsys) for rr, ok := z.Next(); ok; _, ok = z.Next() { switch rr.Header().Name { case "foo.example.org.", "bar.example.org.": default: t.Fatalf("$INCLUDE %q: expected foo.example.org. or bar.example.org., but got %s", p, rr.Header().Name) } got++ } if err := z.Err(); err != nil { t.Fatalf("$INCLUDE %q: expected no error, but got %s", p, err) } if expected := 2; got != expected { t.Errorf("$INCLUDE %q: failed to parse zone after include, expected %d records, got %d", p, expected, got) } } } func TestZoneParserIncludeDisallowed(t *testing.T) { tmpfile, err := os.CreateTemp("", "dns") if err != nil { t.Fatalf("could not create tmpfile for test: %s", err) } defer os.Remove(tmpfile.Name()) if _, err := tmpfile.WriteString("foo\tIN\tA\t127.0.0.1"); err != nil { t.Fatalf("unable to write content to tmpfile %q: %s", tmpfile.Name(), err) } if err := tmpfile.Close(); err != nil { t.Fatalf("could not close tmpfile %q: %s", tmpfile.Name(), err) } zp := NewZoneParser(strings.NewReader("$INCLUDE "+tmpfile.Name()), "example.org.", "") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } const expect = "$INCLUDE directive not allowed" if err := zp.Err(); err == nil || !strings.Contains(err.Error(), expect) { t.Errorf("expected error to contain %q, got %v", expect, err) } } func TestZoneParserAddressAAAA(t *testing.T) { tests := []struct { record string want *AAAA }{ { record: "1.example.org. 600 IN AAAA ::1", want: &AAAA{Hdr: RR_Header{Name: "1.example.org."}, AAAA: net.IPv6loopback}, }, { record: "2.example.org. 600 IN AAAA ::FFFF:127.0.0.1", want: &AAAA{Hdr: RR_Header{Name: "2.example.org."}, AAAA: net.ParseIP("::FFFF:127.0.0.1")}, }, } for _, tc := range tests { got, err := NewRR(tc.record) if err != nil { t.Fatalf("expected no error, but got %s", err) } aaaa, ok := got.(*AAAA) if !ok { t.Fatalf("expected *AAAA RR, but got %T", got) } if !aaaa.AAAA.Equal(tc.want.AAAA) { t.Fatalf("expected AAAA with IP %v, but got %v", tc.want.AAAA, aaaa.AAAA) } } } func TestZoneParserAddressBad(t *testing.T) { records := []string{ "1.bad.example.org. 600 IN A ::1", "2.bad.example.org. 600 IN A ::FFFF:127.0.0.1", "3.bad.example.org. 600 IN AAAA 127.0.0.1", } for _, record := range records { const expect = "bad A" if got, err := NewRR(record); err == nil || !strings.Contains(err.Error(), expect) { t.Errorf("NewRR(%v) = %v, want err to contain %q", record, got, expect) } } } func TestParseTA(t *testing.T) { rr, err := NewRR(` Ta 0 0 0`) if err != nil { t.Fatalf("expected no error, but got %s", err) } if rr == nil { t.Fatal(`expected a normal RR, but got nil`) } } var errTestReadError = &Error{"test error"} type errReader struct{} func (errReader) Read(p []byte) (int, error) { return 0, errTestReadError } func TestParseZoneReadError(t *testing.T) { rr, err := ReadRR(errReader{}, "") if err == nil || !strings.Contains(err.Error(), errTestReadError.Error()) { t.Errorf("expected error to contain %q, but got %v", errTestReadError, err) } if rr != nil { t.Errorf("expected a nil RR, but got %v", rr) } } func TestUnexpectedNewline(t *testing.T) { zone := ` example.com. 60 PX 1000 TXT 1K ` zp := NewZoneParser(strings.NewReader(zone), "example.com.", "") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } const expect = `dns: unexpected newline: "\n" at line: 2:18` if err := zp.Err(); err == nil || err.Error() != expect { t.Errorf("expected error to contain %q, got %v", expect, err) } // Test that newlines inside braces still work. zone = ` example.com. 60 PX ( 1000 TXT 1K ) ` zp = NewZoneParser(strings.NewReader(zone), "example.com.", "") var count int for _, ok := zp.Next(); ok; _, ok = zp.Next() { count++ } if count != 1 { t.Errorf("expected 1 record, got %d", count) } if err := zp.Err(); err != nil { t.Errorf("unexpected error: %v", err) } } func TestParseRFC3597InvalidLength(t *testing.T) { // We need to space separate the 00s otherwise it will exceed the maximum token size // of the zone lexer. _, err := NewRR("example. 3600 CLASS1 TYPE1 \\# 65536 " + strings.Repeat("00 ", 65536)) if err == nil { t.Error("should not have parsed excessively long RFC3579 record") } } func TestParseKnownRRAsRFC3597(t *testing.T) { t.Run("with RDATA", func(t *testing.T) { // This was found by oss-fuzz. _, err := NewRR("example. 3600 tYpe44 \\# 03 75 0100") if err != nil { t.Errorf("failed to parse RFC3579 format: %v", err) } rr, err := NewRR("example. 3600 CLASS1 TYPE1 \\# 4 7f000001") if err != nil { t.Fatalf("failed to parse RFC3579 format: %v", err) } if rr.Header().Rrtype != TypeA { t.Errorf("expected TypeA (1) Rrtype, but got %v", rr.Header().Rrtype) } a, ok := rr.(*A) if !ok { t.Fatalf("expected *A RR, but got %T", rr) } localhost := net.IPv4(127, 0, 0, 1) if !a.A.Equal(localhost) { t.Errorf("expected A with IP %v, but got %v", localhost, a.A) } }) t.Run("without RDATA", func(t *testing.T) { rr, err := NewRR("example. 3600 CLASS1 TYPE1 \\# 0") if err != nil { t.Fatalf("failed to parse RFC3579 format: %v", err) } if rr.Header().Rrtype != TypeA { t.Errorf("expected TypeA (1) Rrtype, but got %v", rr.Header().Rrtype) } a, ok := rr.(*A) if !ok { t.Fatalf("expected *A RR, but got %T", rr) } if len(a.A) != 0 { t.Errorf("expected A with empty IP, but got %v", a.A) } }) } func TestParseOpenEscape(t *testing.T) { if _, err := NewRR("example.net IN CNAME example.net."); err != nil { t.Fatalf("expected no error, but got: %s", err) } if _, err := NewRR("example.net IN CNAME example.org\\"); err == nil { t.Fatalf("expected an error, but got none") } } func BenchmarkNewRR(b *testing.B) { const name1 = "12345678901234567890123456789012345.12345678.123." const s = name1 + " 3600 IN MX 10 " + name1 for n := 0; n < b.N; n++ { _, err := NewRR(s) if err != nil { b.Fatal(err) } } } func BenchmarkReadRR(b *testing.B) { const name1 = "12345678901234567890123456789012345.12345678.123." const s = name1 + " 3600 IN MX 10 " + name1 + "\n" for n := 0; n < b.N; n++ { r := struct{ io.Reader }{strings.NewReader(s)} // r is now only an io.Reader and won't benefit from the // io.ByteReader special-case in zlexer.Next. _, err := ReadRR(r, "") if err != nil { b.Fatal(err) } } } const benchZone = ` foo. IN A 10.0.0.1 ; this is comment 1 foo. IN A ( 10.0.0.2 ; this is comment 2 ) ; this is comment 3 foo. IN A 10.0.0.3 foo. IN A ( 10.0.0.4 ); this is comment 4 foo. IN A 10.0.0.5 ; this is comment 5 foo. IN A 10.0.0.6 foo. IN DNSKEY 256 3 5 AwEAAb+8l ; this is comment 6 foo. IN NSEC miek.nl. TXT RRSIG NSEC; this is comment 7 foo. IN TXT "THIS IS TEXT MAN"; this is comment 8 ` func BenchmarkZoneParser(b *testing.B) { for n := 0; n < b.N; n++ { zp := NewZoneParser(strings.NewReader(benchZone), "example.org.", "") for _, ok := zp.Next(); ok; _, ok = zp.Next() { } if err := zp.Err(); err != nil { b.Fatal(err) } } } func TestEscapedStringOffset(t *testing.T) { var cases = []struct { input string inputOffset int expectedOffset int expectedOK bool }{ {"simple string with no escape sequences", 20, 20, true}, {"simple string with no escape sequences", 500, -1, true}, {`\;\088\\\;\120\\`, 0, 0, true}, {`\;\088\\\;\120\\`, 1, 2, true}, {`\;\088\\\;\120\\`, 2, 6, true}, {`\;\088\\\;\120\\`, 3, 8, true}, {`\;\088\\\;\120\\`, 4, 10, true}, {`\;\088\\\;\120\\`, 5, 14, true}, {`\;\088\\\;\120\\`, 6, 16, true}, {`\;\088\\\;\120\\`, 7, -1, true}, {`\`, 3, 0, false}, {`a\`, 3, 0, false}, {`aa\`, 3, 0, false}, {`aaa\`, 3, 3, true}, {`aaaa\`, 3, 3, true}, } for i, test := range cases { outputOffset, outputOK := escapedStringOffset(test.input, test.inputOffset) if outputOffset != test.expectedOffset { t.Errorf( "Test %d (input %#q offset %d) returned offset %d but expected %d", i, test.input, test.inputOffset, outputOffset, test.expectedOffset, ) } if outputOK != test.expectedOK { t.Errorf( "Test %d (input %#q offset %d) returned ok=%t but expected %t", i, test.input, test.inputOffset, outputOK, test.expectedOK, ) } } } golang-github-miekg-dns-1.1.64/serve_mux.go000066400000000000000000000065711476742671700206100ustar00rootroot00000000000000package dns import ( "sync" ) // ServeMux is an DNS request multiplexer. It matches the zone name of // each incoming request against a list of registered patterns add calls // the handler for the pattern that most closely matches the zone name. // // ServeMux is DNSSEC aware, meaning that queries for the DS record are // redirected to the parent zone (if that is also registered), otherwise // the child gets the query. // // ServeMux is also safe for concurrent access from multiple goroutines. // // The zero ServeMux is empty and ready for use. type ServeMux struct { z map[string]Handler m sync.RWMutex } // NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return new(ServeMux) } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = NewServeMux() func (mux *ServeMux) match(q string, t uint16) Handler { mux.m.RLock() defer mux.m.RUnlock() if mux.z == nil { return nil } q = CanonicalName(q) var handler Handler for off, end := 0, false; !end; off, end = NextLabel(q, off) { if h, ok := mux.z[q[off:]]; ok { if t != TypeDS { return h } // Continue for DS to see if we have a parent too, if so delegate to the parent handler = h } } // Wildcard match, if we have found nothing try the root zone as a last resort. if h, ok := mux.z["."]; ok { return h } return handler } // Handle adds a handler to the ServeMux for pattern. func (mux *ServeMux) Handle(pattern string, handler Handler) { if pattern == "" { panic("dns: invalid pattern " + pattern) } mux.m.Lock() if mux.z == nil { mux.z = make(map[string]Handler) } mux.z[CanonicalName(pattern)] = handler mux.m.Unlock() } // HandleFunc adds a handler function to the ServeMux for pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { mux.Handle(pattern, HandlerFunc(handler)) } // HandleRemove deregisters the handler specific for pattern from the ServeMux. func (mux *ServeMux) HandleRemove(pattern string) { if pattern == "" { panic("dns: invalid pattern " + pattern) } mux.m.Lock() delete(mux.z, CanonicalName(pattern)) mux.m.Unlock() } // ServeDNS dispatches the request to the handler whose pattern most // closely matches the request message. // // ServeDNS is DNSSEC aware, meaning that queries for the DS record // are redirected to the parent zone (if that is also registered), // otherwise the child gets the query. // // If no handler is found, or there is no question, a standard REFUSED // message is returned func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) { var h Handler if len(req.Question) >= 1 { // allow more than one question h = mux.match(req.Question[0].Name, req.Question[0].Qtype) } if h != nil { h.ServeDNS(w, req) } else { handleRefused(w, req) } } // Handle registers the handler with the given pattern // in the DefaultServeMux. The documentation for // ServeMux explains how patterns are matched. func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } // HandleRemove deregisters the handle with the given pattern // in the DefaultServeMux. func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) } // HandleFunc registers the handler function with the given pattern // in the DefaultServeMux. func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { DefaultServeMux.HandleFunc(pattern, handler) } golang-github-miekg-dns-1.1.64/serve_mux_test.go000066400000000000000000000032031476742671700216340ustar00rootroot00000000000000package dns import "testing" func TestDotAsCatchAllWildcard(t *testing.T) { mux := NewServeMux() mux.Handle(".", HandlerFunc(HelloServer)) mux.Handle("example.com.", HandlerFunc(AnotherHelloServer)) handler := mux.match("www.miek.nl.", TypeTXT) if handler == nil { t.Error("wildcard match failed") } handler = mux.match("www.example.com.", TypeTXT) if handler == nil { t.Error("example.com match failed") } handler = mux.match("a.www.example.com.", TypeTXT) if handler == nil { t.Error("a.www.example.com match failed") } handler = mux.match("boe.", TypeTXT) if handler == nil { t.Error("boe. match failed") } } func TestCaseFolding(t *testing.T) { mux := NewServeMux() mux.Handle("_udp.example.com.", HandlerFunc(HelloServer)) handler := mux.match("_dns._udp.example.com.", TypeSRV) if handler == nil { t.Error("case sensitive characters folded") } handler = mux.match("_DNS._UDP.EXAMPLE.COM.", TypeSRV) if handler == nil { t.Error("case insensitive characters not folded") } } func TestRootServer(t *testing.T) { mux := NewServeMux() mux.Handle(".", HandlerFunc(HelloServer)) handler := mux.match(".", TypeNS) if handler == nil { t.Error("root match failed") } } func BenchmarkMuxMatch(b *testing.B) { mux := NewServeMux() mux.Handle("_udp.example.com.", HandlerFunc(HelloServer)) bench := func(q string) func(*testing.B) { return func(b *testing.B) { for n := 0; n < b.N; n++ { handler := mux.match(q, TypeSRV) if handler == nil { b.Fatal("couldn't find match") } } } } b.Run("lowercase", bench("_dns._udp.example.com.")) b.Run("uppercase", bench("_DNS._UDP.EXAMPLE.COM.")) } golang-github-miekg-dns-1.1.64/server.go000066400000000000000000000555301476742671700201000ustar00rootroot00000000000000// DNS server implementation. package dns import ( "context" "crypto/tls" "encoding/binary" "errors" "io" "net" "strings" "sync" "time" ) // Default maximum number of TCP queries before we close the socket. const maxTCPQueries = 128 // aLongTimeAgo is a non-zero time, far in the past, used for // immediate cancellation of network operations. var aLongTimeAgo = time.Unix(1, 0) // Handler is implemented by any value that implements ServeDNS. type Handler interface { ServeDNS(w ResponseWriter, r *Msg) } // The HandlerFunc type is an adapter to allow the use of // ordinary functions as DNS handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Msg) // ServeDNS calls f(w, r). func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) { f(w, r) } // A ResponseWriter interface is used by an DNS handler to // construct an DNS response. type ResponseWriter interface { // LocalAddr returns the net.Addr of the server LocalAddr() net.Addr // RemoteAddr returns the net.Addr of the client that sent the current request. RemoteAddr() net.Addr // WriteMsg writes a reply back to the client. WriteMsg(*Msg) error // Write writes a raw buffer back to the client. Write([]byte) (int, error) // Close closes the connection. Close() error // TsigStatus returns the status of the Tsig. TsigStatus() error // TsigTimersOnly sets the tsig timers only boolean. TsigTimersOnly(bool) // Hijack lets the caller take over the connection. // After a call to Hijack(), the DNS package will not do anything with the connection. Hijack() } // A ConnectionStater interface is used by a DNS Handler to access TLS connection state // when available. type ConnectionStater interface { ConnectionState() *tls.ConnectionState } type response struct { closed bool // connection has been closed hijacked bool // connection has been hijacked by handler tsigTimersOnly bool tsigStatus error tsigRequestMAC string tsigProvider TsigProvider udp net.PacketConn // i/o connection if UDP was used tcp net.Conn // i/o connection if TCP was used udpSession *SessionUDP // oob data to get egress interface right pcSession net.Addr // address to use when writing to a generic net.PacketConn writer Writer // writer to output the raw DNS bits } // handleRefused returns a HandlerFunc that returns REFUSED for every request it gets. func handleRefused(w ResponseWriter, r *Msg) { m := new(Msg) m.SetRcode(r, RcodeRefused) w.WriteMsg(m) } // HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets. // Deprecated: This function is going away. func HandleFailed(w ResponseWriter, r *Msg) { m := new(Msg) m.SetRcode(r, RcodeServerFailure) // does not matter if this write fails w.WriteMsg(m) } // ListenAndServe Starts a server on address and network specified Invoke handler // for incoming queries. func ListenAndServe(addr string, network string, handler Handler) error { server := &Server{Addr: addr, Net: network, Handler: handler} return server.ListenAndServe() } // ListenAndServeTLS acts like http.ListenAndServeTLS, more information in // http://golang.org/pkg/net/http/#ListenAndServeTLS func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return err } config := tls.Config{ Certificates: []tls.Certificate{cert}, } server := &Server{ Addr: addr, Net: "tcp-tls", TLSConfig: &config, Handler: handler, } return server.ListenAndServe() } // ActivateAndServe activates a server with a listener from systemd, // l and p should not both be non-nil. // If both l and p are not nil only p will be used. // Invoke handler for incoming queries. func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error { server := &Server{Listener: l, PacketConn: p, Handler: handler} return server.ActivateAndServe() } // Writer writes raw DNS messages; each call to Write should send an entire message. type Writer interface { io.Writer } // Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message. type Reader interface { // ReadTCP reads a raw message from a TCP connection. Implementations may alter // connection properties, for example the read-deadline. ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) // ReadUDP reads a raw message from a UDP connection. Implementations may alter // connection properties, for example the read-deadline. ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) } // PacketConnReader is an optional interface that Readers can implement to support using generic net.PacketConns. type PacketConnReader interface { Reader // ReadPacketConn reads a raw message from a generic net.PacketConn UDP connection. Implementations may // alter connection properties, for example the read-deadline. ReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) } // defaultReader is an adapter for the Server struct that implements the Reader and // PacketConnReader interfaces using the readTCP, readUDP and readPacketConn funcs // of the embedded Server. type defaultReader struct { *Server } var _ PacketConnReader = defaultReader{} func (dr defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { return dr.readTCP(conn, timeout) } func (dr defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { return dr.readUDP(conn, timeout) } func (dr defaultReader) ReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) { return dr.readPacketConn(conn, timeout) } // DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader. // Implementations should never return a nil Reader. // Readers should also implement the optional PacketConnReader interface. // PacketConnReader is required to use a generic net.PacketConn. type DecorateReader func(Reader) Reader // DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer. // Implementations should never return a nil Writer. type DecorateWriter func(Writer) Writer // MsgInvalidFunc is a listener hook for observing incoming messages that were discarded // because they could not be parsed. // Every message that is read by a Reader will eventually be provided to the Handler, // rejected (or ignored) by the MsgAcceptFunc, or passed to this function. type MsgInvalidFunc func(m []byte, err error) func DefaultMsgInvalidFunc(m []byte, err error) {} // A Server defines parameters for running an DNS server. type Server struct { // Address to listen on, ":dns" if empty. Addr string // if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one Net string // TCP Listener to use, this is to aid in systemd's socket activation. Listener net.Listener // TLS connection configuration TLSConfig *tls.Config // UDP "Listener" to use, this is to aid in systemd's socket activation. PacketConn net.PacketConn // Handler to invoke, dns.DefaultServeMux if nil. Handler Handler // Default buffer size to use to read incoming UDP messages. If not set // it defaults to MinMsgSize (512 B). UDPSize int // The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second. ReadTimeout time.Duration // The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second. WriteTimeout time.Duration // TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966). IdleTimeout func() time.Duration // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. TsigProvider TsigProvider // Secret(s) for Tsig map[]. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2). TsigSecret map[string]string // If NotifyStartedFunc is set it is called once the server has started listening. NotifyStartedFunc func() // DecorateReader is optional, allows customization of the process that reads raw DNS messages. // The decorated reader must not mutate the data read from the conn. DecorateReader DecorateReader // DecorateWriter is optional, allows customization of the process that writes raw DNS messages. DecorateWriter DecorateWriter // Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1). MaxTCPQueries int // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address. // It is only supported on certain GOOSes and when using ListenAndServe. ReusePort bool // Whether to set the SO_REUSEADDR socket option, allowing multiple listeners to be bound to a single address. // Crucially this allows binding when an existing server is listening on `0.0.0.0` or `::`. // It is only supported on certain GOOSes and when using ListenAndServe. ReuseAddr bool // AcceptMsgFunc will check the incoming message and will reject it early in the process. // By default DefaultMsgAcceptFunc will be used. MsgAcceptFunc MsgAcceptFunc // MsgInvalidFunc is optional, will be called if a message is received but cannot be parsed. MsgInvalidFunc MsgInvalidFunc // Shutdown handling lock sync.RWMutex started bool shutdown chan struct{} conns map[net.Conn]struct{} // A pool for UDP message buffers. udpPool sync.Pool } func (srv *Server) tsigProvider() TsigProvider { if srv.TsigProvider != nil { return srv.TsigProvider } if srv.TsigSecret != nil { return tsigSecretProvider(srv.TsigSecret) } return nil } func (srv *Server) isStarted() bool { srv.lock.RLock() started := srv.started srv.lock.RUnlock() return started } func makeUDPBuffer(size int) func() interface{} { return func() interface{} { return make([]byte, size) } } func (srv *Server) init() { srv.shutdown = make(chan struct{}) srv.conns = make(map[net.Conn]struct{}) if srv.UDPSize == 0 { srv.UDPSize = MinMsgSize } if srv.MsgAcceptFunc == nil { srv.MsgAcceptFunc = DefaultMsgAcceptFunc } if srv.MsgInvalidFunc == nil { srv.MsgInvalidFunc = DefaultMsgInvalidFunc } if srv.Handler == nil { srv.Handler = DefaultServeMux } srv.udpPool.New = makeUDPBuffer(srv.UDPSize) } func unlockOnce(l sync.Locker) func() { var once sync.Once return func() { once.Do(l.Unlock) } } // ListenAndServe starts a nameserver on the configured address in *Server. func (srv *Server) ListenAndServe() error { unlock := unlockOnce(&srv.lock) srv.lock.Lock() defer unlock() if srv.started { return &Error{err: "server already started"} } addr := srv.Addr if addr == "" { addr = ":domain" } srv.init() switch srv.Net { case "tcp", "tcp4", "tcp6": l, err := listenTCP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr) if err != nil { return err } srv.Listener = l srv.started = true unlock() return srv.serveTCP(l) case "tcp-tls", "tcp4-tls", "tcp6-tls": if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) { return errors.New("dns: neither Certificates nor GetCertificate set in Config") } network := strings.TrimSuffix(srv.Net, "-tls") l, err := listenTCP(network, addr, srv.ReusePort, srv.ReuseAddr) if err != nil { return err } l = tls.NewListener(l, srv.TLSConfig) srv.Listener = l srv.started = true unlock() return srv.serveTCP(l) case "udp", "udp4", "udp6": l, err := listenUDP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr) if err != nil { return err } u := l.(*net.UDPConn) if e := setUDPSocketOptions(u); e != nil { u.Close() return e } srv.PacketConn = l srv.started = true unlock() return srv.serveUDP(u) } return &Error{err: "bad network"} } // ActivateAndServe starts a nameserver with the PacketConn or Listener // configured in *Server. Its main use is to start a server from systemd. func (srv *Server) ActivateAndServe() error { unlock := unlockOnce(&srv.lock) srv.lock.Lock() defer unlock() if srv.started { return &Error{err: "server already started"} } srv.init() if srv.PacketConn != nil { // Check PacketConn interface's type is valid and value // is not nil if t, ok := srv.PacketConn.(*net.UDPConn); ok && t != nil { if e := setUDPSocketOptions(t); e != nil { return e } } srv.started = true unlock() return srv.serveUDP(srv.PacketConn) } if srv.Listener != nil { srv.started = true unlock() return srv.serveTCP(srv.Listener) } return &Error{err: "bad listeners"} } // Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and // ActivateAndServe will return. func (srv *Server) Shutdown() error { return srv.ShutdownContext(context.Background()) } // ShutdownContext shuts down a server. After a call to ShutdownContext, // ListenAndServe and ActivateAndServe will return. // // A context.Context may be passed to limit how long to wait for connections // to terminate. func (srv *Server) ShutdownContext(ctx context.Context) error { srv.lock.Lock() if !srv.started { srv.lock.Unlock() return &Error{err: "server not started"} } srv.started = false if srv.PacketConn != nil { srv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads } if srv.Listener != nil { srv.Listener.Close() } for rw := range srv.conns { rw.SetReadDeadline(aLongTimeAgo) // Unblock reads } srv.lock.Unlock() if testShutdownNotify != nil { testShutdownNotify.Broadcast() } var ctxErr error select { case <-srv.shutdown: case <-ctx.Done(): ctxErr = ctx.Err() } if srv.PacketConn != nil { srv.PacketConn.Close() } return ctxErr } var testShutdownNotify *sync.Cond // getReadTimeout is a helper func to use system timeout if server did not intend to change it. func (srv *Server) getReadTimeout() time.Duration { if srv.ReadTimeout != 0 { return srv.ReadTimeout } return dnsTimeout } // serveTCP starts a TCP listener for the server. func (srv *Server) serveTCP(l net.Listener) error { defer l.Close() if srv.NotifyStartedFunc != nil { srv.NotifyStartedFunc() } var wg sync.WaitGroup defer func() { wg.Wait() close(srv.shutdown) }() for srv.isStarted() { rw, err := l.Accept() if err != nil { if !srv.isStarted() { return nil } if neterr, ok := err.(net.Error); ok && neterr.Temporary() { continue } return err } srv.lock.Lock() // Track the connection to allow unblocking reads on shutdown. srv.conns[rw] = struct{}{} srv.lock.Unlock() wg.Add(1) go srv.serveTCPConn(&wg, rw) } return nil } // serveUDP starts a UDP listener for the server. func (srv *Server) serveUDP(l net.PacketConn) error { defer l.Close() reader := Reader(defaultReader{srv}) if srv.DecorateReader != nil { reader = srv.DecorateReader(reader) } lUDP, isUDP := l.(*net.UDPConn) readerPC, canPacketConn := reader.(PacketConnReader) if !isUDP && !canPacketConn { return &Error{err: "PacketConnReader was not implemented on Reader returned from DecorateReader but is required for net.PacketConn"} } if srv.NotifyStartedFunc != nil { srv.NotifyStartedFunc() } var wg sync.WaitGroup defer func() { wg.Wait() close(srv.shutdown) }() rtimeout := srv.getReadTimeout() // deadline is not used here for srv.isStarted() { var ( m []byte sPC net.Addr sUDP *SessionUDP err error ) if isUDP { m, sUDP, err = reader.ReadUDP(lUDP, rtimeout) } else { m, sPC, err = readerPC.ReadPacketConn(l, rtimeout) } if err != nil { if !srv.isStarted() { return nil } if netErr, ok := err.(net.Error); ok && netErr.Temporary() { continue } return err } if len(m) < headerSize { if cap(m) == srv.UDPSize { srv.udpPool.Put(m[:srv.UDPSize]) } srv.MsgInvalidFunc(m, ErrShortRead) continue } wg.Add(1) go srv.serveUDPPacket(&wg, m, l, sUDP, sPC) } return nil } // Serve a new TCP connection. func (srv *Server) serveTCPConn(wg *sync.WaitGroup, rw net.Conn) { w := &response{tsigProvider: srv.tsigProvider(), tcp: rw} if srv.DecorateWriter != nil { w.writer = srv.DecorateWriter(w) } else { w.writer = w } reader := Reader(defaultReader{srv}) if srv.DecorateReader != nil { reader = srv.DecorateReader(reader) } idleTimeout := tcpIdleTimeout if srv.IdleTimeout != nil { idleTimeout = srv.IdleTimeout() } timeout := srv.getReadTimeout() limit := srv.MaxTCPQueries if limit == 0 { limit = maxTCPQueries } for q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ { m, err := reader.ReadTCP(w.tcp, timeout) if err != nil { // TODO(tmthrgd): handle error break } srv.serveDNS(m, w) if w.closed { break // Close() was called } if w.hijacked { break // client will call Close() themselves } // The first read uses the read timeout, the rest use the // idle timeout. timeout = idleTimeout } if !w.hijacked { w.Close() } srv.lock.Lock() delete(srv.conns, w.tcp) srv.lock.Unlock() wg.Done() } // Serve a new UDP request. func (srv *Server) serveUDPPacket(wg *sync.WaitGroup, m []byte, u net.PacketConn, udpSession *SessionUDP, pcSession net.Addr) { w := &response{tsigProvider: srv.tsigProvider(), udp: u, udpSession: udpSession, pcSession: pcSession} if srv.DecorateWriter != nil { w.writer = srv.DecorateWriter(w) } else { w.writer = w } srv.serveDNS(m, w) wg.Done() } func (srv *Server) serveDNS(m []byte, w *response) { dh, off, err := unpackMsgHdr(m, 0) if err != nil { srv.MsgInvalidFunc(m, err) // Let client hang, they are sending crap; any reply can be used to amplify. return } req := new(Msg) req.setHdr(dh) switch action := srv.MsgAcceptFunc(dh); action { case MsgAccept: err := req.unpack(dh, m, off) if err == nil { break } srv.MsgInvalidFunc(m, err) fallthrough case MsgReject, MsgRejectNotImplemented: opcode := req.Opcode req.SetRcodeFormatError(req) req.Zero = false if action == MsgRejectNotImplemented { req.Opcode = opcode req.Rcode = RcodeNotImplemented } // Are we allowed to delete any OPT records here? req.Ns, req.Answer, req.Extra = nil, nil, nil w.WriteMsg(req) fallthrough case MsgIgnore: if w.udp != nil && cap(m) == srv.UDPSize { srv.udpPool.Put(m[:srv.UDPSize]) } return } w.tsigStatus = nil if w.tsigProvider != nil { if t := req.IsTsig(); t != nil { w.tsigStatus = TsigVerifyWithProvider(m, w.tsigProvider, "", false) w.tsigTimersOnly = false w.tsigRequestMAC = t.MAC } } if w.udp != nil && cap(m) == srv.UDPSize { srv.udpPool.Put(m[:srv.UDPSize]) } srv.Handler.ServeDNS(w, req) // Writes back to the client } func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { // If we race with ShutdownContext, the read deadline may // have been set in the distant past to unblock the read // below. We must not override it, otherwise we may block // ShutdownContext. srv.lock.RLock() if srv.started { conn.SetReadDeadline(time.Now().Add(timeout)) } srv.lock.RUnlock() var length uint16 if err := binary.Read(conn, binary.BigEndian, &length); err != nil { return nil, err } m := make([]byte, length) if _, err := io.ReadFull(conn, m); err != nil { return nil, err } return m, nil } func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { srv.lock.RLock() if srv.started { // See the comment in readTCP above. conn.SetReadDeadline(time.Now().Add(timeout)) } srv.lock.RUnlock() m := srv.udpPool.Get().([]byte) n, s, err := ReadFromSessionUDP(conn, m) if err != nil { srv.udpPool.Put(m) return nil, nil, err } m = m[:n] return m, s, nil } func (srv *Server) readPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) { srv.lock.RLock() if srv.started { // See the comment in readTCP above. conn.SetReadDeadline(time.Now().Add(timeout)) } srv.lock.RUnlock() m := srv.udpPool.Get().([]byte) n, addr, err := conn.ReadFrom(m) if err != nil { srv.udpPool.Put(m) return nil, nil, err } m = m[:n] return m, addr, nil } // WriteMsg implements the ResponseWriter.WriteMsg method. func (w *response) WriteMsg(m *Msg) (err error) { if w.closed { return &Error{err: "WriteMsg called after Close"} } var data []byte if w.tsigProvider != nil { // if no provider, dont check for the tsig (which is a longer check) if t := m.IsTsig(); t != nil { data, w.tsigRequestMAC, err = TsigGenerateWithProvider(m, w.tsigProvider, w.tsigRequestMAC, w.tsigTimersOnly) if err != nil { return err } _, err = w.writer.Write(data) return err } } data, err = m.Pack() if err != nil { return err } _, err = w.writer.Write(data) return err } // Write implements the ResponseWriter.Write method. func (w *response) Write(m []byte) (int, error) { if w.closed { return 0, &Error{err: "Write called after Close"} } switch { case w.udp != nil: if u, ok := w.udp.(*net.UDPConn); ok { return WriteToSessionUDP(u, m, w.udpSession) } return w.udp.WriteTo(m, w.pcSession) case w.tcp != nil: if len(m) > MaxMsgSize { return 0, &Error{err: "message too large"} } msg := make([]byte, 2+len(m)) binary.BigEndian.PutUint16(msg, uint16(len(m))) copy(msg[2:], m) return w.tcp.Write(msg) default: panic("dns: internal error: udp and tcp both nil") } } // LocalAddr implements the ResponseWriter.LocalAddr method. func (w *response) LocalAddr() net.Addr { switch { case w.udp != nil: return w.udp.LocalAddr() case w.tcp != nil: return w.tcp.LocalAddr() default: panic("dns: internal error: udp and tcp both nil") } } // RemoteAddr implements the ResponseWriter.RemoteAddr method. func (w *response) RemoteAddr() net.Addr { switch { case w.udpSession != nil: return w.udpSession.RemoteAddr() case w.pcSession != nil: return w.pcSession case w.tcp != nil: return w.tcp.RemoteAddr() default: panic("dns: internal error: udpSession, pcSession and tcp are all nil") } } // TsigStatus implements the ResponseWriter.TsigStatus method. func (w *response) TsigStatus() error { return w.tsigStatus } // TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method. func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b } // Hijack implements the ResponseWriter.Hijack method. func (w *response) Hijack() { w.hijacked = true } // Close implements the ResponseWriter.Close method func (w *response) Close() error { if w.closed { return &Error{err: "connection already closed"} } w.closed = true switch { case w.udp != nil: // Can't close the udp conn, as that is actually the listener. return nil case w.tcp != nil: return w.tcp.Close() default: panic("dns: internal error: udp and tcp both nil") } } // ConnectionState() implements the ConnectionStater.ConnectionState() interface. func (w *response) ConnectionState() *tls.ConnectionState { type tlsConnectionStater interface { ConnectionState() tls.ConnectionState } if v, ok := w.tcp.(tlsConnectionStater); ok { t := v.ConnectionState() return &t } return nil } golang-github-miekg-dns-1.1.64/server_test.go000066400000000000000000001152431476742671700211350ustar00rootroot00000000000000package dns import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "runtime" "strings" "sync" "sync/atomic" "testing" "time" "golang.org/x/sync/errgroup" ) func HelloServer(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} w.WriteMsg(m) } func HelloServerBadID(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Id++ m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} w.WriteMsg(m) } func HelloServerBadThenGoodID(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Id++ m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} w.WriteMsg(m) m.Id-- w.WriteMsg(m) } func HelloServerEchoAddrPort(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) remoteAddr := w.RemoteAddr().String() m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{remoteAddr}} w.WriteMsg(m) } func AnotherHelloServer(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}} w.WriteMsg(m) } func RunLocalServer(pc net.PacketConn, l net.Listener, opts ...func(*Server)) (*Server, string, chan error, error) { server := &Server{ PacketConn: pc, Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour, } waitLock := sync.Mutex{} waitLock.Lock() server.NotifyStartedFunc = waitLock.Unlock for _, opt := range opts { opt(server) } var ( addr string closer io.Closer ) if l != nil { addr = l.Addr().String() closer = l } else { addr = pc.LocalAddr().String() closer = pc } // fin must be buffered so the goroutine below won't block // forever if fin is never read from. This always happens // if the channel is discarded and can happen in TestShutdownUDP. fin := make(chan error, 1) go func() { fin <- server.ActivateAndServe() closer.Close() }() waitLock.Lock() return server, addr, fin, nil } func RunLocalUDPServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) { pc, err := net.ListenPacket("udp", laddr) if err != nil { return nil, "", nil, err } return RunLocalServer(pc, nil, opts...) } func RunLocalPacketConnServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) { return RunLocalUDPServer(laddr, append(opts, func(srv *Server) { // Make srv.PacketConn opaque to trigger the generic code paths. srv.PacketConn = struct{ net.PacketConn }{srv.PacketConn} })...) } func RunLocalTCPServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) { l, err := net.Listen("tcp", laddr) if err != nil { return nil, "", nil, err } return RunLocalServer(nil, l, opts...) } func RunLocalTLSServer(laddr string, config *tls.Config) (*Server, string, chan error, error) { return RunLocalTCPServer(laddr, func(srv *Server) { srv.Listener = tls.NewListener(srv.Listener, config) }) } func RunLocalUnixServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) { l, err := net.Listen("unix", laddr) if err != nil { return nil, "", nil, err } return RunLocalServer(nil, l, opts...) } func RunLocalUnixGramServer(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) { pc, err := net.ListenPacket("unixgram", laddr) if err != nil { return nil, "", nil, err } return RunLocalServer(pc, nil, opts...) } func RunLocalUnixSeqPacketServer(laddr string) (chan interface{}, string, error) { pc, err := net.Listen("unixpacket", laddr) if err != nil { return nil, "", err } shutdownChan := make(chan interface{}) go func() { pc.Accept() <-shutdownChan }() return shutdownChan, pc.Addr().String(), nil } func TestServing(t *testing.T) { for _, tc := range []struct { name string network string runServer func(laddr string, opts ...func(*Server)) (*Server, string, chan error, error) }{ {"udp", "udp", RunLocalUDPServer}, {"tcp", "tcp", RunLocalTCPServer}, {"PacketConn", "udp", RunLocalPacketConnServer}, } { t.Run(tc.name, func(t *testing.T) { HandleFunc("miek.nl.", HelloServer) HandleFunc("example.com.", AnotherHelloServer) defer HandleRemove("miek.nl.") defer HandleRemove("example.com.") s, addrstr, _, err := tc.runServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := &Client{ Net: tc.network, } m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) r, _, err := c.Exchange(m, addrstr) if err != nil || len(r.Extra) == 0 { t.Fatal("failed to exchange miek.nl", err) } txt := r.Extra[0].(*TXT).Txt[0] if txt != "Hello world" { t.Error("unexpected result for miek.nl", txt, "!= Hello world") } m.SetQuestion("example.com.", TypeTXT) r, _, err = c.Exchange(m, addrstr) if err != nil { t.Fatal("failed to exchange example.com", err) } txt = r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Error("unexpected result for example.com", txt, "!= Hello example") } // Test Mixes cased as noticed by Ask. m.SetQuestion("eXaMplE.cOm.", TypeTXT) r, _, err = c.Exchange(m, addrstr) if err != nil { t.Error("failed to exchange eXaMplE.cOm", err) } txt = r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Error("unexpected result for example.com", txt, "!= Hello example") } }) } } // Verify that the server responds to a query with Z flag on, ignoring the flag, and does not echoes it back func TestServeIgnoresZFlag(t *testing.T) { HandleFunc("example.com.", AnotherHelloServer) s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) m := new(Msg) // Test the Z flag is not echoed m.SetQuestion("example.com.", TypeTXT) m.Zero = true r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatal("failed to exchange example.com with +zflag", err) } if r.Zero { t.Error("the response should not have Z flag set - even for a query which does") } if r.Rcode != RcodeSuccess { t.Errorf("expected rcode %v, got %v", RcodeSuccess, r.Rcode) } } // Verify that the server responds to a query with unsupported Opcode with a NotImplemented error and that Opcode is unchanged. func TestServeNotImplemented(t *testing.T) { HandleFunc("example.com.", AnotherHelloServer) opcode := 15 s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) m := new(Msg) // Test that Opcode is like the unchanged from request Opcode and that Rcode is set to NotImplemented m.SetQuestion("example.com.", TypeTXT) m.Opcode = opcode r, _, err := c.Exchange(m, addrstr) if err != nil { t.Fatal("failed to exchange example.com with +zflag", err) } if r.Opcode != opcode { t.Errorf("expected opcode %v, got %v", opcode, r.Opcode) } if r.Rcode != RcodeNotImplemented { t.Errorf("expected rcode %v, got %v", RcodeNotImplemented, r.Rcode) } } func TestServingTLS(t *testing.T) { HandleFunc("miek.nl.", HelloServer) HandleFunc("example.com.", AnotherHelloServer) defer HandleRemove("miek.nl.") defer HandleRemove("example.com.") cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } config := tls.Config{ Certificates: []tls.Certificate{cert}, } s, addrstr, _, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) c.Net = "tcp-tls" c.TLSConfig = &tls.Config{ InsecureSkipVerify: true, } m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) r, _, err := c.Exchange(m, addrstr) if err != nil || len(r.Extra) == 0 { t.Fatal("failed to exchange miek.nl", err) } txt := r.Extra[0].(*TXT).Txt[0] if txt != "Hello world" { t.Error("unexpected result for miek.nl", txt, "!= Hello world") } m.SetQuestion("example.com.", TypeTXT) r, _, err = c.Exchange(m, addrstr) if err != nil { t.Fatal("failed to exchange example.com", err) } txt = r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Error("unexpected result for example.com", txt, "!= Hello example") } // Test Mixes cased as noticed by Ask. m.SetQuestion("eXaMplE.cOm.", TypeTXT) r, _, err = c.Exchange(m, addrstr) if err != nil { t.Error("failed to exchange eXaMplE.cOm", err) } txt = r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Error("unexpected result for example.com", txt, "!= Hello example") } } // TestServingTLSConnectionState tests that we only can access // tls.ConnectionState under a DNS query handled by a TLS DNS server. // This test will sequentially create a TLS, UDP and TCP server, attach a custom // handler which will set a testing error if tls.ConnectionState is available // when it is not expected, or the other way around. func TestServingTLSConnectionState(t *testing.T) { handlerResponse := "Hello example" // tlsHandlerTLS is a HandlerFunc that can be set to expect or not TLS // connection state. tlsHandlerTLS := func(tlsExpected bool) func(ResponseWriter, *Msg) { return func(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) tlsFound := true if connState := w.(ConnectionStater).ConnectionState(); connState == nil { tlsFound = false } if tlsFound != tlsExpected { t.Errorf("TLS connection state available: %t, expected: %t", tlsFound, tlsExpected) } m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{handlerResponse}} w.WriteMsg(m) } } // Question used in tests m := new(Msg) m.SetQuestion("tlsstate.example.net.", TypeTXT) // TLS DNS server HandleFunc(".", tlsHandlerTLS(true)) cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } config := tls.Config{ Certificates: []tls.Certificate{cert}, } s, addrstr, _, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() // TLS DNS query c := &Client{ Net: "tcp-tls", TLSConfig: &tls.Config{ InsecureSkipVerify: true, }, } _, _, err = c.Exchange(m, addrstr) if err != nil { t.Error("failed to exchange tlsstate.example.net", err) } HandleRemove(".") // UDP DNS Server HandleFunc(".", tlsHandlerTLS(false)) defer HandleRemove(".") s, addrstr, _, err = RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() // UDP DNS query c = new(Client) _, _, err = c.Exchange(m, addrstr) if err != nil { t.Error("failed to exchange tlsstate.example.net", err) } // TCP DNS Server s, addrstr, _, err = RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() // TCP DNS query c = &Client{Net: "tcp"} _, _, err = c.Exchange(m, addrstr) if err != nil { t.Error("failed to exchange tlsstate.example.net", err) } } func TestServingListenAndServe(t *testing.T) { HandleFunc("example.com.", AnotherHelloServer) defer HandleRemove("example.com.") waitLock := sync.Mutex{} server := &Server{Addr: ":0", Net: "udp", ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock} waitLock.Lock() go func() { server.ListenAndServe() }() waitLock.Lock() c, m := new(Client), new(Msg) m.SetQuestion("example.com.", TypeTXT) addr := server.PacketConn.LocalAddr().String() // Get address via the PacketConn that gets set. r, _, err := c.Exchange(m, addr) if err != nil { t.Fatal("failed to exchange example.com", err) } txt := r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Error("unexpected result for example.com", txt, "!= Hello example") } server.Shutdown() } func TestServingListenAndServeTLS(t *testing.T) { HandleFunc("example.com.", AnotherHelloServer) defer HandleRemove("example.com.") cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } config := &tls.Config{ Certificates: []tls.Certificate{cert}, } waitLock := sync.Mutex{} server := &Server{Addr: ":0", Net: "tcp", TLSConfig: config, ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock} waitLock.Lock() go func() { server.ListenAndServe() }() waitLock.Lock() c, m := new(Client), new(Msg) c.Net = "tcp" m.SetQuestion("example.com.", TypeTXT) addr := server.Listener.Addr().String() // Get address via the Listener that gets set. r, _, err := c.Exchange(m, addr) if err != nil { t.Fatal(err) } txt := r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Error("unexpected result for example.com", txt, "!= Hello example") } server.Shutdown() } func BenchmarkServe(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") a := runtime.GOMAXPROCS(4) s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { b.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) b.StartTimer() for i := 0; i < b.N; i++ { _, _, err := c.Exchange(m, addrstr) if err != nil { b.Fatalf("Exchange failed: %v", err) } } runtime.GOMAXPROCS(a) } func BenchmarkServe6(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") a := runtime.GOMAXPROCS(4) s, addrstr, _, err := RunLocalUDPServer("[::1]:0") if err != nil { if strings.Contains(err.Error(), "bind: cannot assign requested address") { b.Skip("missing IPv6 support") } b.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) b.StartTimer() for i := 0; i < b.N; i++ { _, _, err := c.Exchange(m, addrstr) if err != nil { b.Fatalf("Exchange failed: %v", err) } } runtime.GOMAXPROCS(a) } func HelloServerCompress(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} m.Compress = true w.WriteMsg(m) } func BenchmarkServeCompress(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServerCompress) defer HandleRemove("miek.nl.") a := runtime.GOMAXPROCS(4) s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { b.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) b.StartTimer() for i := 0; i < b.N; i++ { _, _, err := c.Exchange(m, addrstr) if err != nil { b.Fatalf("Exchange failed: %v", err) } } runtime.GOMAXPROCS(a) } type maxRec struct { max int sync.RWMutex } var M = new(maxRec) func HelloServerLargeResponse(resp ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Authoritative = true m1 := 0 M.RLock() m1 = M.max M.RUnlock() for i := 0; i < m1; i++ { aRec := &A{ Hdr: RR_Header{ Name: req.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0, }, A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i+1)).To4(), } m.Answer = append(m.Answer, aRec) } resp.WriteMsg(m) } func TestServingLargeResponses(t *testing.T) { HandleFunc("example.", HelloServerLargeResponse) defer HandleRemove("example.") s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() // Create request m := new(Msg) m.SetQuestion("web.service.example.", TypeANY) c := new(Client) c.Net = "udp" M.Lock() M.max = 2 M.Unlock() _, _, err = c.Exchange(m, addrstr) if err != nil { t.Errorf("failed to exchange: %v", err) } // This must fail M.Lock() M.max = 20 M.Unlock() _, _, err = c.Exchange(m, addrstr) if err == nil { t.Error("failed to fail exchange, this should generate packet error") } // But this must work again c.UDPSize = 7000 _, _, err = c.Exchange(m, addrstr) if err != nil { t.Errorf("failed to exchange: %v", err) } } func TestServingResponse(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } HandleFunc("miek.nl.", HelloServer) s, addrstr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) m.Response = false _, _, err = c.Exchange(m, addrstr) if err != nil { t.Fatal("failed to exchange", err) } m.Response = true // this holds up the reply, set short read time out to avoid waiting too long c.ReadTimeout = 100 * time.Millisecond _, _, err = c.Exchange(m, addrstr) if err == nil { t.Fatal("exchanged response message") } } func TestShutdownTCP(t *testing.T) { s, _, fin, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } err = s.Shutdown() if err != nil { t.Fatalf("could not shutdown test TCP server, %v", err) } select { case err := <-fin: if err != nil { t.Errorf("error returned from ActivateAndServe, %v", err) } case <-time.After(2 * time.Second): t.Error("could not shutdown test TCP server. Gave up waiting") } } func init() { testShutdownNotify = &sync.Cond{ L: new(sync.Mutex), } } func checkInProgressQueriesAtShutdownServer(t *testing.T, srv *Server, addr string, client *Client) { const requests = 15 // enough to make this interesting? TODO: find a proper value var errOnce sync.Once // t.Fail will panic if it's called after the test function has // finished. Burning the sync.Once with a defer will prevent the // handler from calling t.Errorf after we've returned. defer errOnce.Do(func() {}) toHandle := int32(requests) HandleFunc("example.com.", func(w ResponseWriter, req *Msg) { defer atomic.AddInt32(&toHandle, -1) // Wait until ShutdownContext is called before replying. testShutdownNotify.L.Lock() testShutdownNotify.Wait() testShutdownNotify.L.Unlock() m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} if err := w.WriteMsg(m); err != nil { errOnce.Do(func() { t.Errorf("ResponseWriter.WriteMsg error: %s", err) }) } }) defer HandleRemove("example.com.") client.Timeout = 1 * time.Second conns := make([]*Conn, requests) eg := new(errgroup.Group) for i := range conns { conn := &conns[i] eg.Go(func() error { var err error *conn, err = client.Dial(addr) return err }) } if eg.Wait() != nil { t.Fatalf("client.Dial error: %v", eg.Wait()) } m := new(Msg) m.SetQuestion("example.com.", TypeTXT) eg = new(errgroup.Group) for _, conn := range conns { conn := conn eg.Go(func() error { conn.SetWriteDeadline(time.Now().Add(client.Timeout)) return conn.WriteMsg(m) }) } if eg.Wait() != nil { t.Fatalf("conn.WriteMsg error: %v", eg.Wait()) } // This sleep is needed to allow time for the requests to // pass from the client through the kernel and back into // the server. Without it, some requests may still be in // the kernel's buffer when ShutdownContext is called. time.Sleep(100 * time.Millisecond) eg = new(errgroup.Group) for _, conn := range conns { conn := conn eg.Go(func() error { conn.SetReadDeadline(time.Now().Add(client.Timeout)) _, err := conn.ReadMsg() return err }) } ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) defer cancel() if err := srv.ShutdownContext(ctx); err != nil { t.Errorf("could not shutdown test server: %v", err) } if left := atomic.LoadInt32(&toHandle); left != 0 { t.Errorf("ShutdownContext returned before %d replies", left) } if eg.Wait() != nil { t.Errorf("conn.ReadMsg error: %v", eg.Wait()) } srv.lock.RLock() defer srv.lock.RUnlock() if len(srv.conns) != 0 { t.Errorf("TCP connection tracking map not empty after ShutdownContext; map still contains %d connections", len(srv.conns)) } } func TestInProgressQueriesAtShutdownTCP(t *testing.T) { s, addr, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } c := &Client{Net: "tcp"} checkInProgressQueriesAtShutdownServer(t, s, addr, c) } func TestShutdownTLS(t *testing.T) { cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } config := tls.Config{ Certificates: []tls.Certificate{cert}, } s, _, _, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } err = s.Shutdown() if err != nil { t.Errorf("could not shutdown test TLS server, %v", err) } } func TestInProgressQueriesAtShutdownTLS(t *testing.T) { cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } config := tls.Config{ Certificates: []tls.Certificate{cert}, } s, addr, _, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } c := &Client{ Net: "tcp-tls", TLSConfig: &tls.Config{ InsecureSkipVerify: true, }, } checkInProgressQueriesAtShutdownServer(t, s, addr, c) } func TestHandlerCloseTCP(t *testing.T) { ln, err := net.Listen("tcp", ":0") if err != nil { panic(err) } addr := ln.Addr().String() server := &Server{Addr: addr, Net: "tcp", Listener: ln} hname := "testhandlerclosetcp." triggered := make(chan struct{}) HandleFunc(hname, func(w ResponseWriter, r *Msg) { close(triggered) w.Close() }) defer HandleRemove(hname) go func() { defer server.Shutdown() c := &Client{Net: "tcp"} m := new(Msg).SetQuestion(hname, 1) tries := 0 exchange: _, _, err := c.Exchange(m, addr) if err != nil && err != io.EOF { t.Errorf("exchange failed: %v", err) if tries == 3 { return } time.Sleep(time.Second / 10) tries++ goto exchange } }() if err := server.ActivateAndServe(); err != nil { t.Fatalf("ActivateAndServe failed: %v", err) } select { case <-triggered: default: t.Fatalf("handler never called") } } func TestShutdownUDP(t *testing.T) { s, _, fin, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } err = s.Shutdown() if err != nil { t.Errorf("could not shutdown test UDP server, %v", err) } select { case err := <-fin: if err != nil { t.Errorf("error returned from ActivateAndServe, %v", err) } case <-time.After(2 * time.Second): t.Error("could not shutdown test UDP server. Gave up waiting") } } func TestShutdownPacketConn(t *testing.T) { s, _, fin, err := RunLocalPacketConnServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } err = s.Shutdown() if err != nil { t.Errorf("could not shutdown test UDP server, %v", err) } select { case err := <-fin: if err != nil { t.Errorf("error returned from ActivateAndServe, %v", err) } case <-time.After(2 * time.Second): t.Error("could not shutdown test UDP server. Gave up waiting") } } func TestInProgressQueriesAtShutdownUDP(t *testing.T) { s, addr, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } c := &Client{Net: "udp"} checkInProgressQueriesAtShutdownServer(t, s, addr, c) } func TestInProgressQueriesAtShutdownPacketConn(t *testing.T) { s, addr, _, err := RunLocalPacketConnServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } c := &Client{Net: "udp"} checkInProgressQueriesAtShutdownServer(t, s, addr, c) } func TestServerStartStopRace(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) s, _, _, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("could not start server: %s", err) } go func() { defer wg.Done() if err := s.Shutdown(); err != nil { t.Errorf("could not stop server: %s", err) } }() } wg.Wait() } func TestSocketOptions(t *testing.T) { if !supportsReuseAddr || !supportsReusePort { t.Skip("reuseaddr or reuseport is not supported") } testSocketOptions := func(t *testing.T, reuseAddr bool, reusePort bool) { wait := make(chan struct{}) srv := &Server{ Net: "udp", Addr: ":0", ReuseAddr: reuseAddr, ReusePort: reusePort, } srv.NotifyStartedFunc = func() { defer close(wait) conn, ok := srv.PacketConn.(*net.UDPConn) if !ok { t.Errorf("unexpected conn type: %T", srv.PacketConn) return } syscallConn, err := conn.SyscallConn() if err != nil { t.Errorf("cannot cast UDP conn to syscall conn: %v", err) return } err = syscallConn.Control(func(fd uintptr) { actualReusePort, err := checkReuseport(fd) if err != nil { t.Errorf("cannot get SO_REUSEPORT socket option: %v", err) return } if actualReusePort != reusePort { t.Errorf("SO_REUSEPORT is %v instead of %v", actualReusePort, reusePort) } actualReuseAddr, err := checkReuseaddr(fd) if err != nil { t.Errorf("cannot get SO_REUSEADDR socket option: %v", err) return } if actualReuseAddr != reuseAddr { t.Errorf("SO_REUSEADDR is %v instead of %v", actualReuseAddr, reusePort) } }) if err != nil { t.Errorf("cannot check socket options: %v", err) } } fin := make(chan error, 1) go func() { fin <- srv.ListenAndServe() }() select { case <-wait: err := srv.Shutdown() if err != nil { t.Fatalf("cannot shutdown server: %v", err) } err = <-fin if err != nil { t.Fatalf("listen adn serve: %v", err) } case err := <-fin: t.Fatalf("listen adn serve: %v", err) } } t.Run("no socket options", func(t *testing.T) { testSocketOptions(t, false, false) }) t.Run("SO_REUSEPORT", func(t *testing.T) { testSocketOptions(t, false, true) }) t.Run("SO_REUSEADDR", func(t *testing.T) { testSocketOptions(t, true, false) }) t.Run("SO_REUSEADDR and SO_REUSEPORT", func(t *testing.T) { testSocketOptions(t, true, true) }) } func TestServerReuseport(t *testing.T) { if !supportsReusePort { t.Skip("reuseport is not supported") } startServer := func(addr string) (*Server, chan error) { wait := make(chan struct{}) srv := &Server{ Net: "udp", Addr: addr, NotifyStartedFunc: func() { close(wait) }, ReusePort: true, } fin := make(chan error, 1) go func() { fin <- srv.ListenAndServe() }() select { case <-wait: case err := <-fin: t.Fatalf("failed to start server: %v", err) } return srv, fin } srv1, fin1 := startServer(":0") // :0 is resolved to a random free port by the kernel srv2, fin2 := startServer(srv1.PacketConn.LocalAddr().String()) if err := srv1.Shutdown(); err != nil { t.Fatalf("failed to shutdown first server: %v", err) } if err := srv2.Shutdown(); err != nil { t.Fatalf("failed to shutdown second server: %v", err) } if err := <-fin1; err != nil { t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err) } if err := <-fin2; err != nil { t.Fatalf("second ListenAndServe returned error after Shutdown: %v", err) } } func TestServerReuseaddr(t *testing.T) { startServerFn := func(t *testing.T, network, addr string, expectSuccess bool) (*Server, chan error) { t.Helper() wait := make(chan struct{}) srv := &Server{ Net: network, Addr: addr, NotifyStartedFunc: func() { close(wait) }, ReuseAddr: true, } fin := make(chan error, 1) go func() { fin <- srv.ListenAndServe() }() select { case <-wait: case err := <-fin: switch { case expectSuccess: t.Fatalf("%s: failed to start server: %v", t.Name(), err) default: fin <- err return nil, fin } } return srv, fin } externalIPFn := func(t *testing.T) (string, error) { t.Helper() ifaces, err := net.Interfaces() if err != nil { return "", err } for _, iface := range ifaces { if iface.Flags&net.FlagUp == 0 { continue // interface down } if iface.Flags&net.FlagLoopback != 0 { continue // loopback interface } addrs, err := iface.Addrs() if err != nil { return "", err } for _, addr := range addrs { var ip net.IP switch v := addr.(type) { case *net.IPNet: ip = v.IP case *net.IPAddr: ip = v.IP } if ip == nil || ip.IsLoopback() { continue } ip = ip.To4() if ip == nil { continue // not an ipv4 address } return ip.String(), nil } } return "", errors.New("are you connected to the network?") } freePortFn := func(t *testing.T) int { t.Helper() addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { t.Fatalf("unable resolve tcp addr: %s", err) } l, err := net.ListenTCP("tcp", addr) if err != nil { t.Fatalf("unable listen tcp: %s", err) } defer l.Close() return l.Addr().(*net.TCPAddr).Port } t.Run("should-fail-tcp", func(t *testing.T) { // ReuseAddr should fail if you try to bind to exactly the same // combination of source address and port. // This should fail whether or not ReuseAddr is supported on a // particular OS ip, err := externalIPFn(t) if err != nil { t.Skip("no external IPs found") return } port := freePortFn(t) srv1, fin1 := startServerFn(t, "tcp", fmt.Sprintf("%s:%d", ip, port), true) srv2, fin2 := startServerFn(t, "tcp", fmt.Sprintf("%s:%d", ip, port), false) switch { case srv2 != nil && srv2.started: t.Fatalf("second ListenAndServe should not have started") default: if err := <-fin2; err == nil { t.Fatalf("second ListenAndServe should have returned a startup error: %v", err) } } if err := srv1.Shutdown(); err != nil { t.Fatalf("failed to shutdown first server: %v", err) } if err := <-fin1; err != nil { t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err) } }) t.Run("should-succeed-tcp", func(t *testing.T) { if !supportsReuseAddr { t.Skip("reuseaddr is not supported") } ip, err := externalIPFn(t) if err != nil { t.Skip("no external IPs found") return } port := freePortFn(t) // ReuseAddr should succeed if you try to bind to the same port but a different source address srv1, fin1 := startServerFn(t, "tcp", fmt.Sprintf("localhost:%d", port), true) srv2, fin2 := startServerFn(t, "tcp", fmt.Sprintf("%s:%d", ip, port), true) if err := srv1.Shutdown(); err != nil { t.Fatalf("failed to shutdown first server: %v", err) } if err := srv2.Shutdown(); err != nil { t.Fatalf("failed to shutdown second server: %v", err) } if err := <-fin1; err != nil { t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err) } if err := <-fin2; err != nil { t.Fatalf("second ListenAndServe returned error after Shutdown: %v", err) } }) t.Run("should-succeed-udp", func(t *testing.T) { if !supportsReuseAddr { t.Skip("reuseaddr is not supported") } ip, err := externalIPFn(t) if err != nil { t.Skip("no external IPs found") return } port := freePortFn(t) // ReuseAddr should succeed if you try to bind to the same port but a different source address srv1, fin1 := startServerFn(t, "udp", fmt.Sprintf("localhost:%d", port), true) srv2, fin2 := startServerFn(t, "udp", fmt.Sprintf("%s:%d", ip, port), true) if err := srv1.Shutdown(); err != nil { t.Fatalf("failed to shutdown first server: %v", err) } if err := srv2.Shutdown(); err != nil { t.Fatalf("failed to shutdown second server: %v", err) } if err := <-fin1; err != nil { t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err) } if err := <-fin2; err != nil { t.Fatalf("second ListenAndServe returned error after Shutdown: %v", err) } }) } func TestServerRoundtripTsig(t *testing.T) { secret := map[string]string{"test.": "so6ZGir4GPAqINNh9U5c3A=="} s, addrstr, _, err := RunLocalUDPServer(":0", func(srv *Server) { srv.TsigSecret = secret srv.MsgAcceptFunc = func(dh Header) MsgAcceptAction { // defaultMsgAcceptFunc does reject UPDATE queries return MsgAccept } }) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() handlerFired := make(chan struct{}) HandleFunc("example.com.", func(w ResponseWriter, r *Msg) { close(handlerFired) m := new(Msg) m.SetReply(r) if r.IsTsig() != nil { status := w.TsigStatus() if status == nil { // *Msg r has an TSIG record and it was validated m.SetTsig("test.", HmacSHA256, 300, time.Now().Unix()) } else { // *Msg r has an TSIG records and it was not validated t.Errorf("invalid TSIG: %v", status) } } else { t.Error("missing TSIG") } if err := w.WriteMsg(m); err != nil { t.Error("writemsg failed", err) } }) c := new(Client) m := new(Msg) m.Opcode = OpcodeUpdate m.SetQuestion("example.com.", TypeSOA) m.Ns = []RR{&CNAME{ Hdr: RR_Header{ Name: "foo.example.com.", Rrtype: TypeCNAME, Class: ClassINET, Ttl: 300, }, Target: "bar.example.com.", }} c.TsigSecret = secret m.SetTsig("test.", HmacSHA256, 300, time.Now().Unix()) _, _, err = c.Exchange(m, addrstr) if err != nil { t.Fatal("failed to exchange", err) } select { case <-handlerFired: // ok, handler was actually called default: t.Error("handler was not called") } } func TestResponseAfterClose(t *testing.T) { testError := func(name string, err error) { t.Helper() expect := fmt.Sprintf("dns: %s called after Close", name) if err == nil { t.Errorf("expected error from %s after Close", name) } else if err.Error() != expect { t.Errorf("expected explicit error from %s after Close, expected %q, got %q", name, expect, err) } } rw := &response{ closed: true, } _, err := rw.Write(make([]byte, 2)) testError("Write", err) testError("WriteMsg", rw.WriteMsg(new(Msg))) } func TestResponseDoubleClose(t *testing.T) { rw := &response{ closed: true, } if err, expect := rw.Close(), "dns: connection already closed"; err == nil || err.Error() != expect { t.Errorf("Close did not return expected: error %q, got: %v", expect, err) } } type countingConn struct { net.Conn writes int } func (c *countingConn) Write(p []byte) (int, error) { c.writes++ return len(p), nil } func TestResponseWriteSinglePacket(t *testing.T) { c := &countingConn{} rw := &response{ tcp: c, } rw.writer = rw m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) m.Response = true err := rw.WriteMsg(m) if err != nil { t.Fatalf("failed to write: %v", err) } if c.writes != 1 { t.Fatalf("incorrect number of Write calls") } } type ExampleFrameLengthWriter struct { Writer } func (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) { fmt.Println("writing raw DNS message of length", len(m)) return e.Writer.Write(m) } func ExampleDecorateWriter() { // instrument raw DNS message writing wf := DecorateWriter(func(w Writer) Writer { return &ExampleFrameLengthWriter{w} }) // simple UDP server pc, err := net.ListenPacket("udp", ":0") if err != nil { fmt.Println(err.Error()) return } server := &Server{ PacketConn: pc, DecorateWriter: wf, ReadTimeout: time.Hour, WriteTimeout: time.Hour, } waitLock := sync.Mutex{} waitLock.Lock() server.NotifyStartedFunc = waitLock.Unlock defer server.Shutdown() go func() { server.ActivateAndServe() pc.Close() }() waitLock.Lock() HandleFunc("miek.nl.", HelloServer) c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) _, _, err = c.Exchange(m, pc.LocalAddr().String()) if err != nil { fmt.Println("failed to exchange", err.Error()) return } // Output: writing raw DNS message of length 56 } var ( // CertPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair) CertPEMBlock = []byte(`-----BEGIN CERTIFICATE----- MIIDAzCCAeugAwIBAgIRAJFYMkcn+b8dpU15wjf++GgwDQYJKoZIhvcNAQELBQAw EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjAxMDgxMjAzNTNaFw0xNzAxMDcxMjAz NTNaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDXjqO6skvP03k58CNjQggd9G/mt+Wa+xRU+WXiKCCHttawM8x+slq5 yfsHCwxlwsGn79HmJqecNqgHb2GWBXAvVVokFDTcC1hUP4+gp2gu9Ny27UHTjlLm O0l/xZ5MN8tfKyYlFw18tXu3fkaPyHj8v/D1RDkuo4ARdFvGSe8TqisbhLk2+9ow xfIGbEM9Fdiw8qByC2+d+FfvzIKz3GfQVwn0VoRom8L6NBIANq1IGrB5JefZB6nv DnfuxkBmY7F1513HKuEJ8KsLWWZWV9OPU4j4I4Rt+WJNlKjbD2srHxyrS2RDsr91 8nCkNoWVNO3sZq0XkWKecdc921vL4ginAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIC pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQT MBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGcU3iyLBIVZj aDzSvEDHUd1bnLBl1C58Xu/CyKlPqVU7mLfK0JcgEaYQTSX6fCJVNLbbCrcGLsPJ fbjlBbyeLjTV413fxPVuona62pBFjqdtbli2Qe8FRH2KBdm41JUJGdo+SdsFu7nc BFOcubdw6LLIXvsTvwndKcHWx1rMX709QU1Vn1GAIsbJV/DWI231Jyyb+lxAUx/C 8vce5uVxiKcGS+g6OjsN3D3TtiEQGSXLh013W6Wsih8td8yMCMZ3w8LQ38br1GUe ahLIgUJ9l6HDguM17R7kGqxNvbElsMUHfTtXXP7UDQUiYXDakg8xDP6n9DCDhJ8Y bSt7OLB7NQ== -----END CERTIFICATE-----`) // KeyPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair) KeyPEMBlock = []byte(`-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA146jurJLz9N5OfAjY0IIHfRv5rflmvsUVPll4iggh7bWsDPM frJaucn7BwsMZcLBp+/R5iannDaoB29hlgVwL1VaJBQ03AtYVD+PoKdoLvTctu1B 045S5jtJf8WeTDfLXysmJRcNfLV7t35Gj8h4/L/w9UQ5LqOAEXRbxknvE6orG4S5 NvvaMMXyBmxDPRXYsPKgcgtvnfhX78yCs9xn0FcJ9FaEaJvC+jQSADatSBqweSXn 2Qep7w537sZAZmOxdeddxyrhCfCrC1lmVlfTj1OI+COEbfliTZSo2w9rKx8cq0tk Q7K/dfJwpDaFlTTt7GatF5FinnHXPdtby+IIpwIDAQABAoIBAAJK4RDmPooqTJrC JA41MJLo+5uvjwCT9QZmVKAQHzByUFw1YNJkITTiognUI0CdzqNzmH7jIFs39ZeG proKusO2G6xQjrNcZ4cV2fgyb5g4QHStl0qhs94A+WojduiGm2IaumAgm6Mc5wDv ld6HmknN3Mku/ZCyanVFEIjOVn2WB7ZQLTBs6ZYaebTJG2Xv6p9t2YJW7pPQ9Xce s9ohAWohyM4X/OvfnfnLtQp2YLw/BxwehBsCR5SXM3ibTKpFNtxJC8hIfTuWtxZu 2ywrmXShYBRB1WgtZt5k04bY/HFncvvcHK3YfI1+w4URKtwdaQgPUQRbVwDwuyBn flfkCJECgYEA/eWt01iEyE/lXkGn6V9lCocUU7lCU6yk5UT8VXVUc5If4KZKPfCk p4zJDOqwn2eM673aWz/mG9mtvAvmnugaGjcaVCyXOp/D/GDmKSoYcvW5B/yjfkLy dK6Yaa5LDRVYlYgyzcdCT5/9Qc626NzFwKCZNI4ncIU8g7ViATRxWJ8CgYEA2Ver vZ0M606sfgC0H3NtwNBxmuJ+lIF5LNp/wDi07lDfxRR1rnZMX5dnxjcpDr/zvm8J WtJJX3xMgqjtHuWKL3yKKony9J5ZPjichSbSbhrzfovgYIRZLxLLDy4MP9L3+CX/ yBXnqMWuSnFX+M5fVGxdDWiYF3V+wmeOv9JvavkCgYEAiXAPDFzaY+R78O3xiu7M r0o3wqqCMPE/wav6O/hrYrQy9VSO08C0IM6g9pEEUwWmzuXSkZqhYWoQFb8Lc/GI T7CMXAxXQLDDUpbRgG79FR3Wr3AewHZU8LyiXHKwxcBMV4WGmsXGK3wbh8fyU1NO 6NsGk+BvkQVOoK1LBAPzZ1kCgYEAsBSmD8U33T9s4dxiEYTrqyV0lH3g/SFz8ZHH pAyNEPI2iC1ONhyjPWKlcWHpAokiyOqeUpVBWnmSZtzC1qAydsxYB6ShT+sl9BHb RMix/QAauzBJhQhUVJ3OIys0Q1UBDmqCsjCE8SfOT4NKOUnA093C+YT+iyrmmktZ zDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb 7+a1OMhLwWbuz+pl64wFKrbSUyimMOYQpjVE/1vk/kb99pxbgol27hdKyTH1d+ov kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA= -----END RSA PRIVATE KEY-----`) ) golang-github-miekg-dns-1.1.64/sig0.go000066400000000000000000000107761476742671700174370ustar00rootroot00000000000000package dns import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "encoding/binary" "math/big" "time" ) // Sign signs a dns.Msg. It fills the signature with the appropriate data. // The SIG record should have the SignerName, KeyTag, Algorithm, Inception // and Expiration set. func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { if k == nil { return nil, ErrPrivKey } if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 { return nil, ErrKey } rr.Hdr = RR_Header{Name: ".", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0} rr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0 buf := make([]byte, m.Len()+Len(rr)) mbuf, err := m.PackBuffer(buf) if err != nil { return nil, err } if &buf[0] != &mbuf[0] { return nil, ErrBuf } off, err := PackRR(rr, buf, len(mbuf), nil, false) if err != nil { return nil, err } buf = buf[:off:cap(buf)] h, cryptohash, err := hashFromAlgorithm(rr.Algorithm) if err != nil { return nil, err } // Write SIG rdata h.Write(buf[len(mbuf)+1+2+2+4+2:]) // Write message h.Write(buf[:len(mbuf)]) signature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm) if err != nil { return nil, err } rr.Signature = toBase64(signature) buf = append(buf, signature...) if len(buf) > int(^uint16(0)) { return nil, ErrBuf } // Adjust sig data length rdoff := len(mbuf) + 1 + 2 + 2 + 4 rdlen := binary.BigEndian.Uint16(buf[rdoff:]) rdlen += uint16(len(signature)) binary.BigEndian.PutUint16(buf[rdoff:], rdlen) // Adjust additional count adc := binary.BigEndian.Uint16(buf[10:]) adc++ binary.BigEndian.PutUint16(buf[10:], adc) return buf, nil } // Verify validates the message buf using the key k. // It's assumed that buf is a valid message from which rr was unpacked. func (rr *SIG) Verify(k *KEY, buf []byte) error { if k == nil { return ErrKey } if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 { return ErrKey } h, cryptohash, err := hashFromAlgorithm(rr.Algorithm) if err != nil { return err } buflen := len(buf) qdc := binary.BigEndian.Uint16(buf[4:]) anc := binary.BigEndian.Uint16(buf[6:]) auc := binary.BigEndian.Uint16(buf[8:]) adc := binary.BigEndian.Uint16(buf[10:]) offset := headerSize for i := uint16(0); i < qdc && offset < buflen; i++ { _, offset, err = UnpackDomainName(buf, offset) if err != nil { return err } // Skip past Type and Class offset += 2 + 2 } for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ { _, offset, err = UnpackDomainName(buf, offset) if err != nil { return err } // Skip past Type, Class and TTL offset += 2 + 2 + 4 if offset+1 >= buflen { continue } rdlen := binary.BigEndian.Uint16(buf[offset:]) offset += 2 offset += int(rdlen) } if offset >= buflen { return &Error{err: "overflowing unpacking signed message"} } // offset should be just prior to SIG bodyend := offset // owner name SHOULD be root _, offset, err = UnpackDomainName(buf, offset) if err != nil { return err } // Skip Type, Class, TTL, RDLen offset += 2 + 2 + 4 + 2 sigstart := offset // Skip Type Covered, Algorithm, Labels, Original TTL offset += 2 + 1 + 1 + 4 if offset+4+4 >= buflen { return &Error{err: "overflow unpacking signed message"} } expire := binary.BigEndian.Uint32(buf[offset:]) offset += 4 incept := binary.BigEndian.Uint32(buf[offset:]) offset += 4 now := uint32(time.Now().Unix()) if now < incept || now > expire { return ErrTime } // Skip key tag offset += 2 var signername string signername, offset, err = UnpackDomainName(buf, offset) if err != nil { return err } // If key has come from the DNS name compression might // have mangled the case of the name if !equal(signername, k.Header().Name) { return &Error{err: "signer name doesn't match key name"} } sigend := offset h.Write(buf[sigstart:sigend]) h.Write(buf[:10]) h.Write([]byte{ byte((adc - 1) << 8), byte(adc - 1), }) h.Write(buf[12:bodyend]) hashed := h.Sum(nil) sig := buf[sigend:] switch k.Algorithm { case RSASHA1, RSASHA256, RSASHA512: pk := k.publicKeyRSA() if pk != nil { return rsa.VerifyPKCS1v15(pk, cryptohash, hashed, sig) } case ECDSAP256SHA256, ECDSAP384SHA384: pk := k.publicKeyECDSA() r := new(big.Int).SetBytes(sig[:len(sig)/2]) s := new(big.Int).SetBytes(sig[len(sig)/2:]) if pk != nil { if ecdsa.Verify(pk, hashed, r, s) { return nil } return ErrSig } case ED25519: pk := k.publicKeyED25519() if pk != nil { if ed25519.Verify(pk, hashed, sig) { return nil } return ErrSig } } return ErrKeyAlg } golang-github-miekg-dns-1.1.64/sig0_test.go000066400000000000000000000043201476742671700204620ustar00rootroot00000000000000package dns import ( "crypto" "testing" "time" ) func TestSIG0(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } m := new(Msg) m.SetQuestion("example.org.", TypeSOA) for _, alg := range []uint8{ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512, ED25519} { algstr := AlgorithmToString[alg] keyrr := new(KEY) keyrr.Hdr.Name = algstr + "." keyrr.Hdr.Rrtype = TypeKEY keyrr.Hdr.Class = ClassINET keyrr.Algorithm = alg keysize := 512 switch alg { case ECDSAP256SHA256, ED25519: keysize = 256 case ECDSAP384SHA384: keysize = 384 case RSASHA512: keysize = 1024 } pk, err := keyrr.Generate(keysize) if err != nil { t.Errorf("failed to generate key for %q: %v", algstr, err) continue } now := uint32(time.Now().Unix()) sigrr := new(SIG) sigrr.Hdr.Name = "." sigrr.Hdr.Rrtype = TypeSIG sigrr.Hdr.Class = ClassANY sigrr.Algorithm = alg sigrr.Expiration = now + 300 sigrr.Inception = now - 300 sigrr.KeyTag = keyrr.KeyTag() sigrr.SignerName = keyrr.Hdr.Name mb, err := sigrr.Sign(pk.(crypto.Signer), m) if err != nil { t.Errorf("failed to sign message using %q: %v", algstr, err) continue } m := new(Msg) if err := m.Unpack(mb); err != nil { t.Errorf("failed to unpack message signed using %q: %v", algstr, err) continue } if len(m.Extra) != 1 { t.Errorf("missing SIG for message signed using %q", algstr) continue } var sigrrwire *SIG switch rr := m.Extra[0].(type) { case *SIG: sigrrwire = rr default: t.Errorf("expected SIG RR, instead: %v", rr) continue } for _, rr := range []*SIG{sigrr, sigrrwire} { id := "sigrr" if rr == sigrrwire { id = "sigrrwire" } if err := rr.Verify(keyrr, mb); err != nil { t.Errorf("failed to verify %q signed SIG(%s): %v", algstr, id, err) continue } } mb[13]++ if err := sigrr.Verify(keyrr, mb); err == nil { t.Errorf("verify succeeded on an altered message using %q", algstr) continue } sigrr.Expiration = 2 sigrr.Inception = 1 mb, _ = sigrr.Sign(pk.(crypto.Signer), m) if err := sigrr.Verify(keyrr, mb); err == nil { t.Errorf("verify succeeded on an expired message using %q", algstr) continue } } } golang-github-miekg-dns-1.1.64/smimea.go000066400000000000000000000025211476742671700200350ustar00rootroot00000000000000package dns import ( "crypto/sha256" "crypto/x509" "encoding/hex" ) // Sign creates a SMIMEA record from an SSL certificate. func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) { r.Hdr.Rrtype = TypeSMIMEA r.Usage = uint8(usage) r.Selector = uint8(selector) r.MatchingType = uint8(matchingType) r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert) return err } // Verify verifies a SMIMEA record against an SSL certificate. If it is OK // a nil error is returned. func (r *SMIMEA) Verify(cert *x509.Certificate) error { c, err := CertificateToDANE(r.Selector, r.MatchingType, cert) if err != nil { return err // Not also ErrSig? } if r.Certificate == c { return nil } return ErrSig // ErrSig, really? } // SMIMEAName returns the ownername of a SMIMEA resource record as per the // format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3 func SMIMEAName(email, domain string) (string, error) { hasher := sha256.New() hasher.Write([]byte(email)) // RFC Section 3: "The local-part is hashed using the SHA2-256 // algorithm with the hash truncated to 28 octets and // represented in its hexadecimal representation to become the // left-most label in the prepared domain name" return hex.EncodeToString(hasher.Sum(nil)[:28]) + "." + "_smimecert." + domain, nil } golang-github-miekg-dns-1.1.64/svcb.go000066400000000000000000000660731476742671700175330ustar00rootroot00000000000000package dns import ( "bytes" "encoding/binary" "errors" "fmt" "net" "sort" "strconv" "strings" ) // SVCBKey is the type of the keys used in the SVCB RR. type SVCBKey uint16 // Keys defined in rfc9460 const ( SVCB_MANDATORY SVCBKey = iota SVCB_ALPN SVCB_NO_DEFAULT_ALPN SVCB_PORT SVCB_IPV4HINT SVCB_ECHCONFIG SVCB_IPV6HINT SVCB_DOHPATH // rfc9461 Section 5 SVCB_OHTTP // rfc9540 Section 8 svcb_RESERVED SVCBKey = 65535 ) var svcbKeyToStringMap = map[SVCBKey]string{ SVCB_MANDATORY: "mandatory", SVCB_ALPN: "alpn", SVCB_NO_DEFAULT_ALPN: "no-default-alpn", SVCB_PORT: "port", SVCB_IPV4HINT: "ipv4hint", SVCB_ECHCONFIG: "ech", SVCB_IPV6HINT: "ipv6hint", SVCB_DOHPATH: "dohpath", SVCB_OHTTP: "ohttp", } var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap) func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey { n := make(map[string]SVCBKey, len(m)) for u, s := range m { n[s] = u } return n } // String takes the numerical code of an SVCB key and returns its name. // Returns an empty string for reserved keys. // Accepts unassigned keys as well as experimental/private keys. func (key SVCBKey) String() string { if x := svcbKeyToStringMap[key]; x != "" { return x } if key == svcb_RESERVED { return "" } return "key" + strconv.FormatUint(uint64(key), 10) } // svcbStringToKey returns the numerical code of an SVCB key. // Returns svcb_RESERVED for reserved/invalid keys. // Accepts unassigned keys as well as experimental/private keys. func svcbStringToKey(s string) SVCBKey { if strings.HasPrefix(s, "key") { a, err := strconv.ParseUint(s[3:], 10, 16) // no leading zeros // key shouldn't be registered if err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != "" { return svcb_RESERVED } return SVCBKey(a) } if key, ok := svcbStringToKeyMap[s]; ok { return key } return svcb_RESERVED } func (rr *SVCB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return &ParseError{file: l.token, err: "bad SVCB priority", lex: l} } rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Target = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { return &ParseError{file: l.token, err: "bad SVCB Target", lex: l} } rr.Target = name // Values (if any) l, _ = c.Next() var xs []SVCBKeyValue // Helps require whitespace between pairs. // Prevents key1000="a"key1001=... canHaveNextKey := true for l.value != zNewline && l.value != zEOF { switch l.value { case zString: if !canHaveNextKey { // The key we can now read was probably meant to be // a part of the last value. return &ParseError{file: l.token, err: "bad SVCB value quotation", lex: l} } // In key=value pairs, value does not have to be quoted unless value // contains whitespace. And keys don't need to have values. // Similarly, keys with an equality signs after them don't need values. // l.token includes at least up to the first equality sign. idx := strings.IndexByte(l.token, '=') var key, value string if idx < 0 { // Key with no value and no equality sign key = l.token } else if idx == 0 { return &ParseError{file: l.token, err: "bad SVCB key", lex: l} } else { key, value = l.token[:idx], l.token[idx+1:] if value == "" { // We have a key and an equality sign. Maybe we have nothing // after "=" or we have a double quote. l, _ = c.Next() if l.value == zQuote { // Only needed when value ends with double quotes. // Any value starting with zQuote ends with it. canHaveNextKey = false l, _ = c.Next() switch l.value { case zString: // We have a value in double quotes. value = l.token l, _ = c.Next() if l.value != zQuote { return &ParseError{file: l.token, err: "SVCB unterminated value", lex: l} } case zQuote: // There's nothing in double quotes. default: return &ParseError{file: l.token, err: "bad SVCB value", lex: l} } } } } kv := makeSVCBKeyValue(svcbStringToKey(key)) if kv == nil { return &ParseError{file: l.token, err: "bad SVCB key", lex: l} } if err := kv.parse(value); err != nil { return &ParseError{file: l.token, wrappedErr: err, lex: l} } xs = append(xs, kv) case zQuote: return &ParseError{file: l.token, err: "SVCB key can't contain double quotes", lex: l} case zBlank: canHaveNextKey = true default: return &ParseError{file: l.token, err: "bad SVCB values", lex: l} } l, _ = c.Next() } // "In AliasMode, records SHOULD NOT include any SvcParams, and recipients MUST // ignore any SvcParams that are present." // However, we don't check rr.Priority == 0 && len(xs) > 0 here // It is the responsibility of the user of the library to check this. // This is to encourage the fixing of the source of this error. rr.Value = xs return nil } // makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys. func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue { switch key { case SVCB_MANDATORY: return new(SVCBMandatory) case SVCB_ALPN: return new(SVCBAlpn) case SVCB_NO_DEFAULT_ALPN: return new(SVCBNoDefaultAlpn) case SVCB_PORT: return new(SVCBPort) case SVCB_IPV4HINT: return new(SVCBIPv4Hint) case SVCB_ECHCONFIG: return new(SVCBECHConfig) case SVCB_IPV6HINT: return new(SVCBIPv6Hint) case SVCB_DOHPATH: return new(SVCBDoHPath) case SVCB_OHTTP: return new(SVCBOhttp) case svcb_RESERVED: return nil default: e := new(SVCBLocal) e.KeyCode = key return e } } // SVCB RR. See RFC 9460. type SVCB struct { Hdr RR_Header Priority uint16 // If zero, Value must be empty or discarded by the user of this library Target string `dns:"domain-name"` Value []SVCBKeyValue `dns:"pairs"` } // HTTPS RR. See RFC 9460. Everything valid for SVCB applies to HTTPS as well. // Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols. type HTTPS struct { SVCB } func (rr *HTTPS) String() string { return rr.SVCB.String() } func (rr *HTTPS) parse(c *zlexer, o string) *ParseError { return rr.SVCB.parse(c, o) } // SVCBKeyValue defines a key=value pair for the SVCB RR type. // An SVCB RR can have multiple SVCBKeyValues appended to it. type SVCBKeyValue interface { Key() SVCBKey // Key returns the numerical key code. pack() ([]byte, error) // pack returns the encoded value. unpack([]byte) error // unpack sets the value. String() string // String returns the string representation of the value. parse(string) error // parse sets the value to the given string representation of the value. copy() SVCBKeyValue // copy returns a deep-copy of the pair. len() int // len returns the length of value in the wire format. } // SVCBMandatory pair adds to required keys that must be interpreted for the RR // to be functional. If ignored, the whole RRSet must be ignored. // "port" and "no-default-alpn" are mandatory by default if present, // so they shouldn't be included here. // // It is incumbent upon the user of this library to reject the RRSet if // or avoid constructing such an RRSet that: // - "mandatory" is included as one of the keys of mandatory // - no key is listed multiple times in mandatory // - all keys listed in mandatory are present // - escape sequences are not used in mandatory // - mandatory, when present, lists at least one key // // Basic use pattern for creating a mandatory option: // // s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}} // e := new(dns.SVCBMandatory) // e.Code = []uint16{dns.SVCB_ALPN} // s.Value = append(s.Value, e) // t := new(dns.SVCBAlpn) // t.Alpn = []string{"xmpp-client"} // s.Value = append(s.Value, t) type SVCBMandatory struct { Code []SVCBKey } func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY } func (s *SVCBMandatory) String() string { str := make([]string, len(s.Code)) for i, e := range s.Code { str[i] = e.String() } return strings.Join(str, ",") } func (s *SVCBMandatory) pack() ([]byte, error) { codes := cloneSlice(s.Code) sort.Slice(codes, func(i, j int) bool { return codes[i] < codes[j] }) b := make([]byte, 2*len(codes)) for i, e := range codes { binary.BigEndian.PutUint16(b[2*i:], uint16(e)) } return b, nil } func (s *SVCBMandatory) unpack(b []byte) error { if len(b)%2 != 0 { return errors.New("dns: svcbmandatory: value length is not a multiple of 2") } codes := make([]SVCBKey, 0, len(b)/2) for i := 0; i < len(b); i += 2 { // We assume strictly increasing order. codes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:]))) } s.Code = codes return nil } func (s *SVCBMandatory) parse(b string) error { codes := make([]SVCBKey, 0, strings.Count(b, ",")+1) for len(b) > 0 { var key string key, b, _ = strings.Cut(b, ",") codes = append(codes, svcbStringToKey(key)) } s.Code = codes return nil } func (s *SVCBMandatory) len() int { return 2 * len(s.Code) } func (s *SVCBMandatory) copy() SVCBKeyValue { return &SVCBMandatory{cloneSlice(s.Code)} } // SVCBAlpn pair is used to list supported connection protocols. // The user of this library must ensure that at least one protocol is listed when alpn is present. // Protocol IDs can be found at: // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids // Basic use pattern for creating an alpn option: // // h := new(dns.HTTPS) // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} // e := new(dns.SVCBAlpn) // e.Alpn = []string{"h2", "http/1.1"} // h.Value = append(h.Value, e) type SVCBAlpn struct { Alpn []string } func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN } func (s *SVCBAlpn) String() string { // An ALPN value is a comma-separated list of values, each of which can be // an arbitrary binary value. In order to allow parsing, the comma and // backslash characters are themselves escaped. // // However, this escaping is done in addition to the normal escaping which // happens in zone files, meaning that these values must be // double-escaped. This looks terrible, so if you see a never-ending // sequence of backslash in a zone file this may be why. // // https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1 var str strings.Builder for i, alpn := range s.Alpn { // 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 str.Grow(4*len(alpn) + 1) if i > 0 { str.WriteByte(',') } for j := 0; j < len(alpn); j++ { e := alpn[j] if ' ' > e || e > '~' { str.WriteString(escapeByte(e)) continue } switch e { // We escape a few characters which may confuse humans or parsers. case '"', ';', ' ': str.WriteByte('\\') str.WriteByte(e) // The comma and backslash characters themselves must be // doubly-escaped. We use `\\` for the first backslash and // the escaped numeric value for the other value. We especially // don't want a comma in the output. case ',': str.WriteString(`\\\044`) case '\\': str.WriteString(`\\\092`) default: str.WriteByte(e) } } } return str.String() } func (s *SVCBAlpn) pack() ([]byte, error) { // Liberally estimate the size of an alpn as 10 octets b := make([]byte, 0, 10*len(s.Alpn)) for _, e := range s.Alpn { if e == "" { return nil, errors.New("dns: svcbalpn: empty alpn-id") } if len(e) > 255 { return nil, errors.New("dns: svcbalpn: alpn-id too long") } b = append(b, byte(len(e))) b = append(b, e...) } return b, nil } func (s *SVCBAlpn) unpack(b []byte) error { // Estimate the size of the smallest alpn as 4 bytes alpn := make([]string, 0, len(b)/4) for i := 0; i < len(b); { length := int(b[i]) i++ if i+length > len(b) { return errors.New("dns: svcbalpn: alpn array overflowing") } alpn = append(alpn, string(b[i:i+length])) i += length } s.Alpn = alpn return nil } func (s *SVCBAlpn) parse(b string) error { if len(b) == 0 { s.Alpn = []string{} return nil } alpn := []string{} a := []byte{} for p := 0; p < len(b); { c, q := nextByte(b, p) if q == 0 { return errors.New("dns: svcbalpn: unterminated escape") } p += q // If we find a comma, we have finished reading an alpn. if c == ',' { if len(a) == 0 { return errors.New("dns: svcbalpn: empty protocol identifier") } alpn = append(alpn, string(a)) a = []byte{} continue } // If it's a backslash, we need to handle a comma-separated list. if c == '\\' { dc, dq := nextByte(b, p) if dq == 0 { return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list") } if dc != '\\' && dc != ',' { return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list") } p += dq c = dc } a = append(a, c) } // Add the final alpn. if len(a) == 0 { return errors.New("dns: svcbalpn: last protocol identifier empty") } s.Alpn = append(alpn, string(a)) return nil } func (s *SVCBAlpn) len() int { var l int for _, e := range s.Alpn { l += 1 + len(e) } return l } func (s *SVCBAlpn) copy() SVCBKeyValue { return &SVCBAlpn{cloneSlice(s.Alpn)} } // SVCBNoDefaultAlpn pair signifies no support for default connection protocols. // Should be used in conjunction with alpn. // Basic use pattern for creating a no-default-alpn option: // // s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}} // t := new(dns.SVCBAlpn) // t.Alpn = []string{"xmpp-client"} // s.Value = append(s.Value, t) // e := new(dns.SVCBNoDefaultAlpn) // s.Value = append(s.Value, e) type SVCBNoDefaultAlpn struct{} func (*SVCBNoDefaultAlpn) Key() SVCBKey { return SVCB_NO_DEFAULT_ALPN } func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue { return &SVCBNoDefaultAlpn{} } func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil } func (*SVCBNoDefaultAlpn) String() string { return "" } func (*SVCBNoDefaultAlpn) len() int { return 0 } func (*SVCBNoDefaultAlpn) unpack(b []byte) error { if len(b) != 0 { return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value") } return nil } func (*SVCBNoDefaultAlpn) parse(b string) error { if b != "" { return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value") } return nil } // SVCBPort pair defines the port for connection. // Basic use pattern for creating a port option: // // s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}} // e := new(dns.SVCBPort) // e.Port = 80 // s.Value = append(s.Value, e) type SVCBPort struct { Port uint16 } func (*SVCBPort) Key() SVCBKey { return SVCB_PORT } func (*SVCBPort) len() int { return 2 } func (s *SVCBPort) String() string { return strconv.FormatUint(uint64(s.Port), 10) } func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} } func (s *SVCBPort) unpack(b []byte) error { if len(b) != 2 { return errors.New("dns: svcbport: port length is not exactly 2 octets") } s.Port = binary.BigEndian.Uint16(b) return nil } func (s *SVCBPort) pack() ([]byte, error) { b := make([]byte, 2) binary.BigEndian.PutUint16(b, s.Port) return b, nil } func (s *SVCBPort) parse(b string) error { port, err := strconv.ParseUint(b, 10, 16) if err != nil { return errors.New("dns: svcbport: port out of range") } s.Port = uint16(port) return nil } // SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections // if A and AAAA record responses for SVCB's Target domain haven't been received. // In that case, optionally, A and AAAA requests can be made, after which the connection // to the hinted IP address may be terminated and a new connection may be opened. // Basic use pattern for creating an ipv4hint option: // // h := new(dns.HTTPS) // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} // e := new(dns.SVCBIPv4Hint) // e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()} // // Or // // e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()} // h.Value = append(h.Value, e) type SVCBIPv4Hint struct { Hint []net.IP } func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT } func (s *SVCBIPv4Hint) len() int { return 4 * len(s.Hint) } func (s *SVCBIPv4Hint) pack() ([]byte, error) { b := make([]byte, 0, 4*len(s.Hint)) for _, e := range s.Hint { x := e.To4() if x == nil { return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6") } b = append(b, x...) } return b, nil } func (s *SVCBIPv4Hint) unpack(b []byte) error { if len(b) == 0 || len(b)%4 != 0 { return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4") } b = cloneSlice(b) x := make([]net.IP, 0, len(b)/4) for i := 0; i < len(b); i += 4 { x = append(x, net.IP(b[i:i+4])) } s.Hint = x return nil } func (s *SVCBIPv4Hint) String() string { str := make([]string, len(s.Hint)) for i, e := range s.Hint { x := e.To4() if x == nil { return "" } str[i] = x.String() } return strings.Join(str, ",") } func (s *SVCBIPv4Hint) parse(b string) error { if b == "" { return errors.New("dns: svcbipv4hint: empty hint") } if strings.Contains(b, ":") { return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6") } hint := make([]net.IP, 0, strings.Count(b, ",")+1) for len(b) > 0 { var e string e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e).To4() if ip == nil { return errors.New("dns: svcbipv4hint: bad ip") } hint = append(hint, ip) } s.Hint = hint return nil } func (s *SVCBIPv4Hint) copy() SVCBKeyValue { hint := make([]net.IP, len(s.Hint)) for i, ip := range s.Hint { hint[i] = cloneSlice(ip) } return &SVCBIPv4Hint{Hint: hint} } // SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx]. // Basic use pattern for creating an ech option: // // h := new(dns.HTTPS) // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} // e := new(dns.SVCBECHConfig) // e.ECH = []byte{0xfe, 0x08, ...} // h.Value = append(h.Value, e) type SVCBECHConfig struct { ECH []byte // Specifically ECHConfigList including the redundant length prefix } func (*SVCBECHConfig) Key() SVCBKey { return SVCB_ECHCONFIG } func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) } func (s *SVCBECHConfig) len() int { return len(s.ECH) } func (s *SVCBECHConfig) pack() ([]byte, error) { return cloneSlice(s.ECH), nil } func (s *SVCBECHConfig) copy() SVCBKeyValue { return &SVCBECHConfig{cloneSlice(s.ECH)} } func (s *SVCBECHConfig) unpack(b []byte) error { s.ECH = cloneSlice(b) return nil } func (s *SVCBECHConfig) parse(b string) error { x, err := fromBase64([]byte(b)) if err != nil { return errors.New("dns: svcbech: bad base64 ech") } s.ECH = x return nil } // SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections // if A and AAAA record responses for SVCB's Target domain haven't been received. // In that case, optionally, A and AAAA requests can be made, after which the // connection to the hinted IP address may be terminated and a new connection may be opened. // Basic use pattern for creating an ipv6hint option: // // h := new(dns.HTTPS) // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} // e := new(dns.SVCBIPv6Hint) // e.Hint = []net.IP{net.ParseIP("2001:db8::1")} // h.Value = append(h.Value, e) type SVCBIPv6Hint struct { Hint []net.IP } func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT } func (s *SVCBIPv6Hint) len() int { return 16 * len(s.Hint) } func (s *SVCBIPv6Hint) pack() ([]byte, error) { b := make([]byte, 0, 16*len(s.Hint)) for _, e := range s.Hint { if len(e) != net.IPv6len || e.To4() != nil { return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4") } b = append(b, e...) } return b, nil } func (s *SVCBIPv6Hint) unpack(b []byte) error { if len(b) == 0 || len(b)%16 != 0 { return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16") } b = cloneSlice(b) x := make([]net.IP, 0, len(b)/16) for i := 0; i < len(b); i += 16 { ip := net.IP(b[i : i+16]) if ip.To4() != nil { return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4") } x = append(x, ip) } s.Hint = x return nil } func (s *SVCBIPv6Hint) String() string { str := make([]string, len(s.Hint)) for i, e := range s.Hint { if x := e.To4(); x != nil { return "" } str[i] = e.String() } return strings.Join(str, ",") } func (s *SVCBIPv6Hint) parse(b string) error { if b == "" { return errors.New("dns: svcbipv6hint: empty hint") } hint := make([]net.IP, 0, strings.Count(b, ",")+1) for len(b) > 0 { var e string e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e) if ip == nil { return errors.New("dns: svcbipv6hint: bad ip") } if ip.To4() != nil { return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6") } hint = append(hint, ip) } s.Hint = hint return nil } func (s *SVCBIPv6Hint) copy() SVCBKeyValue { hint := make([]net.IP, len(s.Hint)) for i, ip := range s.Hint { hint[i] = cloneSlice(ip) } return &SVCBIPv6Hint{Hint: hint} } // SVCBDoHPath pair is used to indicate the URI template that the // clients may use to construct a DNS over HTTPS URI. // // See RFC 9461 (https://datatracker.ietf.org/doc/html/rfc9461) // and RFC 9462 (https://datatracker.ietf.org/doc/html/rfc9462). // // A basic example of using the dohpath option together with the alpn // option to indicate support for DNS over HTTPS on a certain path: // // s := new(dns.SVCB) // s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET} // e := new(dns.SVCBAlpn) // e.Alpn = []string{"h2", "h3"} // p := new(dns.SVCBDoHPath) // p.Template = "/dns-query{?dns}" // s.Value = append(s.Value, e, p) // // The parsing currently doesn't validate that Template is a valid // RFC 6570 URI template. type SVCBDoHPath struct { Template string } func (*SVCBDoHPath) Key() SVCBKey { return SVCB_DOHPATH } func (s *SVCBDoHPath) String() string { return svcbParamToStr([]byte(s.Template)) } func (s *SVCBDoHPath) len() int { return len(s.Template) } func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil } func (s *SVCBDoHPath) unpack(b []byte) error { s.Template = string(b) return nil } func (s *SVCBDoHPath) parse(b string) error { template, err := svcbParseParam(b) if err != nil { return fmt.Errorf("dns: svcbdohpath: %w", err) } s.Template = string(template) return nil } func (s *SVCBDoHPath) copy() SVCBKeyValue { return &SVCBDoHPath{ Template: s.Template, } } // The "ohttp" SvcParamKey is used to indicate that a service described in a SVCB RR // can be accessed as a target using an associated gateway. // Both the presentation and wire-format values for the "ohttp" parameter MUST be empty. // // See RFC 9460 (https://datatracker.ietf.org/doc/html/rfc9460/) // and RFC 9230 (https://datatracker.ietf.org/doc/html/rfc9230/) // // A basic example of using the dohpath option together with the alpn // option to indicate support for DNS over HTTPS on a certain path: // // s := new(dns.SVCB) // s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET} // e := new(dns.SVCBAlpn) // e.Alpn = []string{"h2", "h3"} // p := new(dns.SVCBOhttp) // s.Value = append(s.Value, e, p) type SVCBOhttp struct{} func (*SVCBOhttp) Key() SVCBKey { return SVCB_OHTTP } func (*SVCBOhttp) copy() SVCBKeyValue { return &SVCBOhttp{} } func (*SVCBOhttp) pack() ([]byte, error) { return []byte{}, nil } func (*SVCBOhttp) String() string { return "" } func (*SVCBOhttp) len() int { return 0 } func (*SVCBOhttp) unpack(b []byte) error { if len(b) != 0 { return errors.New("dns: svcbotthp: svcbotthp must have no value") } return nil } func (*SVCBOhttp) parse(b string) error { if b != "" { return errors.New("dns: svcbotthp: svcbotthp must have no value") } return nil } // SVCBLocal pair is intended for experimental/private use. The key is recommended // to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER]. // Basic use pattern for creating a keyNNNNN option: // // h := new(dns.HTTPS) // h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} // e := new(dns.SVCBLocal) // e.KeyCode = 65400 // e.Data = []byte("abc") // h.Value = append(h.Value, e) type SVCBLocal struct { KeyCode SVCBKey // Never 65535 or any assigned keys. Data []byte // All byte sequences are allowed. } func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode } func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) } func (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil } func (s *SVCBLocal) len() int { return len(s.Data) } func (s *SVCBLocal) unpack(b []byte) error { s.Data = cloneSlice(b) return nil } func (s *SVCBLocal) parse(b string) error { data, err := svcbParseParam(b) if err != nil { return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", err) } s.Data = data return nil } func (s *SVCBLocal) copy() SVCBKeyValue { return &SVCBLocal{s.KeyCode, cloneSlice(s.Data)} } func (rr *SVCB) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + " " + sprintName(rr.Target) for _, e := range rr.Value { s += " " + e.Key().String() + "=\"" + e.String() + "\"" } return s } // areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their // copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function. func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool { a = cloneSlice(a) b = cloneSlice(b) sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() }) sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() }) for i, e := range a { if e.Key() != b[i].Key() { return false } b1, err1 := e.pack() b2, err2 := b[i].pack() if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) { return false } } return true } // svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string. func svcbParamToStr(s []byte) string { var str strings.Builder str.Grow(4 * len(s)) for _, e := range s { if ' ' <= e && e <= '~' { switch e { case '"', ';', ' ', '\\': str.WriteByte('\\') str.WriteByte(e) default: str.WriteByte(e) } } else { str.WriteString(escapeByte(e)) } } return str.String() } // svcbParseParam parses a DNS presentation-format string into an SVCB parameter value. func svcbParseParam(b string) ([]byte, error) { data := make([]byte, 0, len(b)) for i := 0; i < len(b); { if b[i] != '\\' { data = append(data, b[i]) i++ continue } if i+1 == len(b) { return nil, errors.New("escape unterminated") } if isDigit(b[i+1]) { if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) { a, err := strconv.ParseUint(b[i+1:i+4], 10, 8) if err == nil { i += 4 data = append(data, byte(a)) continue } } return nil, errors.New("bad escaped octet") } else { data = append(data, b[i+1]) i += 2 } } return data, nil } golang-github-miekg-dns-1.1.64/svcb_test.go000066400000000000000000000076601476742671700205670ustar00rootroot00000000000000package dns import ( "testing" ) // This tests everything valid about SVCB but parsing. // Parsing tests belong to parse_test.go. func TestSVCB(t *testing.T) { svcbs := []struct { key string data string }{ {`mandatory`, `alpn,key65000`}, {`alpn`, `h2,h2c`}, {`port`, `499`}, {`ipv4hint`, `3.4.3.2,1.1.1.1`}, {`no-default-alpn`, ``}, {`ipv6hint`, `1::4:4:4:4,1::3:3:3:3`}, {`ech`, `YUdWc2JHOD0=`}, {`dohpath`, `/dns-query{?dns}`}, {`key65000`, `4\ 3`}, {`key65001`, `\"\ `}, {`key65002`, ``}, {`key65003`, `=\"\"`}, {`key65004`, `\254\ \ \030\000`}, {`ohttp`, ``}, } for _, o := range svcbs { keyCode := svcbStringToKey(o.key) kv := makeSVCBKeyValue(keyCode) if kv == nil { t.Error("failed to parse svc key: ", o.key) continue } if kv.Key() != keyCode { t.Error("key constant is not in sync: ", keyCode) continue } err := kv.parse(o.data) if err != nil { t.Error("failed to parse svc pair: ", o.key) continue } b, err := kv.pack() if err != nil { t.Error("failed to pack value of svc pair: ", o.key, err) continue } if len(b) != int(kv.len()) { t.Errorf("expected packed svc value %s to be of length %d but got %d", o.key, int(kv.len()), len(b)) } err = kv.unpack(b) if err != nil { t.Error("failed to unpack value of svc pair: ", o.key, err) continue } if str := kv.String(); str != o.data { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", o.key, o.data, str) } } } func TestDecodeBadSVCB(t *testing.T) { svcbs := []struct { key SVCBKey data []byte }{ { key: SVCB_ALPN, data: []byte{3, 0, 0}, // There aren't three octets after 3 }, { key: SVCB_NO_DEFAULT_ALPN, data: []byte{0}, }, { key: SVCB_PORT, data: []byte{}, }, { key: SVCB_IPV4HINT, data: []byte{0, 0, 0}, }, { key: SVCB_IPV6HINT, data: []byte{0, 0, 0}, }, { key: SVCB_OHTTP, data: []byte{0}, }, } for _, o := range svcbs { err := makeSVCBKeyValue(SVCBKey(o.key)).unpack(o.data) if err == nil { t.Error("accepted invalid svc value with key ", SVCBKey(o.key).String()) } } } func TestPresentationSVCBAlpn(t *testing.T) { tests := map[string]string{ "h2": "h2", "http": "http", "\xfa": `\250`, "some\"other,chars": `some\"other\\\044chars`, } for input, want := range tests { e := new(SVCBAlpn) e.Alpn = []string{input} if e.String() != want { t.Errorf("improper conversion with String(), wanted %v got %v", want, e.String()) } } } func TestSVCBAlpn(t *testing.T) { tests := map[string][]string{ `. 1 IN SVCB 10 one.test. alpn=h2`: {"h2"}, `. 2 IN SVCB 20 two.test. alpn=h2,h3-19`: {"h2", "h3-19"}, `. 3 IN SVCB 30 three.test. alpn="f\\\\oo\\,bar,h2"`: {`f\oo,bar`, "h2"}, `. 4 IN SVCB 40 four.test. alpn="part1,part2,part3\\,part4\\\\"`: {"part1", "part2", `part3,part4\`}, `. 5 IN SVCB 50 five.test. alpn=part1\,\p\a\r\t2\044part3\092,part4\092\\`: {"part1", "part2", `part3,part4\`}, } for s, v := range tests { rr, err := NewRR(s) if err != nil { t.Error("failed to parse RR: ", err) continue } alpn := rr.(*SVCB).Value[0].(*SVCBAlpn).Alpn if len(v) != len(alpn) { t.Fatalf("parsing alpn failed, wanted %v got %v", v, alpn) } for i := range v { if v[i] != alpn[i] { t.Fatalf("parsing alpn failed, wanted %v got %v", v, alpn) } } } } func TestCompareSVCB(t *testing.T) { val1 := []SVCBKeyValue{ &SVCBPort{ Port: 117, }, &SVCBAlpn{ Alpn: []string{"h2", "h3"}, }, } val2 := []SVCBKeyValue{ &SVCBAlpn{ Alpn: []string{"h2", "h3"}, }, &SVCBPort{ Port: 117, }, } if !areSVCBPairArraysEqual(val1, val2) { t.Error("svcb pairs were compared without sorting") } if val1[0].Key() != SVCB_PORT || val2[0].Key() != SVCB_ALPN { t.Error("original svcb pairs were reordered during comparison") } } golang-github-miekg-dns-1.1.64/tlsa.go000066400000000000000000000021531476742671700175260ustar00rootroot00000000000000package dns import ( "crypto/x509" "net" "strconv" ) // Sign creates a TLSA record from an SSL certificate. func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) { r.Hdr.Rrtype = TypeTLSA r.Usage = uint8(usage) r.Selector = uint8(selector) r.MatchingType = uint8(matchingType) r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert) return err } // Verify verifies a TLSA record against an SSL certificate. If it is OK // a nil error is returned. func (r *TLSA) Verify(cert *x509.Certificate) error { c, err := CertificateToDANE(r.Selector, r.MatchingType, cert) if err != nil { return err // Not also ErrSig? } if r.Certificate == c { return nil } return ErrSig // ErrSig, really? } // TLSAName returns the ownername of a TLSA resource record as per the // rules specified in RFC 6698, Section 3. func TLSAName(name, service, network string) (string, error) { if !IsFqdn(name) { return "", ErrFqdn } p, err := net.LookupPort(network, service) if err != nil { return "", err } return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil } golang-github-miekg-dns-1.1.64/tmpdir_darwin_test.go000066400000000000000000000014471476742671700224720ustar00rootroot00000000000000//go:build darwin package dns import ( "os" "path/filepath" "strings" "testing" ) // tempDir creates a temporary directory for tests and returns a file path as // a result of concatenation of said temporary directory path and provided filename. // The reason for this is to work around some limitations in socket file name // lengths on darwin. // // Ref: // - https://github.com/golang/go/blob/go1.20.2/src/syscall/ztypes_darwin_arm64.go#L178 // - https://github.com/golang/go/blob/go1.20.2/src/syscall/ztypes_linux_arm64.go#L175 func tempFile(t *testing.T, filename string) string { t.Helper() dir, err := os.MkdirTemp("", strings.ReplaceAll(t.Name(), string(filepath.Separator), "-")) if err != nil { t.Fatalf("failed to create temp dir: %v", err) } return filepath.Join(dir, filename) } golang-github-miekg-dns-1.1.64/tmpdir_test.go000066400000000000000000000005361476742671700211240ustar00rootroot00000000000000//go:build !darwin package dns import ( "path/filepath" "testing" ) // tempDir creates a temporary directory for tests and returns a file path as // a result of concatenation of said temporary directory path and provided filename. func tempFile(t *testing.T, filename string) string { t.Helper() return filepath.Join(t.TempDir(), filename) } golang-github-miekg-dns-1.1.64/tools.go000066400000000000000000000005271476742671700177260ustar00rootroot00000000000000//go:build tools // +build tools // We include our tool dependencies for `go generate` here to ensure they're // properly tracked by the go tool. See the Go Wiki for the rationale behind this: // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module. package dns import _ "golang.org/x/tools/go/packages" golang-github-miekg-dns-1.1.64/tsig.go000066400000000000000000000273741476742671700175450ustar00rootroot00000000000000package dns import ( "crypto/hmac" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/binary" "encoding/hex" "hash" "strconv" "strings" "time" ) // HMAC hashing codes. These are transmitted as domain names. const ( HmacSHA1 = "hmac-sha1." HmacSHA224 = "hmac-sha224." HmacSHA256 = "hmac-sha256." HmacSHA384 = "hmac-sha384." HmacSHA512 = "hmac-sha512." HmacMD5 = "hmac-md5.sig-alg.reg.int." // Deprecated: HmacMD5 is no longer supported. ) // TsigProvider provides the API to plug-in a custom TSIG implementation. type TsigProvider interface { // Generate is passed the DNS message to be signed and the partial TSIG RR. It returns the signature and nil, otherwise an error. Generate(msg []byte, t *TSIG) ([]byte, error) // 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. Verify(msg []byte, t *TSIG) error } type tsigHMACProvider string func (key tsigHMACProvider) Generate(msg []byte, t *TSIG) ([]byte, error) { // If we barf here, the caller is to blame rawsecret, err := fromBase64([]byte(key)) if err != nil { return nil, err } var h hash.Hash switch CanonicalName(t.Algorithm) { case HmacSHA1: h = hmac.New(sha1.New, rawsecret) case HmacSHA224: h = hmac.New(sha256.New224, rawsecret) case HmacSHA256: h = hmac.New(sha256.New, rawsecret) case HmacSHA384: h = hmac.New(sha512.New384, rawsecret) case HmacSHA512: h = hmac.New(sha512.New, rawsecret) default: return nil, ErrKeyAlg } h.Write(msg) return h.Sum(nil), nil } func (key tsigHMACProvider) Verify(msg []byte, t *TSIG) error { b, err := key.Generate(msg, t) if err != nil { return err } mac, err := hex.DecodeString(t.MAC) if err != nil { return err } if !hmac.Equal(b, mac) { return ErrSig } return nil } type tsigSecretProvider map[string]string func (ts tsigSecretProvider) Generate(msg []byte, t *TSIG) ([]byte, error) { key, ok := ts[t.Hdr.Name] if !ok { return nil, ErrSecret } return tsigHMACProvider(key).Generate(msg, t) } func (ts tsigSecretProvider) Verify(msg []byte, t *TSIG) error { key, ok := ts[t.Hdr.Name] if !ok { return ErrSecret } return tsigHMACProvider(key).Verify(msg, t) } // TSIG is the RR the holds the transaction signature of a message. // See RFC 2845 and RFC 4635. type TSIG struct { Hdr RR_Header Algorithm string `dns:"domain-name"` TimeSigned uint64 `dns:"uint48"` Fudge uint16 MACSize uint16 MAC string `dns:"size-hex:MACSize"` OrigId uint16 Error uint16 OtherLen uint16 OtherData string `dns:"size-hex:OtherLen"` } // TSIG has no official presentation format, but this will suffice. func (rr *TSIG) String() string { s := "\n;; TSIG PSEUDOSECTION:\n; " // add another semi-colon to signify TSIG does not have a presentation format s += rr.Hdr.String() + " " + rr.Algorithm + " " + tsigTimeToString(rr.TimeSigned) + " " + strconv.Itoa(int(rr.Fudge)) + " " + strconv.Itoa(int(rr.MACSize)) + " " + strings.ToUpper(rr.MAC) + " " + strconv.Itoa(int(rr.OrigId)) + " " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR " " + strconv.Itoa(int(rr.OtherLen)) + " " + rr.OtherData return s } func (*TSIG) parse(c *zlexer, origin string) *ParseError { return &ParseError{err: "TSIG records do not have a presentation format"} } // The following values must be put in wireformat, so that the MAC can be calculated. // RFC 2845, section 3.4.2. TSIG Variables. type tsigWireFmt struct { // From RR_Header Name string `dns:"domain-name"` Class uint16 Ttl uint32 // Rdata of the TSIG Algorithm string `dns:"domain-name"` TimeSigned uint64 `dns:"uint48"` Fudge uint16 // MACSize, MAC and OrigId excluded Error uint16 OtherLen uint16 OtherData string `dns:"size-hex:OtherLen"` } // If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC type macWireFmt struct { MACSize uint16 MAC string `dns:"size-hex:MACSize"` } // 3.3. Time values used in TSIG calculations type timerWireFmt struct { TimeSigned uint64 `dns:"uint48"` Fudge uint16 } // TsigGenerate fills out the TSIG record attached to the message. // The message should contain a "stub" TSIG RR with the algorithm, key name // (owner name of the RR), time fudge (defaults to 300 seconds) and the current // time The TSIG MAC is saved in that Tsig RR. When TsigGenerate is called for // the first time requestMAC should be set to the empty string and timersOnly to // false. func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) { return TsigGenerateWithProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly) } // TsigGenerateWithProvider is similar to TsigGenerate, but allows for a custom TsigProvider. func TsigGenerateWithProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) { if m.IsTsig() == nil { panic("dns: TSIG not last RR in additional") } rr := m.Extra[len(m.Extra)-1].(*TSIG) m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg mbuf, err := m.Pack() if err != nil { return nil, "", err } buf, err := tsigBuffer(mbuf, rr, requestMAC, timersOnly) if err != nil { return nil, "", err } t := new(TSIG) // Copy all TSIG fields except MAC, its size, and time signed which are filled when signing. *t = *rr t.TimeSigned = 0 t.MAC = "" t.MACSize = 0 // Sign unless there is a key or MAC validation error (RFC 8945 5.3.2) if rr.Error != RcodeBadKey && rr.Error != RcodeBadSig { mac, err := provider.Generate(buf, rr) if err != nil { return nil, "", err } t.TimeSigned = rr.TimeSigned t.MAC = hex.EncodeToString(mac) t.MACSize = uint16(len(t.MAC) / 2) // Size is half! } tbuf := make([]byte, Len(t)) off, err := PackRR(t, tbuf, 0, nil, false) if err != nil { return nil, "", err } mbuf = append(mbuf, tbuf[:off]...) // Update the ArCount directly in the buffer. binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1)) return mbuf, t.MAC, nil } // TsigVerify verifies the TSIG on a message. If the signature does not // validate the returned error contains the cause. If the signature is OK, the // error is nil. func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { return tsigVerify(msg, tsigHMACProvider(secret), requestMAC, timersOnly, uint64(time.Now().Unix())) } // TsigVerifyWithProvider is similar to TsigVerify, but allows for a custom TsigProvider. func TsigVerifyWithProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error { return tsigVerify(msg, provider, requestMAC, timersOnly, uint64(time.Now().Unix())) } // actual implementation of TsigVerify, taking the current time ('now') as a parameter for the convenience of tests. func tsigVerify(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool, now uint64) error { // Strip the TSIG from the incoming msg stripped, tsig, err := stripTsig(msg) if err != nil { return err } buf, err := tsigBuffer(stripped, tsig, requestMAC, timersOnly) if err != nil { return err } if err := provider.Verify(buf, tsig); err != nil { return err } // Fudge factor works both ways. A message can arrive before it was signed because // of clock skew. // We check this after verifying the signature, following draft-ietf-dnsop-rfc2845bis // instead of RFC2845, in order to prevent a security vulnerability as reported in CVE-2017-3142/3143. ti := now - tsig.TimeSigned if now < tsig.TimeSigned { ti = tsig.TimeSigned - now } if uint64(tsig.Fudge) < ti { return ErrTime } return nil } // Create a wiredata buffer for the MAC calculation. func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) ([]byte, error) { var buf []byte if rr.TimeSigned == 0 { rr.TimeSigned = uint64(time.Now().Unix()) } if rr.Fudge == 0 { rr.Fudge = 300 // Standard (RFC) default. } // Replace message ID in header with original ID from TSIG binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId) if requestMAC != "" { m := new(macWireFmt) m.MACSize = uint16(len(requestMAC) / 2) m.MAC = requestMAC buf = make([]byte, len(requestMAC)) // long enough n, err := packMacWire(m, buf) if err != nil { return nil, err } buf = buf[:n] } tsigvar := make([]byte, DefaultMsgSize) if timersOnly { tsig := new(timerWireFmt) tsig.TimeSigned = rr.TimeSigned tsig.Fudge = rr.Fudge n, err := packTimerWire(tsig, tsigvar) if err != nil { return nil, err } tsigvar = tsigvar[:n] } else { tsig := new(tsigWireFmt) tsig.Name = CanonicalName(rr.Hdr.Name) tsig.Class = ClassANY tsig.Ttl = rr.Hdr.Ttl tsig.Algorithm = CanonicalName(rr.Algorithm) tsig.TimeSigned = rr.TimeSigned tsig.Fudge = rr.Fudge tsig.Error = rr.Error tsig.OtherLen = rr.OtherLen tsig.OtherData = rr.OtherData n, err := packTsigWire(tsig, tsigvar) if err != nil { return nil, err } tsigvar = tsigvar[:n] } if requestMAC != "" { x := append(buf, msgbuf...) buf = append(x, tsigvar...) } else { buf = append(msgbuf, tsigvar...) } return buf, nil } // Strip the TSIG from the raw message. func stripTsig(msg []byte) ([]byte, *TSIG, error) { // Copied from msg.go's Unpack() Header, but modified. var ( dh Header err error ) off, tsigoff := 0, 0 if dh, off, err = unpackMsgHdr(msg, off); err != nil { return nil, nil, err } if dh.Arcount == 0 { return nil, nil, ErrNoSig } // Rcode, see msg.go Unpack() if int(dh.Bits&0xF) == RcodeNotAuth { return nil, nil, ErrAuth } for i := 0; i < int(dh.Qdcount); i++ { _, off, err = unpackQuestion(msg, off) if err != nil { return nil, nil, err } } _, off, err = unpackRRslice(int(dh.Ancount), msg, off) if err != nil { return nil, nil, err } _, off, err = unpackRRslice(int(dh.Nscount), msg, off) if err != nil { return nil, nil, err } rr := new(TSIG) var extra RR for i := 0; i < int(dh.Arcount); i++ { tsigoff = off extra, off, err = UnpackRR(msg, off) if err != nil { return nil, nil, err } if extra.Header().Rrtype == TypeTSIG { rr = extra.(*TSIG) // Adjust Arcount. arcount := binary.BigEndian.Uint16(msg[10:]) binary.BigEndian.PutUint16(msg[10:], arcount-1) break } } if rr == nil { return nil, nil, ErrNoSig } return msg[:tsigoff], rr, nil } // Translate the TSIG time signed into a date. There is no // need for RFC1982 calculations as this date is 48 bits. func tsigTimeToString(t uint64) string { ti := time.Unix(int64(t), 0).UTC() return ti.Format("20060102150405") } func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) { // copied from zmsg.go TSIG packing // RR_Header off, err := PackDomainName(tw.Name, msg, 0, nil, false) if err != nil { return off, err } off, err = packUint16(tw.Class, msg, off) if err != nil { return off, err } off, err = packUint32(tw.Ttl, msg, off) if err != nil { return off, err } off, err = PackDomainName(tw.Algorithm, msg, off, nil, false) if err != nil { return off, err } off, err = packUint48(tw.TimeSigned, msg, off) if err != nil { return off, err } off, err = packUint16(tw.Fudge, msg, off) if err != nil { return off, err } off, err = packUint16(tw.Error, msg, off) if err != nil { return off, err } off, err = packUint16(tw.OtherLen, msg, off) if err != nil { return off, err } off, err = packStringHex(tw.OtherData, msg, off) if err != nil { return off, err } return off, nil } func packMacWire(mw *macWireFmt, msg []byte) (int, error) { off, err := packUint16(mw.MACSize, msg, 0) if err != nil { return off, err } off, err = packStringHex(mw.MAC, msg, off) if err != nil { return off, err } return off, nil } func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) { off, err := packUint48(tw.TimeSigned, msg, 0) if err != nil { return off, err } off, err = packUint16(tw.Fudge, msg, off) if err != nil { return off, err } return off, nil } golang-github-miekg-dns-1.1.64/tsig_test.go000066400000000000000000000300241476742671700205660ustar00rootroot00000000000000package dns import ( "encoding/binary" "encoding/hex" "errors" "fmt" "strings" "testing" "time" ) func newTsig(algo string) *Msg { m := new(Msg) m.SetQuestion("example.org.", TypeA) m.SetTsig("example.", algo, 300, time.Now().Unix()) return m } func TestTsig(t *testing.T) { m := newTsig(HmacSHA256) buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } // TSIG accounts for ID substitution. This means if the message ID is // changed by a forwarder, we should still be able to verify the TSIG. m = newTsig(HmacSHA256) buf, _, err = TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } binary.BigEndian.PutUint16(buf[0:2], 42) err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } } func TestTsigCase(t *testing.T) { m := newTsig(strings.ToUpper(HmacSHA256)) buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } } func TestTsigErrorResponse(t *testing.T) { for _, rcode := range []uint16{RcodeBadSig, RcodeBadKey} { m := newTsig(strings.ToUpper(HmacSHA256)) m.IsTsig().Error = rcode buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } err = m.Unpack(buf) if err != nil { t.Fatal(err) } mTsig := m.IsTsig() if mTsig.MAC != "" { t.Error("Expected empty MAC") } if mTsig.MACSize != 0 { t.Error("Expected 0 MACSize") } if mTsig.TimeSigned != 0 { t.Errorf("Expected TimeSigned to be 0, got %v", mTsig.TimeSigned) } } } func TestTsigBadTimeResponse(t *testing.T) { clientTime := uint64(time.Now().Unix()) - 3600 m := newTsig(strings.ToUpper(HmacSHA256)) m.IsTsig().Error = RcodeBadTime m.IsTsig().TimeSigned = clientTime buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) if err != nil { t.Fatal(err) } err = m.Unpack(buf) if err != nil { t.Fatal(err) } mTsig := m.IsTsig() if mTsig.MAC == "" { t.Error("Expected non-empty MAC") } if int(mTsig.MACSize) != len(mTsig.MAC)/2 { t.Errorf("Expected MACSize %v, got %v", len(mTsig.MAC)/2, mTsig.MACSize) } if mTsig.TimeSigned != clientTime { t.Errorf("Expected TimeSigned %v to be retained, got %v", clientTime, mTsig.TimeSigned) } } const ( // A template wire-format DNS message (in hex form) containing a TSIG RR. // Its time signed field will be filled by tests. wireMsg = "c60028000001000000010001076578616d706c6503636f6d00000600010161c00c0001000100000e100004c0000201077465" + "73746b65790000fa00ff00000000003d0b686d61632d73686132353600" + "%012x" + // placeholder for the "time signed" field "012c00208cf23e0081d915478a182edcea7ff48ad102948e6c7ef8e887536957d1fa5616c60000000000" // A secret (in base64 format) with which the TSIG in wireMsg will be validated testSecret = "NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk=" // the 'time signed' field value that would make the TSIG RR valid with testSecret timeSigned uint64 = 1594855491 ) func TestTsigErrors(t *testing.T) { // Helper shortcut to build wire-format test message. // TsigVerify can modify the slice, so we need to create a new one for each test case below. buildMsgData := func(tm uint64) []byte { msgData, err := hex.DecodeString(fmt.Sprintf(wireMsg, tm)) if err != nil { t.Fatal(err) } return msgData } // the signature is valid but 'time signed' is too far from the "current time". if err := tsigVerify(buildMsgData(timeSigned), tsigHMACProvider(testSecret), "", false, timeSigned+301); err != ErrTime { t.Fatalf("expected an error '%v' but got '%v'", ErrTime, err) } if err := tsigVerify(buildMsgData(timeSigned), tsigHMACProvider(testSecret), "", false, timeSigned-301); err != ErrTime { t.Fatalf("expected an error '%v' but got '%v'", ErrTime, err) } // the signature is invalid and 'time signed' is too far. // the signature should be checked first, so we should see ErrSig. if err := tsigVerify(buildMsgData(timeSigned+301), tsigHMACProvider(testSecret), "", false, timeSigned); err != ErrSig { t.Fatalf("expected an error '%v' but got '%v'", ErrSig, err) } // tweak the algorithm name in the wire data, resulting in the "unknown algorithm" error. msgData := buildMsgData(timeSigned) copy(msgData[67:], "bogus") if err := tsigVerify(msgData, tsigHMACProvider(testSecret), "", false, timeSigned); err != ErrKeyAlg { t.Fatalf("expected an error '%v' but got '%v'", ErrKeyAlg, err) } // call TsigVerify with a message that doesn't contain a TSIG msgData, tsig, err := stripTsig(buildMsgData(timeSigned)) if err != nil { t.Fatal(err) } if err := tsigVerify(msgData, tsigHMACProvider(testSecret), "", false, timeSigned); err != ErrNoSig { t.Fatalf("expected an error '%v' but got '%v'", ErrNoSig, err) } // replace the test TSIG with a bogus one with large "other data", which would cause overflow in TsigVerify. // The overflow should be caught without disruption. tsig.OtherData = strings.Repeat("00", 4096) tsig.OtherLen = uint16(len(tsig.OtherData) / 2) msg := new(Msg) if err = msg.Unpack(msgData); err != nil { t.Fatal(err) } msg.Extra = append(msg.Extra, tsig) if msgData, err = msg.Pack(); err != nil { t.Fatal(err) } err = tsigVerify(msgData, tsigHMACProvider(testSecret), "", false, timeSigned) if err == nil || !strings.Contains(err.Error(), "overflow") { t.Errorf("expected error to contain %q, but got %v", "overflow", err) } } // This test exercises some more corner cases for TsigGenerate. func TestTsigGenerate(t *testing.T) { // This is a template TSIG to be used for signing. tsig := TSIG{ Hdr: RR_Header{Name: "testkey.", Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}, Algorithm: HmacSHA256, TimeSigned: timeSigned, Fudge: 300, OrigId: 42, Error: RcodeBadTime, // use a non-0 value to make sure it's indeed used } tests := []struct { desc string // test description requestMAC string // request MAC to be passed to TsigGenerate (arbitrary choice) otherData string // other data specified in the TSIG (arbitrary choice) expectedMAC string // pre-computed expected (correct) MAC in hex form }{ {"with request MAC", "3684c225", "", "c110e3f62694755c10761dc8717462431ee34340b7c9d1eee09449150757c5b1"}, {"no request MAC", "", "", "385449a425c6d52b9bf2c65c0726eefa0ad8084cdaf488f24547e686605b9610"}, {"with other data", "3684c225", "666f6f", "15b91571ca80b3b410a77e2b44f8cc4f35ace22b26020138439dd94803e23b5d"}, } for _, tc := range tests { tc := tc t.Run(tc.desc, func(t *testing.T) { // Build TSIG for signing from the template testTSIG := tsig testTSIG.OtherLen = uint16(len(tc.otherData) / 2) testTSIG.OtherData = tc.otherData req := &Msg{ MsgHdr: MsgHdr{Opcode: OpcodeUpdate}, Question: []Question{{Name: "example.com.", Qtype: TypeSOA, Qclass: ClassINET}}, Extra: []RR{&testTSIG}, } // Call generate, and check the returned MAC against the expected value msgData, mac, err := TsigGenerate(req, testSecret, tc.requestMAC, false) if err != nil { t.Error(err) } if mac != tc.expectedMAC { t.Fatalf("MAC doesn't match: expected '%s', but got '%s'", tc.expectedMAC, mac) } // Retrieve the TSIG to be sent out, confirm the MAC in it _, outTSIG, err := stripTsig(msgData) if err != nil { t.Error(err) } if outTSIG.MAC != tc.expectedMAC { t.Fatalf("MAC doesn't match: expected '%s', but got '%s'", tc.expectedMAC, outTSIG.MAC) } // Confirm other fields of MAC. // RDLENGTH should be valid as stripTsig succeeded, so we exclude it from comparison outTSIG.MACSize = 0 outTSIG.MAC = "" testTSIG.Hdr.Rdlength = outTSIG.Hdr.Rdlength if *outTSIG != testTSIG { t.Fatalf("TSIG RR doesn't match: expected '%v', but got '%v'", *outTSIG, testTSIG) } }) } } func TestTSIGHMAC224And384(t *testing.T) { tests := []struct { algorithm string // TSIG algorithm, also used as test description secret string // (arbitrarily chosen) secret suitable for the algorithm in base64 format expectedMAC string // pre-computed expected (correct) MAC in hex form }{ {HmacSHA224, "hVEkQuAqnTmBuRrT9KF1Udr91gOMGWPw9LaTtw==", "d6daf9ea189e48bc38f9aed63d6cc4140cdfa38a7a333ee2eefdbd31", }, {HmacSHA384, "Qjer2TL2lAdpq9w6Gjs98/ClCQx/L3vtgVHCmrZ8l/oKEPjqUUMFO18gMCRwd5H4", "89a48936d29187870c325cbdba5ad71609bd038d0459d6010c844d659c570e881d3650e4fe7310be53ebe5178d0d1001", }, } for _, tc := range tests { tc := tc t.Run(tc.algorithm, func(t *testing.T) { // Build a DNS message with TSIG for the test scenario tsig := TSIG{ Hdr: RR_Header{Name: "testkey.", Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}, Algorithm: tc.algorithm, TimeSigned: timeSigned, Fudge: 300, OrigId: 42, } req := &Msg{ MsgHdr: MsgHdr{Opcode: OpcodeUpdate}, Question: []Question{{Name: "example.com.", Qtype: TypeSOA, Qclass: ClassINET}}, Extra: []RR{&tsig}, } // Confirm both Generate and Verify recognize the algorithm and handle it correctly msgData, mac, err := TsigGenerate(req, tc.secret, "", false) if err != nil { t.Error(err) } if mac != tc.expectedMAC { t.Fatalf("MAC doesn't match: expected '%s' but got '%s'", tc.expectedMAC, mac) } if err = tsigVerify(msgData, tsigHMACProvider(tc.secret), "", false, timeSigned); err != nil { t.Error(err) } }) } } const testGoodKeyName = "goodkey." var ( errBadKey = errors.New("this is an intentional error") testGoodMAC = []byte{0, 1, 2, 3} ) // testProvider always generates the same MAC and only accepts the one signature type testProvider struct { GenerateAllKeys bool } func (provider *testProvider) Generate(_ []byte, t *TSIG) ([]byte, error) { if t.Hdr.Name == testGoodKeyName || provider.GenerateAllKeys { return testGoodMAC, nil } return nil, errBadKey } func (*testProvider) Verify(_ []byte, t *TSIG) error { if t.Hdr.Name == testGoodKeyName { return nil } return errBadKey } func TestTsigGenerateProvider(t *testing.T) { tables := []struct { keyname string mac []byte err error }{ { testGoodKeyName, testGoodMAC, nil, }, { "badkey.", nil, errBadKey, }, } for _, table := range tables { t.Run(table.keyname, func(t *testing.T) { tsig := TSIG{ Hdr: RR_Header{Name: table.keyname, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}, Algorithm: HmacSHA1, TimeSigned: timeSigned, Fudge: 300, OrigId: 42, } req := &Msg{ MsgHdr: MsgHdr{Opcode: OpcodeUpdate}, Question: []Question{{Name: "example.com.", Qtype: TypeSOA, Qclass: ClassINET}}, Extra: []RR{&tsig}, } _, mac, err := TsigGenerateWithProvider(req, new(testProvider), "", false) if err != table.err { t.Fatalf("error doesn't match: expected '%s' but got '%s'", table.err, err) } expectedMAC := hex.EncodeToString(table.mac) if mac != expectedMAC { t.Fatalf("MAC doesn't match: expected '%s' but got '%s'", table.mac, expectedMAC) } }) } } func TestTsigVerifyProvider(t *testing.T) { tables := []struct { keyname string err error }{ { testGoodKeyName, nil, }, { "badkey.", errBadKey, }, } for _, table := range tables { t.Run(table.keyname, func(t *testing.T) { tsig := TSIG{ Hdr: RR_Header{Name: table.keyname, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}, Algorithm: HmacSHA1, TimeSigned: timeSigned, Fudge: 300, OrigId: 42, } req := &Msg{ MsgHdr: MsgHdr{Opcode: OpcodeUpdate}, Question: []Question{{Name: "example.com.", Qtype: TypeSOA, Qclass: ClassINET}}, Extra: []RR{&tsig}, } provider := &testProvider{true} msgData, _, err := TsigGenerateWithProvider(req, provider, "", false) if err != nil { t.Error(err) } if err = tsigVerify(msgData, provider, "", false, timeSigned); err != table.err { t.Fatalf("error doesn't match: expected '%s' but got '%s'", table.err, err) } }) } } golang-github-miekg-dns-1.1.64/types.go000066400000000000000000001160711476742671700177340ustar00rootroot00000000000000package dns import ( "bytes" "fmt" "net" "strconv" "strings" "time" ) type ( // Type is a DNS type. Type uint16 // Class is a DNS class. Class uint16 // Name is a DNS domain name. Name string ) // Packet formats // Wire constants and supported types. const ( // valid RR_Header.Rrtype and Question.qtype TypeNone uint16 = 0 TypeA uint16 = 1 TypeNS uint16 = 2 TypeMD uint16 = 3 TypeMF uint16 = 4 TypeCNAME uint16 = 5 TypeSOA uint16 = 6 TypeMB uint16 = 7 TypeMG uint16 = 8 TypeMR uint16 = 9 TypeNULL uint16 = 10 TypePTR uint16 = 12 TypeHINFO uint16 = 13 TypeMINFO uint16 = 14 TypeMX uint16 = 15 TypeTXT uint16 = 16 TypeRP uint16 = 17 TypeAFSDB uint16 = 18 TypeX25 uint16 = 19 TypeISDN uint16 = 20 TypeRT uint16 = 21 TypeNSAPPTR uint16 = 23 TypeSIG uint16 = 24 TypeKEY uint16 = 25 TypePX uint16 = 26 TypeGPOS uint16 = 27 TypeAAAA uint16 = 28 TypeLOC uint16 = 29 TypeNXT uint16 = 30 TypeEID uint16 = 31 TypeNIMLOC uint16 = 32 TypeSRV uint16 = 33 TypeATMA uint16 = 34 TypeNAPTR uint16 = 35 TypeKX uint16 = 36 TypeCERT uint16 = 37 TypeDNAME uint16 = 39 TypeOPT uint16 = 41 // EDNS TypeAPL uint16 = 42 TypeDS uint16 = 43 TypeSSHFP uint16 = 44 TypeIPSECKEY uint16 = 45 TypeRRSIG uint16 = 46 TypeNSEC uint16 = 47 TypeDNSKEY uint16 = 48 TypeDHCID uint16 = 49 TypeNSEC3 uint16 = 50 TypeNSEC3PARAM uint16 = 51 TypeTLSA uint16 = 52 TypeSMIMEA uint16 = 53 TypeHIP uint16 = 55 TypeNINFO uint16 = 56 TypeRKEY uint16 = 57 TypeTALINK uint16 = 58 TypeCDS uint16 = 59 TypeCDNSKEY uint16 = 60 TypeOPENPGPKEY uint16 = 61 TypeCSYNC uint16 = 62 TypeZONEMD uint16 = 63 TypeSVCB uint16 = 64 TypeHTTPS uint16 = 65 TypeSPF uint16 = 99 TypeUINFO uint16 = 100 TypeUID uint16 = 101 TypeGID uint16 = 102 TypeUNSPEC uint16 = 103 TypeNID uint16 = 104 TypeL32 uint16 = 105 TypeL64 uint16 = 106 TypeLP uint16 = 107 TypeEUI48 uint16 = 108 TypeEUI64 uint16 = 109 TypeNXNAME uint16 = 128 TypeURI uint16 = 256 TypeCAA uint16 = 257 TypeAVC uint16 = 258 TypeAMTRELAY uint16 = 260 TypeRESINFO uint16 = 261 TypeTKEY uint16 = 249 TypeTSIG uint16 = 250 // valid Question.Qtype only TypeIXFR uint16 = 251 TypeAXFR uint16 = 252 TypeMAILB uint16 = 253 TypeMAILA uint16 = 254 TypeANY uint16 = 255 TypeTA uint16 = 32768 TypeDLV uint16 = 32769 TypeReserved uint16 = 65535 // valid Question.Qclass ClassINET = 1 ClassCSNET = 2 ClassCHAOS = 3 ClassHESIOD = 4 ClassNONE = 254 ClassANY = 255 // Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml RcodeSuccess = 0 // NoError - No Error [DNS] RcodeFormatError = 1 // FormErr - Format Error [DNS] RcodeServerFailure = 2 // ServFail - Server Failure [DNS] RcodeNameError = 3 // NXDomain - Non-Existent Domain [DNS] RcodeNotImplemented = 4 // NotImp - Not Implemented [DNS] RcodeRefused = 5 // Refused - Query Refused [DNS] RcodeYXDomain = 6 // YXDomain - Name Exists when it should not [DNS Update] RcodeYXRrset = 7 // YXRRSet - RR Set Exists when it should not [DNS Update] RcodeNXRrset = 8 // NXRRSet - RR Set that should exist does not [DNS Update] RcodeNotAuth = 9 // NotAuth - Server Not Authoritative for zone [DNS Update] RcodeNotZone = 10 // NotZone - Name not contained in zone [DNS Update/TSIG] RcodeBadSig = 16 // BADSIG - TSIG Signature Failure [TSIG] https://www.rfc-editor.org/rfc/rfc6895.html#section-2.3 RcodeBadVers = 16 // BADVERS - Bad OPT Version [EDNS0] https://www.rfc-editor.org/rfc/rfc6895.html#section-2.3 RcodeBadKey = 17 // BADKEY - Key not recognized [TSIG] RcodeBadTime = 18 // BADTIME - Signature out of time window [TSIG] RcodeBadMode = 19 // BADMODE - Bad TKEY Mode [TKEY] RcodeBadName = 20 // BADNAME - Duplicate key name [TKEY] RcodeBadAlg = 21 // BADALG - Algorithm not supported [TKEY] RcodeBadTrunc = 22 // BADTRUNC - Bad Truncation [TSIG] RcodeBadCookie = 23 // BADCOOKIE - Bad/missing Server Cookie [DNS Cookies] // Message Opcodes. There is no 3. OpcodeQuery = 0 OpcodeIQuery = 1 OpcodeStatus = 2 OpcodeNotify = 4 OpcodeUpdate = 5 ) // Used in ZONEMD https://tools.ietf.org/html/rfc8976 const ( ZoneMDSchemeSimple = 1 ZoneMDHashAlgSHA384 = 1 ZoneMDHashAlgSHA512 = 2 ) // Used in IPSEC https://datatracker.ietf.org/doc/html/rfc4025#section-2.3 const ( IPSECGatewayNone uint8 = iota IPSECGatewayIPv4 IPSECGatewayIPv6 IPSECGatewayHost ) // Used in AMTRELAY https://datatracker.ietf.org/doc/html/rfc8777#section-4.2.3 const ( AMTRELAYNone = IPSECGatewayNone AMTRELAYIPv4 = IPSECGatewayIPv4 AMTRELAYIPv6 = IPSECGatewayIPv6 AMTRELAYHost = IPSECGatewayHost ) // Header is the wire format for the DNS packet header. type Header struct { Id uint16 Bits uint16 Qdcount, Ancount, Nscount, Arcount uint16 } const ( headerSize = 12 // Header.Bits _QR = 1 << 15 // query/response (response=1) _AA = 1 << 10 // authoritative _TC = 1 << 9 // truncated _RD = 1 << 8 // recursion desired _RA = 1 << 7 // recursion available _Z = 1 << 6 // Z _AD = 1 << 5 // authenticated data _CD = 1 << 4 // checking disabled ) // Various constants used in the LOC RR. See RFC 1876. const ( LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. LOC_HOURS = 60 * 1000 LOC_DEGREES = 60 * LOC_HOURS LOC_ALTITUDEBASE = 100000 ) // Different Certificate Types, see RFC 4398, Section 2.1 const ( CertPKIX = 1 + iota CertSPKI CertPGP CertIPIX CertISPKI CertIPGP CertACPKIX CertIACPKIX CertURI = 253 CertOID = 254 ) // CertTypeToString converts the Cert Type to its string representation. // See RFC 4398 and RFC 6944. var CertTypeToString = map[uint16]string{ CertPKIX: "PKIX", CertSPKI: "SPKI", CertPGP: "PGP", CertIPIX: "IPIX", CertISPKI: "ISPKI", CertIPGP: "IPGP", CertACPKIX: "ACPKIX", CertIACPKIX: "IACPKIX", CertURI: "URI", CertOID: "OID", } // Prefix for IPv4 encoded as IPv6 address const ipv4InIPv6Prefix = "::ffff:" //go:generate go run types_generate.go // Question holds a DNS question. Usually there is just one. While the // original DNS RFCs allow multiple questions in the question section of a // message, in practice it never works. Because most DNS servers see multiple // questions as an error, it is recommended to only have one question per // message. type Question struct { Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed) Qtype uint16 Qclass uint16 } func (q *Question) len(off int, compression map[string]struct{}) int { l := domainNameLen(q.Name, off, compression, true) l += 2 + 2 return l } func (q *Question) String() (s string) { // prefix with ; (as in dig) s = ";" + sprintName(q.Name) + "\t" s += Class(q.Qclass).String() + "\t" s += " " + Type(q.Qtype).String() return s } // ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY // is named "*" there. type ANY struct { Hdr RR_Header // Does not have any rdata } func (rr *ANY) String() string { return rr.Hdr.String() } func (*ANY) parse(c *zlexer, origin string) *ParseError { return &ParseError{err: "ANY records do not have a presentation format"} } // NULL RR. See RFC 1035. type NULL struct { Hdr RR_Header Data string `dns:"any"` } func (rr *NULL) String() string { // There is no presentation format; prefix string with a comment. return ";" + rr.Hdr.String() + rr.Data } func (*NULL) parse(c *zlexer, origin string) *ParseError { return &ParseError{err: "NULL records do not have a presentation format"} } // NXNAME is a meta record. See https://www.iana.org/go/draft-ietf-dnsop-compact-denial-of-existence-04 // Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml type NXNAME struct { Hdr RR_Header // Does not have any rdata } func (rr *NXNAME) String() string { return rr.Hdr.String() } func (*NXNAME) parse(c *zlexer, origin string) *ParseError { return &ParseError{err: "NXNAME records do not have a presentation format"} } // CNAME RR. See RFC 1034. type CNAME struct { Hdr RR_Header Target string `dns:"cdomain-name"` } func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } // HINFO RR. See RFC 1034. type HINFO struct { Hdr RR_Header Cpu string Os string } func (rr *HINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os}) } // MB RR. See RFC 1035. type MB struct { Hdr RR_Header Mb string `dns:"cdomain-name"` } func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) } // MG RR. See RFC 1035. type MG struct { Hdr RR_Header Mg string `dns:"cdomain-name"` } func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) } // MINFO RR. See RFC 1035. type MINFO struct { Hdr RR_Header Rmail string `dns:"cdomain-name"` Email string `dns:"cdomain-name"` } func (rr *MINFO) String() string { return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email) } // MR RR. See RFC 1035. type MR struct { Hdr RR_Header Mr string `dns:"cdomain-name"` } func (rr *MR) String() string { return rr.Hdr.String() + sprintName(rr.Mr) } // MF RR. See RFC 1035. type MF struct { Hdr RR_Header Mf string `dns:"cdomain-name"` } func (rr *MF) String() string { return rr.Hdr.String() + sprintName(rr.Mf) } // MD RR. See RFC 1035. type MD struct { Hdr RR_Header Md string `dns:"cdomain-name"` } func (rr *MD) String() string { return rr.Hdr.String() + sprintName(rr.Md) } // MX RR. See RFC 1035. type MX struct { Hdr RR_Header Preference uint16 Mx string `dns:"cdomain-name"` } func (rr *MX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx) } // AFSDB RR. See RFC 1183. type AFSDB struct { Hdr RR_Header Subtype uint16 Hostname string `dns:"domain-name"` } func (rr *AFSDB) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname) } // X25 RR. See RFC 1183, Section 3.1. type X25 struct { Hdr RR_Header PSDNAddress string } func (rr *X25) String() string { return rr.Hdr.String() + rr.PSDNAddress } // ISDN RR. See RFC 1183, Section 3.2. type ISDN struct { Hdr RR_Header Address string SubAddress string } func (rr *ISDN) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Address, rr.SubAddress}) } // RT RR. See RFC 1183, Section 3.3. type RT struct { Hdr RR_Header Preference uint16 Host string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035. } func (rr *RT) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host) } // NS RR. See RFC 1035. type NS struct { Hdr RR_Header Ns string `dns:"cdomain-name"` } func (rr *NS) String() string { return rr.Hdr.String() + sprintName(rr.Ns) } // PTR RR. See RFC 1035. type PTR struct { Hdr RR_Header Ptr string `dns:"cdomain-name"` } func (rr *PTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } // RP RR. See RFC 1138, Section 2.2. type RP struct { Hdr RR_Header Mbox string `dns:"domain-name"` Txt string `dns:"domain-name"` } func (rr *RP) String() string { return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt) } // SOA RR. See RFC 1035. type SOA struct { Hdr RR_Header Ns string `dns:"cdomain-name"` Mbox string `dns:"cdomain-name"` Serial uint32 Refresh uint32 Retry uint32 Expire uint32 Minttl uint32 } func (rr *SOA) String() string { return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) + " " + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.FormatInt(int64(rr.Refresh), 10) + " " + strconv.FormatInt(int64(rr.Retry), 10) + " " + strconv.FormatInt(int64(rr.Expire), 10) + " " + strconv.FormatInt(int64(rr.Minttl), 10) } // TXT RR. See RFC 1035. type TXT struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } func sprintName(s string) string { var dst strings.Builder for i := 0; i < len(s); { if s[i] == '.' { if dst.Len() != 0 { dst.WriteByte('.') } i++ continue } b, n := nextByte(s, i) if n == 0 { // Drop "dangling" incomplete escapes. if dst.Len() == 0 { return s[:i] } break } if isDomainNameLabelSpecial(b) { if dst.Len() == 0 { dst.Grow(len(s) * 2) dst.WriteString(s[:i]) } dst.WriteByte('\\') dst.WriteByte(b) } else if b < ' ' || b > '~' { // unprintable, use \DDD if dst.Len() == 0 { dst.Grow(len(s) * 2) dst.WriteString(s[:i]) } dst.WriteString(escapeByte(b)) } else { if dst.Len() != 0 { dst.WriteByte(b) } } i += n } if dst.Len() == 0 { return s } return dst.String() } func sprintTxtOctet(s string) string { var dst strings.Builder dst.Grow(2 + len(s)) dst.WriteByte('"') for i := 0; i < len(s); { if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { dst.WriteString(s[i : i+2]) i += 2 continue } b, n := nextByte(s, i) if n == 0 { i++ // dangling back slash } else { writeTXTStringByte(&dst, b) } i += n } dst.WriteByte('"') return dst.String() } func sprintTxt(txt []string) string { var out strings.Builder for i, s := range txt { out.Grow(3 + len(s)) if i > 0 { out.WriteString(` "`) } else { out.WriteByte('"') } for j := 0; j < len(s); { b, n := nextByte(s, j) if n == 0 { break } writeTXTStringByte(&out, b) j += n } out.WriteByte('"') } return out.String() } func writeTXTStringByte(s *strings.Builder, b byte) { switch { case b == '"' || b == '\\': s.WriteByte('\\') s.WriteByte(b) case b < ' ' || b > '~': s.WriteString(escapeByte(b)) default: s.WriteByte(b) } } const ( escapedByteSmall = "" + `\000\001\002\003\004\005\006\007\008\009` + `\010\011\012\013\014\015\016\017\018\019` + `\020\021\022\023\024\025\026\027\028\029` + `\030\031` escapedByteLarge = `\127\128\129` + `\130\131\132\133\134\135\136\137\138\139` + `\140\141\142\143\144\145\146\147\148\149` + `\150\151\152\153\154\155\156\157\158\159` + `\160\161\162\163\164\165\166\167\168\169` + `\170\171\172\173\174\175\176\177\178\179` + `\180\181\182\183\184\185\186\187\188\189` + `\190\191\192\193\194\195\196\197\198\199` + `\200\201\202\203\204\205\206\207\208\209` + `\210\211\212\213\214\215\216\217\218\219` + `\220\221\222\223\224\225\226\227\228\229` + `\230\231\232\233\234\235\236\237\238\239` + `\240\241\242\243\244\245\246\247\248\249` + `\250\251\252\253\254\255` ) // escapeByte returns the \DDD escaping of b which must // satisfy b < ' ' || b > '~'. func escapeByte(b byte) string { if b < ' ' { return escapedByteSmall[b*4 : b*4+4] } b -= '~' + 1 // The cast here is needed as b*4 may overflow byte. return escapedByteLarge[int(b)*4 : int(b)*4+4] } // isDomainNameLabelSpecial returns true if // a domain name label byte should be prefixed // with an escaping backslash. func isDomainNameLabelSpecial(b byte) bool { switch b { case '.', ' ', '\'', '@', ';', '(', ')', '"', '\\': return true } return false } func nextByte(s string, offset int) (byte, int) { if offset >= len(s) { return 0, 0 } if s[offset] != '\\' { // not an escape sequence return s[offset], 1 } switch len(s) - offset { case 1: // dangling escape return 0, 0 case 2, 3: // too short to be \ddd default: // maybe \ddd if isDDD(s[offset+1:]) { return dddToByte(s[offset+1:]), 4 } } // not \ddd, just an RFC 1035 "quoted" character return s[offset+1], 2 } // SPF RR. See RFC 4408, Section 3.1.1. type SPF struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } // AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template. type AVC struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } // SRV RR. See RFC 2782. type SRV struct { Hdr RR_Header Priority uint16 Weight uint16 Port uint16 Target string `dns:"domain-name"` } func (rr *SRV) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + " " + strconv.Itoa(int(rr.Weight)) + " " + strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target) } // NAPTR RR. See RFC 2915. type NAPTR struct { Hdr RR_Header Order uint16 Preference uint16 Flags string Service string Regexp string Replacement string `dns:"domain-name"` } func (rr *NAPTR) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Order)) + " " + strconv.Itoa(int(rr.Preference)) + " " + "\"" + rr.Flags + "\" " + "\"" + rr.Service + "\" " + "\"" + rr.Regexp + "\" " + rr.Replacement } // CERT RR. See RFC 4398. type CERT struct { Hdr RR_Header Type uint16 KeyTag uint16 Algorithm uint8 Certificate string `dns:"base64"` } func (rr *CERT) String() string { var ( ok bool certtype, algorithm string ) if certtype, ok = CertTypeToString[rr.Type]; !ok { certtype = strconv.Itoa(int(rr.Type)) } if algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok { algorithm = strconv.Itoa(int(rr.Algorithm)) } return rr.Hdr.String() + certtype + " " + strconv.Itoa(int(rr.KeyTag)) + " " + algorithm + " " + rr.Certificate } // DNAME RR. See RFC 2672. type DNAME struct { Hdr RR_Header Target string `dns:"domain-name"` } func (rr *DNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } // A RR. See RFC 1035. type A struct { Hdr RR_Header A net.IP `dns:"a"` } func (rr *A) String() string { if rr.A == nil { return rr.Hdr.String() } return rr.Hdr.String() + rr.A.String() } // AAAA RR. See RFC 3596. type AAAA struct { Hdr RR_Header AAAA net.IP `dns:"aaaa"` } func (rr *AAAA) String() string { if rr.AAAA == nil { return rr.Hdr.String() } if rr.AAAA.To4() != nil { return rr.Hdr.String() + ipv4InIPv6Prefix + rr.AAAA.String() } return rr.Hdr.String() + rr.AAAA.String() } // PX RR. See RFC 2163. type PX struct { Hdr RR_Header Preference uint16 Map822 string `dns:"domain-name"` Mapx400 string `dns:"domain-name"` } func (rr *PX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400) } // GPOS RR. See RFC 1712. type GPOS struct { Hdr RR_Header Longitude string Latitude string Altitude string } func (rr *GPOS) String() string { return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude } // LOC RR. See RFC 1876. type LOC struct { Hdr RR_Header Version uint8 Size uint8 HorizPre uint8 VertPre uint8 Latitude uint32 Longitude uint32 Altitude uint32 } // cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent // format and returns a string in m (two decimals for the cm). func cmToM(x uint8) string { m := x & 0xf0 >> 4 e := x & 0x0f if e < 2 { if e == 1 { m *= 10 } return fmt.Sprintf("0.%02d", m) } s := fmt.Sprintf("%d", m) for e > 2 { s += "0" e-- } return s } func (rr *LOC) String() string { s := rr.Hdr.String() lat := rr.Latitude ns := "N" if lat > LOC_EQUATOR { lat = lat - LOC_EQUATOR } else { ns = "S" lat = LOC_EQUATOR - lat } h := lat / LOC_DEGREES lat = lat % LOC_DEGREES m := lat / LOC_HOURS lat = lat % LOC_HOURS s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns) lon := rr.Longitude ew := "E" if lon > LOC_PRIMEMERIDIAN { lon = lon - LOC_PRIMEMERIDIAN } else { ew = "W" lon = LOC_PRIMEMERIDIAN - lon } h = lon / LOC_DEGREES lon = lon % LOC_DEGREES m = lon / LOC_HOURS lon = lon % LOC_HOURS s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew) var alt = float64(rr.Altitude) / 100 alt -= LOC_ALTITUDEBASE if rr.Altitude%100 != 0 { s += fmt.Sprintf("%.2fm ", alt) } else { s += fmt.Sprintf("%.0fm ", alt) } s += cmToM(rr.Size) + "m " s += cmToM(rr.HorizPre) + "m " s += cmToM(rr.VertPre) + "m" return s } // SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931. type SIG struct { RRSIG } // RRSIG RR. See RFC 4034 and RFC 3755. type RRSIG struct { Hdr RR_Header TypeCovered uint16 Algorithm uint8 Labels uint8 OrigTtl uint32 Expiration uint32 Inception uint32 KeyTag uint16 SignerName string `dns:"domain-name"` Signature string `dns:"base64"` } func (rr *RRSIG) String() string { s := rr.Hdr.String() s += Type(rr.TypeCovered).String() s += " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.Labels)) + " " + strconv.FormatInt(int64(rr.OrigTtl), 10) + " " + TimeToString(rr.Expiration) + " " + TimeToString(rr.Inception) + " " + strconv.Itoa(int(rr.KeyTag)) + " " + sprintName(rr.SignerName) + " " + rr.Signature return s } // NXT RR. See RFC 2535. type NXT struct { NSEC } // NSEC RR. See RFC 4034 and RFC 3755. type NSEC struct { Hdr RR_Header NextDomain string `dns:"domain-name"` TypeBitMap []uint16 `dns:"nsec"` } func (rr *NSEC) String() string { s := rr.Hdr.String() + sprintName(rr.NextDomain) for _, t := range rr.TypeBitMap { s += " " + Type(t).String() } return s } func (rr *NSEC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.NextDomain, off+l, compression, false) l += typeBitMapLen(rr.TypeBitMap) return l } // DLV RR. See RFC 4431. type DLV struct{ DS } // CDS RR. See RFC 7344. type CDS struct{ DS } // DS RR. See RFC 4034 and RFC 3658. type DS struct { Hdr RR_Header KeyTag uint16 Algorithm uint8 DigestType uint8 Digest string `dns:"hex"` } func (rr *DS) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.DigestType)) + " " + strings.ToUpper(rr.Digest) } // KX RR. See RFC 2230. type KX struct { Hdr RR_Header Preference uint16 Exchanger string `dns:"domain-name"` } func (rr *KX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Exchanger) } // TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf. type TA struct { Hdr RR_Header KeyTag uint16 Algorithm uint8 DigestType uint8 Digest string `dns:"hex"` } func (rr *TA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.DigestType)) + " " + strings.ToUpper(rr.Digest) } // TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template. type TALINK struct { Hdr RR_Header PreviousName string `dns:"domain-name"` NextName string `dns:"domain-name"` } func (rr *TALINK) String() string { return rr.Hdr.String() + sprintName(rr.PreviousName) + " " + sprintName(rr.NextName) } // SSHFP RR. See RFC 4255. type SSHFP struct { Hdr RR_Header Algorithm uint8 Type uint8 FingerPrint string `dns:"hex"` } func (rr *SSHFP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.Type)) + " " + strings.ToUpper(rr.FingerPrint) } // KEY RR. See RFC 2535. type KEY struct { DNSKEY } // CDNSKEY RR. See RFC 7344. type CDNSKEY struct { DNSKEY } // DNSKEY RR. See RFC 4034 and RFC 3755. type DNSKEY struct { Hdr RR_Header Flags uint16 Protocol uint8 Algorithm uint8 PublicKey string `dns:"base64"` } func (rr *DNSKEY) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Protocol)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + rr.PublicKey } // IPSECKEY RR. See RFC 4025. type IPSECKEY struct { Hdr RR_Header Precedence uint8 GatewayType uint8 Algorithm uint8 GatewayAddr net.IP `dns:"-"` // packing/unpacking/parsing/etc handled together with GatewayHost GatewayHost string `dns:"ipsechost"` PublicKey string `dns:"base64"` } func (rr *IPSECKEY) String() string { var gateway string switch rr.GatewayType { case IPSECGatewayIPv4, IPSECGatewayIPv6: gateway = rr.GatewayAddr.String() case IPSECGatewayHost: gateway = rr.GatewayHost case IPSECGatewayNone: fallthrough default: gateway = "." } return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + " " + strconv.Itoa(int(rr.GatewayType)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + gateway + " " + rr.PublicKey } // AMTRELAY RR. See RFC 8777. type AMTRELAY struct { Hdr RR_Header Precedence uint8 GatewayType uint8 // discovery is packed in here at bit 0x80 GatewayAddr net.IP `dns:"-"` // packing/unpacking/parsing/etc handled together with GatewayHost GatewayHost string `dns:"amtrelayhost"` } func (rr *AMTRELAY) String() string { var gateway string switch rr.GatewayType & 0x7f { case AMTRELAYIPv4, AMTRELAYIPv6: gateway = rr.GatewayAddr.String() case AMTRELAYHost: gateway = rr.GatewayHost case AMTRELAYNone: fallthrough default: gateway = "." } boolS := "0" if rr.GatewayType&0x80 == 0x80 { boolS = "1" } return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + " " + boolS + " " + strconv.Itoa(int(rr.GatewayType&0x7f)) + " " + gateway } // RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template. type RKEY struct { Hdr RR_Header Flags uint16 Protocol uint8 Algorithm uint8 PublicKey string `dns:"base64"` } func (rr *RKEY) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Protocol)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + rr.PublicKey } // NSAPPTR RR. See RFC 1348. type NSAPPTR struct { Hdr RR_Header Ptr string `dns:"domain-name"` } func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } // NSEC3 RR. See RFC 5155. type NSEC3 struct { Hdr RR_Header Hash uint8 Flags uint8 Iterations uint16 SaltLength uint8 Salt string `dns:"size-hex:SaltLength"` HashLength uint8 NextDomain string `dns:"size-base32:HashLength"` TypeBitMap []uint16 `dns:"nsec"` } func (rr *NSEC3) String() string { s := rr.Hdr.String() s += strconv.Itoa(int(rr.Hash)) + " " + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Iterations)) + " " + saltToString(rr.Salt) + " " + rr.NextDomain for _, t := range rr.TypeBitMap { s += " " + Type(t).String() } return s } func (rr *NSEC3) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 l += typeBitMapLen(rr.TypeBitMap) return l } // NSEC3PARAM RR. See RFC 5155. type NSEC3PARAM struct { Hdr RR_Header Hash uint8 Flags uint8 Iterations uint16 SaltLength uint8 Salt string `dns:"size-hex:SaltLength"` } func (rr *NSEC3PARAM) String() string { s := rr.Hdr.String() s += strconv.Itoa(int(rr.Hash)) + " " + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Iterations)) + " " + saltToString(rr.Salt) return s } // TKEY RR. See RFC 2930. type TKEY struct { Hdr RR_Header Algorithm string `dns:"domain-name"` Inception uint32 Expiration uint32 Mode uint16 Error uint16 KeySize uint16 Key string `dns:"size-hex:KeySize"` OtherLen uint16 OtherData string `dns:"size-hex:OtherLen"` } // TKEY has no official presentation format, but this will suffice. func (rr *TKEY) String() string { s := ";" + rr.Hdr.String() + " " + rr.Algorithm + " " + TimeToString(rr.Inception) + " " + TimeToString(rr.Expiration) + " " + strconv.Itoa(int(rr.Mode)) + " " + strconv.Itoa(int(rr.Error)) + " " + strconv.Itoa(int(rr.KeySize)) + " " + rr.Key + " " + strconv.Itoa(int(rr.OtherLen)) + " " + rr.OtherData return s } // RFC3597 represents an unknown/generic RR. See RFC 3597. type RFC3597 struct { Hdr RR_Header Rdata string `dns:"hex"` } func (rr *RFC3597) String() string { // Let's call it a hack s := rfc3597Header(rr.Hdr) s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata return s } func rfc3597Header(h RR_Header) string { var s string s += sprintName(h.Name) + "\t" s += strconv.FormatInt(int64(h.Ttl), 10) + "\t" s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t" s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t" return s } // URI RR. See RFC 7553. type URI struct { Hdr RR_Header Priority uint16 Weight uint16 Target string `dns:"octet"` } // rr.Target to be parsed as a sequence of character encoded octets according to RFC 3986 func (rr *URI) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target) } // DHCID RR. See RFC 4701. type DHCID struct { Hdr RR_Header Digest string `dns:"base64"` } func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest } // TLSA RR. See RFC 6698. type TLSA struct { Hdr RR_Header Usage uint8 Selector uint8 MatchingType uint8 Certificate string `dns:"hex"` } func (rr *TLSA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Usage)) + " " + strconv.Itoa(int(rr.Selector)) + " " + strconv.Itoa(int(rr.MatchingType)) + " " + rr.Certificate } // SMIMEA RR. See RFC 8162. type SMIMEA struct { Hdr RR_Header Usage uint8 Selector uint8 MatchingType uint8 Certificate string `dns:"hex"` } func (rr *SMIMEA) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Usage)) + " " + strconv.Itoa(int(rr.Selector)) + " " + strconv.Itoa(int(rr.MatchingType)) // Every Nth char needs a space on this output. If we output // this as one giant line, we can't read it can in because in some cases // the cert length overflows scan.maxTok (2048). sx := splitN(rr.Certificate, 1024) // conservative value here s += " " + strings.Join(sx, " ") return s } // HIP RR. See RFC 8005. type HIP struct { Hdr RR_Header HitLength uint8 PublicKeyAlgorithm uint8 PublicKeyLength uint16 Hit string `dns:"size-hex:HitLength"` PublicKey string `dns:"size-base64:PublicKeyLength"` RendezvousServers []string `dns:"domain-name"` } func (rr *HIP) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.PublicKeyAlgorithm)) + " " + rr.Hit + " " + rr.PublicKey for _, d := range rr.RendezvousServers { s += " " + sprintName(d) } return s } // NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template. type NINFO struct { Hdr RR_Header ZSData []string `dns:"txt"` } func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) } // NID RR. See RFC 6742. type NID struct { Hdr RR_Header Preference uint16 NodeID uint64 } func (rr *NID) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) node := fmt.Sprintf("%0.16x", rr.NodeID) s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16] return s } // L32 RR, See RFC 6742. type L32 struct { Hdr RR_Header Preference uint16 Locator32 net.IP `dns:"a"` } func (rr *L32) String() string { if rr.Locator32 == nil { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) } return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + rr.Locator32.String() } // L64 RR, See RFC 6742. type L64 struct { Hdr RR_Header Preference uint16 Locator64 uint64 } func (rr *L64) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) node := fmt.Sprintf("%0.16X", rr.Locator64) s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16] return s } // LP RR. See RFC 6742. type LP struct { Hdr RR_Header Preference uint16 Fqdn string `dns:"domain-name"` } func (rr *LP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn) } // EUI48 RR. See RFC 7043. type EUI48 struct { Hdr RR_Header Address uint64 `dns:"uint48"` } func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) } // EUI64 RR. See RFC 7043. type EUI64 struct { Hdr RR_Header Address uint64 } func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) } // CAA RR. See RFC 6844. type CAA struct { Hdr RR_Header Flag uint8 Tag string Value string `dns:"octet"` } // rr.Value Is the character-string encoding of the value field as specified in RFC 1035, Section 5.1. func (rr *CAA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value) } // UID RR. Deprecated, IANA-Reserved. type UID struct { Hdr RR_Header Uid uint32 } func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) } // GID RR. Deprecated, IANA-Reserved. type GID struct { Hdr RR_Header Gid uint32 } func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) } // UINFO RR. Deprecated, IANA-Reserved. type UINFO struct { Hdr RR_Header Uinfo string } func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) } // EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt. type EID struct { Hdr RR_Header Endpoint string `dns:"hex"` } func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) } // NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt. type NIMLOC struct { Hdr RR_Header Locator string `dns:"hex"` } func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) } // OPENPGPKEY RR. See RFC 7929. type OPENPGPKEY struct { Hdr RR_Header PublicKey string `dns:"base64"` } func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey } // CSYNC RR. See RFC 7477. type CSYNC struct { Hdr RR_Header Serial uint32 Flags uint16 TypeBitMap []uint16 `dns:"nsec"` } func (rr *CSYNC) String() string { s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags)) for _, t := range rr.TypeBitMap { s += " " + Type(t).String() } return s } func (rr *CSYNC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 + 2 l += typeBitMapLen(rr.TypeBitMap) return l } // ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest type ZONEMD struct { Hdr RR_Header Serial uint32 Scheme uint8 Hash uint8 Digest string `dns:"hex"` } func (rr *ZONEMD) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Serial)) + " " + strconv.Itoa(int(rr.Scheme)) + " " + strconv.Itoa(int(rr.Hash)) + " " + rr.Digest } // RESINFO RR. See RFC 9606. type RESINFO struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *RESINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } // APL RR. See RFC 3123. type APL struct { Hdr RR_Header Prefixes []APLPrefix `dns:"apl"` } // APLPrefix is an address prefix hold by an APL record. type APLPrefix struct { Negation bool Network net.IPNet } // String returns presentation form of the APL record. func (rr *APL) String() string { var sb strings.Builder sb.WriteString(rr.Hdr.String()) for i, p := range rr.Prefixes { if i > 0 { sb.WriteByte(' ') } sb.WriteString(p.str()) } return sb.String() } // str returns presentation form of the APL prefix. func (a *APLPrefix) str() string { var sb strings.Builder if a.Negation { sb.WriteByte('!') } switch len(a.Network.IP) { case net.IPv4len: sb.WriteByte('1') case net.IPv6len: sb.WriteByte('2') } sb.WriteByte(':') switch len(a.Network.IP) { case net.IPv4len: sb.WriteString(a.Network.IP.String()) case net.IPv6len: // add prefix for IPv4-mapped IPv6 if v4 := a.Network.IP.To4(); v4 != nil { sb.WriteString(ipv4InIPv6Prefix) } sb.WriteString(a.Network.IP.String()) } sb.WriteByte('/') prefix, _ := a.Network.Mask.Size() sb.WriteString(strconv.Itoa(prefix)) return sb.String() } // equals reports whether two APL prefixes are identical. func (a *APLPrefix) equals(b *APLPrefix) bool { return a.Negation == b.Negation && a.Network.IP.Equal(b.Network.IP) && bytes.Equal(a.Network.Mask, b.Network.Mask) } // copy returns a copy of the APL prefix. func (a *APLPrefix) copy() APLPrefix { return APLPrefix{ Negation: a.Negation, Network: copyNet(a.Network), } } // len returns size of the prefix in wire format. func (a *APLPrefix) len() int { // 4-byte header and the network address prefix (see Section 4 of RFC 3123) prefix, _ := a.Network.Mask.Size() return 4 + (prefix+7)/8 } // TimeToString translates the RRSIG's incep. and expir. times to the // string representation used when printing the record. // It takes serial arithmetic (RFC 1982) into account. func TimeToString(t uint32) string { mod := (int64(t)-time.Now().Unix())/year68 - 1 if mod < 0 { mod = 0 } ti := time.Unix(int64(t)-mod*year68, 0).UTC() return ti.Format("20060102150405") } // StringToTime translates the RRSIG's incep. and expir. times from // string values like "20110403154150" to an 32 bit integer. // It takes serial arithmetic (RFC 1982) into account. func StringToTime(s string) (uint32, error) { t, err := time.Parse("20060102150405", s) if err != nil { return 0, err } mod := t.Unix()/year68 - 1 if mod < 0 { mod = 0 } return uint32(t.Unix() - mod*year68), nil } // saltToString converts a NSECX salt to uppercase and returns "-" when it is empty. func saltToString(s string) string { if s == "" { return "-" } return strings.ToUpper(s) } func euiToString(eui uint64, bits int) (hex string) { switch bits { case 64: hex = fmt.Sprintf("%16.16x", eui) hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] + "-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16] case 48: hex = fmt.Sprintf("%12.12x", eui) hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] + "-" + hex[8:10] + "-" + hex[10:12] } return } // cloneSlice returns a shallow copy of s. func cloneSlice[E any, S ~[]E](s S) S { if s == nil { return nil } return append(S(nil), s...) } // copyNet returns a copy of a subnet. func copyNet(n net.IPNet) net.IPNet { return net.IPNet{ IP: cloneSlice(n.IP), Mask: cloneSlice(n.Mask), } } // SplitN splits a string into N sized string chunks. // This might become an exported function once. func splitN(s string, n int) []string { if len(s) < n { return []string{s} } sx := []string{} p, i := 0, n for { if i <= len(s) { sx = append(sx, s[p:i]) } else { sx = append(sx, s[p:]) break } p, i = p+n, i+n } return sx } golang-github-miekg-dns-1.1.64/types_generate.go000066400000000000000000000215131476742671700216020ustar00rootroot00000000000000//go:build ignore // +build ignore // types_generate.go is meant to run with go generate. It will use // go/{importer,types} to track down all the RR struct types. Then for each type // it will generate conversion tables (TypeToRR and TypeToString) and banal // methods (len, Header, copy) based on the struct tags. The generated source is // written to ztypes.go, and is meant to be checked into git. package main import ( "bytes" "fmt" "go/format" "go/types" "log" "os" "strings" "text/template" "golang.org/x/tools/go/packages" ) var skipLen = map[string]struct{}{ "NSEC": {}, "NSEC3": {}, "OPT": {}, "CSYNC": {}, } var packageHdr = ` // Code generated by "go run types_generate.go"; DO NOT EDIT. package dns import ( "encoding/base64" "net" ) ` var TypeToRR = template.Must(template.New("TypeToRR").Parse(` // TypeToRR is a map of constructors for each RR type. var TypeToRR = map[uint16]func() RR{ {{range .}}{{if ne . "RFC3597"}} Type{{.}}: func() RR { return new({{.}}) }, {{end}}{{end}} } `)) var typeToString = template.Must(template.New("typeToString").Parse(` // TypeToString is a map of strings for each RR type. var TypeToString = map[uint16]string{ {{range .}}{{if ne . "NSAPPTR"}} Type{{.}}: "{{.}}", {{end}}{{end}} TypeNSAPPTR: "NSAP-PTR", } `)) var headerFunc = template.Must(template.New("headerFunc").Parse(` {{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr } {{end}} `)) // getTypeStruct will take a type and the package scope, and return the // (innermost) struct if the type is considered a RR type (currently defined as // those structs beginning with a RR_Header, could be redefined as implementing // the RR interface). The bool return value indicates if embedded structs were // resolved. func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { st, ok := t.Underlying().(*types.Struct) if !ok { return nil, false } if st.NumFields() == 0 { return nil, false } if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { return st, false } if st.Field(0).Anonymous() { st, _ := getTypeStruct(st.Field(0).Type(), scope) return st, true } return nil, false } // loadModule retrieves package description for a given module. func loadModule(name string) (*types.Package, error) { conf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo} pkgs, err := packages.Load(&conf, name) if err != nil { return nil, err } return pkgs[0].Types, nil } func main() { // Import and type-check the package pkg, err := loadModule("github.com/miekg/dns") fatalIfErr(err) scope := pkg.Scope() // Collect constants like TypeX var numberedTypes []string for _, name := range scope.Names() { o := scope.Lookup(name) if o == nil || !o.Exported() { continue } b, ok := o.Type().(*types.Basic) if !ok || b.Kind() != types.Uint16 { continue } if !strings.HasPrefix(o.Name(), "Type") { continue } name := strings.TrimPrefix(o.Name(), "Type") if name == "PrivateRR" { continue } numberedTypes = append(numberedTypes, name) } // Collect actual types (*X) var namedTypes []string for _, name := range scope.Names() { o := scope.Lookup(name) if o == nil || !o.Exported() { continue } if st, _ := getTypeStruct(o.Type(), scope); st == nil { continue } if name == "PrivateRR" { continue } // Check if corresponding TypeX exists if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" { log.Fatalf("Constant Type%s does not exist.", o.Name()) } namedTypes = append(namedTypes, o.Name()) } b := &bytes.Buffer{} b.WriteString(packageHdr) // Generate TypeToRR fatalIfErr(TypeToRR.Execute(b, namedTypes)) // Generate typeToString fatalIfErr(typeToString.Execute(b, numberedTypes)) // Generate headerFunc fatalIfErr(headerFunc.Execute(b, namedTypes)) // Generate len() fmt.Fprint(b, "// len() functions\n") for _, name := range namedTypes { if _, ok := skipLen[name]; ok { continue } o := scope.Lookup(name) st, isEmbedded := getTypeStruct(o.Type(), scope) if isEmbedded { continue } fmt.Fprintf(b, "func (rr *%s) len(off int, compression map[string]struct{}) int {\n", name) fmt.Fprintf(b, "l := rr.Hdr.len(off, compression)\n") for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) } if _, ok := st.Field(i).Type().(*types.Slice); ok { switch st.Tag(i) { case `dns:"-"`: // ignored case `dns:"cdomain-name"`: o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, true) }\n") case `dns:"domain-name"`: o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\n") case `dns:"txt"`: o("for _, x := range rr.%s { l += len(x) + 1 }\n") case `dns:"apl"`: o("for _, x := range rr.%s { l += x.len() }\n") case `dns:"pairs"`: o("for _, x := range rr.%s { l += 4 + int(x.len()) }\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } continue } switch { case st.Tag(i) == `dns:"-"`: // ignored case st.Tag(i) == `dns:"cdomain-name"`: o("l += domainNameLen(rr.%s, off+l, compression, true)\n") case st.Tag(i) == `dns:"domain-name"`: o("l += domainNameLen(rr.%s, off+l, compression, false)\n") case st.Tag(i) == `dns:"octet"`: o("l += len(rr.%s)\n") case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): fallthrough case st.Tag(i) == `dns:"base64"`: o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n") case strings.HasPrefix(st.Tag(i), `dns:"size-hex:`): // this has an extra field where the length is stored o("l += len(rr.%s)/2\n") case st.Tag(i) == `dns:"hex"`: o("l += len(rr.%s)/2\n") case st.Tag(i) == `dns:"any"`: o("l += len(rr.%s)\n") case st.Tag(i) == `dns:"a"`: o("if len(rr.%s) != 0 { l += net.IPv4len }\n") case st.Tag(i) == `dns:"aaaa"`: o("if len(rr.%s) != 0 { l += net.IPv6len }\n") case st.Tag(i) == `dns:"txt"`: o("for _, t := range rr.%s { l += len(t) + 1 }\n") case st.Tag(i) == `dns:"uint48"`: o("l += 6 // %s\n") case st.Tag(i) == `dns:"ipsechost"`: o(`switch rr.GatewayType { case IPSECGatewayIPv4: l += net.IPv4len case IPSECGatewayIPv6: l += net.IPv6len case IPSECGatewayHost: l += len(rr.%s) + 1 } `) case st.Tag(i) == `dns:"amtrelayhost"`: o(`switch rr.GatewayType { case AMTRELAYIPv4: l += net.IPv4len case AMTRELAYIPv6: l += net.IPv6len case AMTRELAYHost: l += len(rr.%s) + 1 } `) case st.Tag(i) == `dns:"amtrelaytype"`: o("l++ // %s\n") case st.Tag(i) == "": switch st.Field(i).Type().(*types.Basic).Kind() { case types.Uint8: o("l++ // %s\n") case types.Uint16: o("l += 2 // %s\n") case types.Uint32: o("l += 4 // %s\n") case types.Uint64: o("l += 8 // %s\n") case types.String: o("l += len(rr.%s) + 1\n") default: log.Fatalln(name, st.Field(i).Name()) } default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } } fmt.Fprint(b, "return l }\n\n") } // Generate copy() fmt.Fprint(b, "// copy() functions\n") for _, name := range namedTypes { o := scope.Lookup(name) st, isEmbedded := getTypeStruct(o.Type(), scope) fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name) fields := make([]string, 0, st.NumFields()) if isEmbedded { a, _ := o.Type().Underlying().(*types.Struct) parent := a.Field(0).Name() fields = append(fields, "*rr."+parent+".copy().(*"+parent+")") goto WriteCopy } fields = append(fields, "rr.Hdr") for i := 1; i < st.NumFields(); i++ { f := st.Field(i).Name() if sl, ok := st.Field(i).Type().(*types.Slice); ok { t := sl.Underlying().String() t = strings.TrimPrefix(t, "[]") if idx := strings.LastIndex(t, "."); idx >= 0 { t = t[idx+1:] } // For the EDNS0 interface (and others), we need to call the copy method on each element. if t == "EDNS0" || t == "APLPrefix" || t == "SVCBKeyValue" { fmt.Fprintf(b, "%s := make([]%s, len(rr.%s));\nfor i,e := range rr.%s {\n %s[i] = e.copy()\n}\n", f, t, f, f, f) fields = append(fields, f) continue } fields = append(fields, "cloneSlice(rr."+f+")") continue } if st.Field(i).Type().String() == "net.IP" { fields = append(fields, "cloneSlice(rr."+f+")") continue } fields = append(fields, "rr."+f) } WriteCopy: if len(fields) > 3 { fmt.Fprintf(b, "return &%s{\n%s,\n}\n", name, strings.Join(fields, ",\n")) } else { fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ",")) } fmt.Fprint(b, "}\n\n") } // gofmt res, err := format.Source(b.Bytes()) if err != nil { b.WriteTo(os.Stderr) log.Fatal(err) } // write result f, err := os.Create("ztypes.go") fatalIfErr(err) defer f.Close() f.Write(res) } func fatalIfErr(err error) { if err != nil { log.Fatal(err) } } golang-github-miekg-dns-1.1.64/types_test.go000066400000000000000000000105661476742671700207750ustar00rootroot00000000000000package dns import ( "testing" ) func TestCmToM(t *testing.T) { s := cmToM((0 << 4) + 0) if s != "0.00" { t.Error("0, 0") } s = cmToM((1 << 4) + 0) if s != "0.01" { t.Error("1, 0") } s = cmToM((3 << 4) + 1) if s != "0.30" { t.Error("3, 1") } s = cmToM((4 << 4) + 2) if s != "4" { t.Error("4, 2") } s = cmToM((5 << 4) + 3) if s != "50" { t.Error("5, 3") } s = cmToM((7 << 4) + 5) if s != "7000" { t.Error("7, 5") } s = cmToM((9 << 4) + 9) if s != "90000000" { t.Error("9, 9") } } func TestSplitN(t *testing.T) { xs := splitN("abc", 5) if len(xs) != 1 && xs[0] != "abc" { t.Errorf("failure to split abc") } s := "" for i := 0; i < 255; i++ { s += "a" } xs = splitN(s, 255) if len(xs) != 1 && xs[0] != s { t.Errorf("failure to split 255 char long string") } s += "b" xs = splitN(s, 255) if len(xs) != 2 || xs[1] != "b" { t.Errorf("failure to split 256 char long string: %d", len(xs)) } // Make s longer for i := 0; i < 255; i++ { s += "a" } xs = splitN(s, 255) if len(xs) != 3 || xs[2] != "a" { t.Errorf("failure to split 510 char long string: %d", len(xs)) } } func TestSprintName(t *testing.T) { tests := map[string]string{ // Non-numeric escaping of special printable characters. " '@;()\"\\..example": `\ \'\@\;\(\)\"\..example`, "\\032\\039\\064\\059\\040\\041\\034\\046\\092.example": `\ \'\@\;\(\)\"\.\\.example`, // Numeric escaping of nonprintable characters. "\x00\x07\x09\x0a\x1f.\x7f\x80\xad\xef\xff": `\000\007\009\010\031.\127\128\173\239\255`, "\\000\\007\\009\\010\\031.\\127\\128\\173\\239\\255": `\000\007\009\010\031.\127\128\173\239\255`, // No escaping of other printable characters, at least after a prior escape. ";[a-zA-Z0-9_]+/*.~": `\;[a-zA-Z0-9_]+/*.~`, ";\\091\\097\\045\\122\\065\\045\\090\\048\\045\\057\\095\\093\\043\\047\\042.\\126": `\;[a-zA-Z0-9_]+/*.~`, // "\\091\\097\\045\\122\\065\\045\\090\\048\\045\\057\\095\\093\\043\\047\\042.\\126": `[a-zA-Z0-9_]+/*.~`, // Incomplete "dangling" escapes are dropped regardless of prior escaping. "a\\": `a`, ";\\": `\;`, // Escaped dots stay escaped regardless of prior escaping. "a\\.\\046.\\.\\046": `a\.\..\.\.`, "a\\046\\..\\046\\.": `a\.\..\.\.`, } for input, want := range tests { got := sprintName(input) if got != want { t.Errorf("input %q: expected %q, got %q", input, want, got) } } } func TestSprintTxtOctet(t *testing.T) { got := sprintTxtOctet("abc\\.def\007\"\127@\255\x05\xef\\") if want := "\"abc\\.def\\007\\\"W@\\173\\005\\239\""; got != want { t.Errorf("expected %q, got %q", want, got) } } func TestSprintTxt(t *testing.T) { got := sprintTxt([]string{ "abc\\.def\007\"\127@\255\x05\xef\\", "example.com", }) if want := "\"abc.def\\007\\\"W@\\173\\005\\239\" \"example.com\""; got != want { t.Errorf("expected %q, got %q", want, got) } } func TestRPStringer(t *testing.T) { rp := &RP{ Hdr: RR_Header{ Name: "test.example.com.", Rrtype: TypeRP, Class: ClassINET, Ttl: 600, }, Mbox: "\x05first.example.com.", Txt: "second.\x07example.com.", } const expected = "test.example.com.\t600\tIN\tRP\t\\005first.example.com. second.\\007example.com." if rp.String() != expected { t.Errorf("expected %v, got %v", expected, rp) } _, err := NewRR(rp.String()) if err != nil { t.Fatalf("error parsing %q: %v", rp, err) } } func BenchmarkSprintName(b *testing.B) { for n := 0; n < b.N; n++ { got := sprintName("abc\\.def\007\"\127@\255\x05\xef\\") if want := "abc\\.def\\007\\\"W\\@\\173\\005\\239"; got != want { b.Fatalf("expected %q, got %q", want, got) } } } func BenchmarkSprintName_NoEscape(b *testing.B) { for n := 0; n < b.N; n++ { got := sprintName("large.example.com") if want := "large.example.com"; got != want { b.Fatalf("expected %q, got %q", want, got) } } } func BenchmarkSprintTxtOctet(b *testing.B) { for n := 0; n < b.N; n++ { got := sprintTxtOctet("abc\\.def\007\"\127@\255\x05\xef\\") if want := "\"abc\\.def\\007\\\"W@\\173\\005\\239\""; got != want { b.Fatalf("expected %q, got %q", want, got) } } } func BenchmarkSprintTxt(b *testing.B) { txt := []string{ "abc\\.def\007\"\127@\255\x05\xef\\", "example.com", } b.ResetTimer() for n := 0; n < b.N; n++ { got := sprintTxt(txt) if want := "\"abc.def\\007\\\"W@\\173\\005\\239\" \"example.com\""; got != want { b.Fatalf("expected %q, got %q", got, want) } } } golang-github-miekg-dns-1.1.64/udp.go000066400000000000000000000056511476742671700173610ustar00rootroot00000000000000//go:build !windows // +build !windows package dns import ( "net" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" ) // This is the required size of the OOB buffer to pass to ReadMsgUDP. var udpOOBSize = func() int { // We can't know whether we'll get an IPv4 control message or an // IPv6 control message ahead of time. To get around this, we size // the buffer equal to the largest of the two. oob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface) oob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface) if len(oob4) > len(oob6) { return len(oob4) } return len(oob6) }() // SessionUDP holds the remote address and the associated // out-of-band data. type SessionUDP struct { raddr *net.UDPAddr context []byte } // RemoteAddr returns the remote network address. func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } // ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a // net.UDPAddr. func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { oob := make([]byte, udpOOBSize) n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) if err != nil { return n, nil, err } return n, &SessionUDP{raddr, oob[:oobn]}, err } // WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr. func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { oob := correctSource(session.context) n, _, err := conn.WriteMsgUDP(b, oob, session.raddr) return n, err } func setUDPSocketOptions(conn *net.UDPConn) error { // Try setting the flags for both families and ignore the errors unless they // both error. err6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true) err4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true) if err6 != nil && err4 != nil { return err4 } return nil } // parseDstFromOOB takes oob data and returns the destination IP. func parseDstFromOOB(oob []byte) net.IP { // Start with IPv6 and then fallback to IPv4 // TODO(fastest963): Figure out a way to prefer one or the other. Looking at // the lvl of the header for a 0 or 41 isn't cross-platform. cm6 := new(ipv6.ControlMessage) if cm6.Parse(oob) == nil && cm6.Dst != nil { return cm6.Dst } cm4 := new(ipv4.ControlMessage) if cm4.Parse(oob) == nil && cm4.Dst != nil { return cm4.Dst } return nil } // correctSource takes oob data and returns new oob data with the Src equal to the Dst func correctSource(oob []byte) []byte { dst := parseDstFromOOB(oob) if dst == nil { return nil } // If the dst is definitely an IPv6, then use ipv6's ControlMessage to // respond otherwise use ipv4's because ipv6's marshal ignores ipv4 // addresses. if dst.To4() == nil { cm := new(ipv6.ControlMessage) cm.Src = dst oob = cm.Marshal() } else { cm := new(ipv4.ControlMessage) cm.Src = dst oob = cm.Marshal() } return oob } golang-github-miekg-dns-1.1.64/udp_test.go000066400000000000000000000106771476742671700204240ustar00rootroot00000000000000//go:build linux && !appengine // +build linux,!appengine package dns import ( "bytes" "net" "runtime" "strings" "testing" "time" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" ) func TestSetUDPSocketOptions(t *testing.T) { // returns an error if we cannot resolve that address testFamily := func(n, addr string) error { a, err := net.ResolveUDPAddr(n, addr) if err != nil { return err } c, err := net.ListenUDP(n, a) if err != nil { return err } if err := setUDPSocketOptions(c); err != nil { t.Fatalf("failed to set socket options: %v", err) } ch := make(chan *SessionUDP) go func() { // Set some deadline so this goroutine doesn't hang forever c.SetReadDeadline(time.Now().Add(time.Minute)) b := make([]byte, 1) _, sess, err := ReadFromSessionUDP(c, b) if err != nil { t.Errorf("failed to read from conn: %v", err) // fallthrough to chan send below } ch <- sess }() c2, err := net.Dial("udp", c.LocalAddr().String()) if err != nil { t.Fatalf("failed to dial udp: %v", err) } if _, err := c2.Write([]byte{1}); err != nil { t.Fatalf("failed to write to conn: %v", err) } sess := <-ch if sess == nil { // t.Error was already called in the goroutine above. t.FailNow() } if len(sess.context) == 0 { t.Fatalf("empty session context: %v", sess) } ip := parseDstFromOOB(sess.context) if ip == nil { t.Fatalf("failed to parse dst: %v", sess) } if !strings.Contains(c.LocalAddr().String(), ip.String()) { t.Fatalf("dst was different than listen addr: %v != %v", ip.String(), c.LocalAddr().String()) } return nil } // we require that ipv4 be supported if err := testFamily("udp4", "127.0.0.1:0"); err != nil { t.Fatalf("failed to test socket options on IPv4: %v", err) } // IPv6 might not be supported so these will just log if err := testFamily("udp6", "[::1]:0"); err != nil { t.Logf("failed to test socket options on IPv6-only: %v", err) } if err := testFamily("udp", "[::1]:0"); err != nil { t.Logf("failed to test socket options on IPv6/IPv4: %v", err) } } func TestParseDstFromOOB(t *testing.T) { if runtime.GOARCH != "amd64" { // The cmsghdr struct differs in the width (32/64-bit) of // lengths and the struct padding between architectures. // The data below was only written with amd64 in mind, and // thus the test must be skipped on other architectures. t.Skip("skipping test on unsupported architecture") } // dst is :ffff:100.100.100.100 oob := []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} dst := parseDstFromOOB(oob) dst4 := dst.To4() if dst4 == nil { t.Errorf("failed to parse IPv4 in IPv6: %v", dst) } else if dst4.String() != "100.100.100.100" { t.Errorf("unexpected IPv4: %v", dst4) } // dst is 2001:db8::1 oob = []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} dst = parseDstFromOOB(oob) dst6 := dst.To16() if dst6 == nil { t.Errorf("failed to parse IPv6: %v", dst) } else if dst6.String() != "2001:db8::1" { t.Errorf("unexpected IPv6: %v", dst4) } // dst is 100.100.100.100 but was received on 10.10.10.10 oob = []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} dst = parseDstFromOOB(oob) dst4 = dst.To4() if dst4 == nil { t.Errorf("failed to parse IPv4: %v", dst) } else if dst4.String() != "100.100.100.100" { t.Errorf("unexpected IPv4: %v", dst4) } } func TestCorrectSource(t *testing.T) { if runtime.GOARCH != "amd64" { // See comment above in TestParseDstFromOOB. t.Skip("skipping test on unsupported architecture") } // dst is :ffff:100.100.100.100 which should be counted as IPv4 oob := []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} soob := correctSource(oob) cm4 := new(ipv4.ControlMessage) cm4.Src = net.ParseIP("100.100.100.100") if !bytes.Equal(soob, cm4.Marshal()) { t.Errorf("unexpected oob for ipv4 address: %v", soob) } // dst is 2001:db8::1 oob = []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} soob = correctSource(oob) cm6 := new(ipv6.ControlMessage) cm6.Src = net.ParseIP("2001:db8::1") if !bytes.Equal(soob, cm6.Marshal()) { t.Errorf("unexpected oob for IPv6 address: %v", soob) } } golang-github-miekg-dns-1.1.64/udp_windows.go000066400000000000000000000020521476742671700211230ustar00rootroot00000000000000//go:build windows // +build windows // TODO(tmthrgd): Remove this Windows-specific code if go.dev/issue/7175 and // go.dev/issue/7174 are ever fixed. package dns import "net" // SessionUDP holds the remote address type SessionUDP struct { raddr *net.UDPAddr } // RemoteAddr returns the remote network address. func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } // ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a // net.UDPAddr. func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { n, raddr, err := conn.ReadFrom(b) if err != nil { return n, nil, err } return n, &SessionUDP{raddr.(*net.UDPAddr)}, err } // WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr. func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { return conn.WriteTo(b, session.raddr) } func setUDPSocketOptions(*net.UDPConn) error { return nil } func parseDstFromOOB([]byte, net.IP) net.IP { return nil } golang-github-miekg-dns-1.1.64/update.go000066400000000000000000000061551476742671700200530ustar00rootroot00000000000000package dns // NameUsed sets the RRs in the prereq section to // "Name is in use" RRs. RFC 2136 section 2.4.4. func (u *Msg) NameUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) } for _, r := range rr { u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) } } // NameNotUsed sets the RRs in the prereq section to // "Name is in not use" RRs. RFC 2136 section 2.4.5. func (u *Msg) NameNotUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) } for _, r := range rr { u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}}) } } // Used sets the RRs in the prereq section to // "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2. func (u *Msg) Used(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") } if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) } for _, r := range rr { hdr := r.Header() hdr.Class = u.Question[0].Qclass hdr.Ttl = 0 u.Answer = append(u.Answer, r) } } // RRsetUsed sets the RRs in the prereq section to // "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1. func (u *Msg) RRsetUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) } for _, r := range rr { h := r.Header() u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}}) } } // RRsetNotUsed sets the RRs in the prereq section to // "RRset does not exist" RRs. RFC 2136 section 2.4.3. func (u *Msg) RRsetNotUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) } for _, r := range rr { h := r.Header() u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassNONE}}) } } // Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1. func (u *Msg) Insert(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") } if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) } for _, r := range rr { r.Header().Class = u.Question[0].Qclass u.Ns = append(u.Ns, r) } } // RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2. func (u *Msg) RemoveRRset(rr []RR) { if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) } for _, r := range rr { h := r.Header() u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}}) } } // RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3 func (u *Msg) RemoveName(rr []RR) { if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) } for _, r := range rr { u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) } } // Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4 func (u *Msg) Remove(rr []RR) { if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) } for _, r := range rr { h := r.Header() h.Class = ClassNONE h.Ttl = 0 u.Ns = append(u.Ns, r) } } golang-github-miekg-dns-1.1.64/update_test.go000066400000000000000000000105751476742671700211130ustar00rootroot00000000000000package dns import ( "bytes" "testing" ) func TestDynamicUpdateParsing(t *testing.T) { const prefix = "example.com. IN " for typ, name := range TypeToString { switch typ { case TypeNone, TypeReserved: continue case TypeANY: // ANY is ambiguous here and ends up parsed as a CLASS. // // TODO(tmthrgd): Using TYPE255 here doesn't seem to work and also // seems to fail for some other record types. Investigate. continue } s := prefix + name if _, err := NewRR(s); err != nil { t.Errorf("failure to parse: %s: %v", s, err) } s += " \\# 0" if _, err := NewRR(s); err != nil { t.Errorf("failure to parse: %s: %v", s, err) } } } func TestDynamicUpdateUnpack(t *testing.T) { // From https://github.com/miekg/dns/issues/150#issuecomment-62296803 // It should be an update message for the zone "example.", // deleting the A RRset "example." and then adding an A record at "example.". // class ANY, TYPE A buf := []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} msg := new(Msg) err := msg.Unpack(buf) if err != nil { t.Errorf("failed to unpack: %v\n%s", err, msg.String()) } } func TestDynamicUpdateZeroRdataUnpack(t *testing.T) { m := new(Msg) rr := &RR_Header{Name: ".", Rrtype: 0, Class: 1, Ttl: ^uint32(0), Rdlength: 0} m.Answer = []RR{rr, rr, rr, rr, rr} m.Ns = m.Answer for n, s := range TypeToString { rr.Rrtype = n bytes, err := m.Pack() if err != nil { t.Errorf("failed to pack %s: %v", s, err) continue } if err := new(Msg).Unpack(bytes); err != nil { t.Errorf("failed to unpack %s: %v", s, err) } } } func TestRemoveRRset(t *testing.T) { // Should add a zero data RR in Class ANY with a TTL of 0 // for each set mentioned in the RRs provided to it. rr := testRR(". 100 IN A 127.0.0.1") m := new(Msg) m.Ns = []RR{&RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}} expectstr := m.String() expect, err := m.Pack() if err != nil { t.Fatalf("error packing expected msg: %v", err) } m.Ns = nil m.RemoveRRset([]RR{rr}) actual, err := m.Pack() if err != nil { t.Fatalf("error packing actual msg: %v", err) } if !bytes.Equal(actual, expect) { tmp := new(Msg) if err := tmp.Unpack(actual); err != nil { t.Fatalf("error unpacking actual msg: %v\nexpected: %v\ngot: %v\n", err, expect, actual) } t.Errorf("expected msg:\n%s", expectstr) t.Errorf("actual msg:\n%v", tmp) } } func TestPreReqAndRemovals(t *testing.T) { // Build a list of multiple prereqs and then some removes followed by an insert. // We should be able to add multiple prereqs and updates. m := new(Msg) m.SetUpdate("example.org.") m.Id = 1234 // Use a full set of RRs each time, so we are sure the rdata is stripped. rrName1 := testRR("name_used. 3600 IN A 127.0.0.1") rrName2 := testRR("name_not_used. 3600 IN A 127.0.0.1") rrRemove1 := testRR("remove1. 3600 IN A 127.0.0.1") rrRemove2 := testRR("remove2. 3600 IN A 127.0.0.1") rrRemove3 := testRR("remove3. 3600 IN A 127.0.0.1") rrInsert := testRR("insert. 3600 IN A 127.0.0.1") rrRrset1 := testRR("rrset_used1. 3600 IN A 127.0.0.1") rrRrset2 := testRR("rrset_used2. 3600 IN A 127.0.0.1") rrRrset3 := testRR("rrset_not_used. 3600 IN A 127.0.0.1") // Handle the prereqs. m.NameUsed([]RR{rrName1}) m.NameNotUsed([]RR{rrName2}) m.RRsetUsed([]RR{rrRrset1}) m.Used([]RR{rrRrset2}) m.RRsetNotUsed([]RR{rrRrset3}) // and now the updates. m.RemoveName([]RR{rrRemove1}) m.RemoveRRset([]RR{rrRemove2}) m.Remove([]RR{rrRemove3}) m.Insert([]RR{rrInsert}) // This test function isn't a Example function because we print these RR with tabs at the // end and the Example function trim these, thus they never match. // TODO(miek): don't print these tabs and make this into an Example function. expect := `;; opcode: UPDATE, status: NOERROR, id: 1234 ;; flags:; ZONE: 1, PREREQ: 5, UPDATE: 4, ADDITIONAL: 0 ;; ZONE SECTION: ;example.org. IN SOA ;; PREREQUISITE SECTION: name_used. 0 CLASS255 ANY name_not_used. 0 NONE ANY rrset_used1. 0 CLASS255 A rrset_used2. 0 IN A 127.0.0.1 rrset_not_used. 0 NONE A ;; UPDATE SECTION: remove1. 0 CLASS255 ANY remove2. 0 CLASS255 A remove3. 0 NONE A 127.0.0.1 insert. 3600 IN A 127.0.0.1 ` if m.String() != expect { t.Errorf("expected msg:\n%s", expect) t.Errorf("actual msg:\n%v", m.String()) } } golang-github-miekg-dns-1.1.64/version.go000066400000000000000000000004231476742671700202460ustar00rootroot00000000000000package dns import "fmt" // Version is current version of this library. var Version = v{1, 1, 64} // v holds the version of this library. type v struct { Major, Minor, Patch int } func (v v) String() string { return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) } golang-github-miekg-dns-1.1.64/version_test.go000066400000000000000000000002641476742671700213100ustar00rootroot00000000000000package dns import "testing" func TestVersion(t *testing.T) { v := v{1, 0, 0} if x := v.String(); x != "1.0.0" { t.Fatalf("Failed to convert version %v, got: %s", v, x) } } golang-github-miekg-dns-1.1.64/xfr.go000066400000000000000000000166101476742671700173650ustar00rootroot00000000000000package dns import ( "crypto/tls" "fmt" "time" ) // Envelope is used when doing a zone transfer with a remote server. type Envelope struct { RR []RR // The set of RRs in the answer section of the xfr reply message. Error error // If something went wrong, this contains the error. } // A Transfer defines parameters that are used during a zone transfer. type Transfer struct { *Conn DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) tsigTimersOnly bool TLS *tls.Config // TLS config. If Xfr over TLS will be attempted } func (t *Transfer) tsigProvider() TsigProvider { if t.TsigProvider != nil { return t.TsigProvider } if t.TsigSecret != nil { return tsigSecretProvider(t.TsigSecret) } return nil } // TODO: Think we need to away to stop the transfer // In performs an incoming transfer with the server in a. // If you would like to set the source IP, or some other attribute // of a Dialer for a Transfer, you can do so by specifying the attributes // in the Transfer.Conn: // // d := net.Dialer{LocalAddr: transfer_source} // con, err := d.Dial("tcp", master) // dnscon := &dns.Conn{Conn:con} // transfer = &dns.Transfer{Conn: dnscon} // channel, err := transfer.In(message, master) func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { switch q.Question[0].Qtype { case TypeAXFR, TypeIXFR: default: return nil, &Error{"unsupported question type"} } timeout := dnsTimeout if t.DialTimeout != 0 { timeout = t.DialTimeout } if t.Conn == nil { if t.TLS != nil { t.Conn, err = DialTimeoutWithTLS("tcp-tls", a, t.TLS, timeout) } else { t.Conn, err = DialTimeout("tcp", a, timeout) } if err != nil { return nil, err } } if err := t.WriteMsg(q); err != nil { return nil, err } env = make(chan *Envelope) switch q.Question[0].Qtype { case TypeAXFR: go t.inAxfr(q, env) case TypeIXFR: go t.inIxfr(q, env) } return env, nil } func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) { first := true defer func() { // First close the connection, then the channel. This allows functions blocked on // the channel to assume that the connection is closed and no further operations are // pending when they resume. t.Close() close(c) }() timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout } for { t.Conn.SetReadDeadline(time.Now().Add(timeout)) in, err := t.ReadMsg() if err != nil { c <- &Envelope{nil, err} return } if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } if first { if in.Rcode != RcodeSuccess { c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} return } if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} return } first = !first // only one answer that is SOA, receive more if len(in.Answer) == 1 { t.tsigTimersOnly = true c <- &Envelope{in.Answer, nil} continue } } if !first { t.tsigTimersOnly = true // Subsequent envelopes use this. if isSOALast(in) { c <- &Envelope{in.Answer, nil} return } c <- &Envelope{in.Answer, nil} } } } func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { var serial uint32 // The first serial seen is the current server serial axfr := true n := 0 qser := q.Ns[0].(*SOA).Serial defer func() { // First close the connection, then the channel. This allows functions blocked on // the channel to assume that the connection is closed and no further operations are // pending when they resume. t.Close() close(c) }() timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout } for { t.SetReadDeadline(time.Now().Add(timeout)) in, err := t.ReadMsg() if err != nil { c <- &Envelope{nil, err} return } if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } if in.Rcode != RcodeSuccess { c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} return } if n == 0 { // Check if the returned answer is ok if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} return } // This serial is important serial = in.Answer[0].(*SOA).Serial // Check if there are no changes in zone if qser >= serial { c <- &Envelope{in.Answer, nil} return } } // Now we need to check each message for SOA records, to see what we need to do t.tsigTimersOnly = true for _, rr := range in.Answer { if v, ok := rr.(*SOA); ok { if v.Serial == serial { n++ // quit if it's a full axfr or the servers' SOA is repeated the third time if axfr && n == 2 || n == 3 { c <- &Envelope{in.Answer, nil} return } } else if axfr { // it's an ixfr axfr = false } } } c <- &Envelope{in.Answer, nil} } } // Out performs an outgoing transfer with the client connecting in w. // Basic use pattern: // // ch := make(chan *dns.Envelope) // tr := new(dns.Transfer) // var wg sync.WaitGroup // wg.Add(1) // go func() { // tr.Out(w, r, ch) // wg.Done() // }() // ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} // close(ch) // wg.Wait() // wait until everything is written out // w.Close() // close connection // // The server is responsible for sending the correct sequence of RRs through the channel ch. func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { for x := range ch { r := new(Msg) // Compress? r.SetReply(q) r.Authoritative = true // assume it fits TODO(miek): fix r.Answer = append(r.Answer, x.RR...) if tsig := q.IsTsig(); tsig != nil && w.TsigStatus() == nil { r.SetTsig(tsig.Hdr.Name, tsig.Algorithm, tsig.Fudge, time.Now().Unix()) } if err := w.WriteMsg(r); err != nil { return err } w.TsigTimersOnly(true) } return nil } // ReadMsg reads a message from the transfer connection t. func (t *Transfer) ReadMsg() (*Msg, error) { m := new(Msg) p := make([]byte, MaxMsgSize) n, err := t.Read(p) if err != nil && n == 0 { return nil, err } p = p[:n] if err := m.Unpack(p); err != nil { return nil, err } if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil { // Need to work on the original message p, as that was used to calculate the tsig. err = TsigVerifyWithProvider(p, tp, t.tsigRequestMAC, t.tsigTimersOnly) t.tsigRequestMAC = ts.MAC } return m, err } // WriteMsg writes a message through the transfer connection t. func (t *Transfer) WriteMsg(m *Msg) (err error) { var out []byte if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil { out, t.tsigRequestMAC, err = TsigGenerateWithProvider(m, tp, t.tsigRequestMAC, t.tsigTimersOnly) } else { out, err = m.Pack() } if err != nil { return err } _, err = t.Write(out) return err } func isSOAFirst(in *Msg) bool { return len(in.Answer) > 0 && in.Answer[0].Header().Rrtype == TypeSOA } func isSOALast(in *Msg) bool { return len(in.Answer) > 0 && in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA } const errXFR = "bad xfr rcode: %d" golang-github-miekg-dns-1.1.64/xfr_test.go000066400000000000000000000121431476742671700204210ustar00rootroot00000000000000package dns import ( "crypto/tls" "testing" "time" ) var ( tsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} xfrSoa = testRR(`miek.nl. 0 IN SOA linode.atoom.net. miek.miek.nl. 2009032802 21600 7200 604800 3600`) xfrA = testRR(`x.miek.nl. 1792 IN A 10.0.0.1`) xfrMX = testRR(`miek.nl. 1800 IN MX 1 x.miek.nl.`) xfrTestData = []RR{xfrSoa, xfrA, xfrMX, xfrSoa} ) func InvalidXfrServer(w ResponseWriter, req *Msg) { ch := make(chan *Envelope) tr := new(Transfer) go tr.Out(w, req, ch) ch <- &Envelope{RR: []RR{}} close(ch) w.Hijack() } func SingleEnvelopeXfrServer(w ResponseWriter, req *Msg) { ch := make(chan *Envelope) tr := new(Transfer) go tr.Out(w, req, ch) ch <- &Envelope{RR: xfrTestData} close(ch) w.Hijack() } func MultipleEnvelopeXfrServer(w ResponseWriter, req *Msg) { ch := make(chan *Envelope) tr := new(Transfer) go tr.Out(w, req, ch) for _, rr := range xfrTestData { ch <- &Envelope{RR: []RR{rr}} } close(ch) w.Hijack() } func TestInvalidXfr(t *testing.T) { HandleFunc("miek.nl.", InvalidXfrServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %s", err) } defer s.Shutdown() tr := new(Transfer) m := new(Msg) m.SetAxfr("miek.nl.") c, err := tr.In(m, addrstr) if err != nil { t.Fatal("failed to zone transfer in", err) } for msg := range c { if msg.Error == nil { t.Fatal("failed to catch 'no SOA' error") } } } func TestSingleEnvelopeXfr(t *testing.T) { HandleFunc("miek.nl.", SingleEnvelopeXfrServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalTCPServer(":0", func(srv *Server) { srv.TsigSecret = tsigSecret }) if err != nil { t.Fatalf("unable to run test server: %s", err) } defer s.Shutdown() axfrTestingSuite(t, addrstr) } func TestSingleEnvelopeXfrTLS(t *testing.T) { HandleFunc("miek.nl.", SingleEnvelopeXfrServer) defer HandleRemove("miek.nl.") cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) if err != nil { t.Fatalf("unable to build certificate: %v", err) } tlsConfig := tls.Config{ Certificates: []tls.Certificate{cert}, } s, addrstr, _, err := RunLocalTLSServer(":0", &tlsConfig) if err != nil { t.Fatalf("unable to run test server: %s", err) } defer s.Shutdown() axfrTestingSuiteTLS(t, addrstr) } func TestMultiEnvelopeXfr(t *testing.T) { HandleFunc("miek.nl.", MultipleEnvelopeXfrServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalTCPServer(":0", func(srv *Server) { srv.TsigSecret = tsigSecret }) if err != nil { t.Fatalf("unable to run test server: %s", err) } defer s.Shutdown() axfrTestingSuite(t, addrstr) } func axfrTestingSuite(t *testing.T, addrstr string) { tr := new(Transfer) m := new(Msg) m.SetAxfr("miek.nl.") c, err := tr.In(m, addrstr) if err != nil { t.Fatal("failed to zone transfer in", err) } var records []RR for msg := range c { if msg.Error != nil { t.Fatal(msg.Error) } records = append(records, msg.RR...) } if len(records) != len(xfrTestData) { t.Fatalf("bad axfr: expected %v, got %v", records, xfrTestData) } for i, rr := range records { if !IsDuplicate(rr, xfrTestData[i]) { t.Fatalf("bad axfr: expected %v, got %v", records, xfrTestData) } } } func axfrTestingSuiteTLS(t *testing.T, addrstr string) { tr := new(Transfer) m := new(Msg) m.SetAxfr("miek.nl.") tr.TLS = &tls.Config{ InsecureSkipVerify: true, } c, err := tr.In(m, addrstr) if err != nil { t.Fatal("failed to zone transfer in", err) } var records []RR for msg := range c { if msg.Error != nil { t.Fatal(msg.Error) } records = append(records, msg.RR...) } if len(records) != len(xfrTestData) { t.Fatalf("bad axfr: expected %v, got %v", records, xfrTestData) } for i, rr := range records { if !IsDuplicate(rr, xfrTestData[i]) { t.Fatalf("bad axfr: expected %v, got %v", records, xfrTestData) } } } func axfrTestingSuiteWithCustomTsig(t *testing.T, addrstr string, provider TsigProvider) { tr := new(Transfer) m := new(Msg) var err error tr.Conn, err = Dial("tcp", addrstr) if err != nil { t.Fatal("failed to dial", err) } tr.TsigProvider = provider m.SetAxfr("miek.nl.") m.SetTsig("axfr.", HmacSHA256, 300, time.Now().Unix()) c, err := tr.In(m, addrstr) if err != nil { t.Fatal("failed to zone transfer in", err) } var records []RR for msg := range c { if msg.Error != nil { t.Fatal(msg.Error) } records = append(records, msg.RR...) } if len(records) != len(xfrTestData) { t.Fatalf("bad axfr: expected %v, got %v", records, xfrTestData) } for i, rr := range records { if !IsDuplicate(rr, xfrTestData[i]) { t.Errorf("bad axfr: expected %v, got %v", records, xfrTestData) } } } func TestCustomTsigProvider(t *testing.T) { HandleFunc("miek.nl.", SingleEnvelopeXfrServer) defer HandleRemove("miek.nl.") s, addrstr, _, err := RunLocalTCPServer(":0", func(srv *Server) { srv.TsigProvider = tsigSecretProvider(tsigSecret) }) if err != nil { t.Fatalf("unable to run test server: %s", err) } defer s.Shutdown() axfrTestingSuiteWithCustomTsig(t, addrstr, tsigSecretProvider(tsigSecret)) } golang-github-miekg-dns-1.1.64/zduplicate.go000066400000000000000000000543211476742671700207330ustar00rootroot00000000000000// Code generated by "go run duplicate_generate.go"; DO NOT EDIT. package dns // isDuplicate() functions func (r1 *A) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*A) if !ok { return false } _ = r2 if !r1.A.Equal(r2.A) { return false } return true } func (r1 *AAAA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*AAAA) if !ok { return false } _ = r2 if !r1.AAAA.Equal(r2.AAAA) { return false } return true } func (r1 *AFSDB) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*AFSDB) if !ok { return false } _ = r2 if r1.Subtype != r2.Subtype { return false } if !isDuplicateName(r1.Hostname, r2.Hostname) { return false } return true } func (r1 *AMTRELAY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*AMTRELAY) if !ok { return false } _ = r2 if r1.Precedence != r2.Precedence { return false } if r1.GatewayType != r2.GatewayType { return false } switch r1.GatewayType { case IPSECGatewayIPv4, IPSECGatewayIPv6: if !r1.GatewayAddr.Equal(r2.GatewayAddr) { return false } case IPSECGatewayHost: if !isDuplicateName(r1.GatewayHost, r2.GatewayHost) { return false } } return true } func (r1 *ANY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*ANY) if !ok { return false } _ = r2 return true } func (r1 *APL) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*APL) if !ok { return false } _ = r2 if len(r1.Prefixes) != len(r2.Prefixes) { return false } for i := 0; i < len(r1.Prefixes); i++ { if !r1.Prefixes[i].equals(&r2.Prefixes[i]) { return false } } return true } func (r1 *AVC) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*AVC) if !ok { return false } _ = r2 if len(r1.Txt) != len(r2.Txt) { return false } for i := 0; i < len(r1.Txt); i++ { if r1.Txt[i] != r2.Txt[i] { return false } } return true } func (r1 *CAA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CAA) if !ok { return false } _ = r2 if r1.Flag != r2.Flag { return false } if r1.Tag != r2.Tag { return false } if r1.Value != r2.Value { return false } return true } func (r1 *CDNSKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CDNSKEY) if !ok { return false } _ = r2 if r1.Flags != r2.Flags { return false } if r1.Protocol != r2.Protocol { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.PublicKey != r2.PublicKey { return false } return true } func (r1 *CDS) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CDS) if !ok { return false } _ = r2 if r1.KeyTag != r2.KeyTag { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.DigestType != r2.DigestType { return false } if r1.Digest != r2.Digest { return false } return true } func (r1 *CERT) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CERT) if !ok { return false } _ = r2 if r1.Type != r2.Type { return false } if r1.KeyTag != r2.KeyTag { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.Certificate != r2.Certificate { return false } return true } func (r1 *CNAME) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CNAME) if !ok { return false } _ = r2 if !isDuplicateName(r1.Target, r2.Target) { return false } return true } func (r1 *CSYNC) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CSYNC) if !ok { return false } _ = r2 if r1.Serial != r2.Serial { return false } if r1.Flags != r2.Flags { return false } if len(r1.TypeBitMap) != len(r2.TypeBitMap) { return false } for i := 0; i < len(r1.TypeBitMap); i++ { if r1.TypeBitMap[i] != r2.TypeBitMap[i] { return false } } return true } func (r1 *DHCID) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*DHCID) if !ok { return false } _ = r2 if r1.Digest != r2.Digest { return false } return true } func (r1 *DLV) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*DLV) if !ok { return false } _ = r2 if r1.KeyTag != r2.KeyTag { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.DigestType != r2.DigestType { return false } if r1.Digest != r2.Digest { return false } return true } func (r1 *DNAME) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*DNAME) if !ok { return false } _ = r2 if !isDuplicateName(r1.Target, r2.Target) { return false } return true } func (r1 *DNSKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*DNSKEY) if !ok { return false } _ = r2 if r1.Flags != r2.Flags { return false } if r1.Protocol != r2.Protocol { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.PublicKey != r2.PublicKey { return false } return true } func (r1 *DS) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*DS) if !ok { return false } _ = r2 if r1.KeyTag != r2.KeyTag { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.DigestType != r2.DigestType { return false } if r1.Digest != r2.Digest { return false } return true } func (r1 *EID) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*EID) if !ok { return false } _ = r2 if r1.Endpoint != r2.Endpoint { return false } return true } func (r1 *EUI48) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*EUI48) if !ok { return false } _ = r2 if r1.Address != r2.Address { return false } return true } func (r1 *EUI64) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*EUI64) if !ok { return false } _ = r2 if r1.Address != r2.Address { return false } return true } func (r1 *GID) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*GID) if !ok { return false } _ = r2 if r1.Gid != r2.Gid { return false } return true } func (r1 *GPOS) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*GPOS) if !ok { return false } _ = r2 if r1.Longitude != r2.Longitude { return false } if r1.Latitude != r2.Latitude { return false } if r1.Altitude != r2.Altitude { return false } return true } func (r1 *HINFO) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*HINFO) if !ok { return false } _ = r2 if r1.Cpu != r2.Cpu { return false } if r1.Os != r2.Os { return false } return true } func (r1 *HIP) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*HIP) if !ok { return false } _ = r2 if r1.HitLength != r2.HitLength { return false } if r1.PublicKeyAlgorithm != r2.PublicKeyAlgorithm { return false } if r1.PublicKeyLength != r2.PublicKeyLength { return false } if r1.Hit != r2.Hit { return false } if r1.PublicKey != r2.PublicKey { return false } if len(r1.RendezvousServers) != len(r2.RendezvousServers) { return false } for i := 0; i < len(r1.RendezvousServers); i++ { if !isDuplicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) { return false } } return true } func (r1 *HTTPS) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*HTTPS) if !ok { return false } _ = r2 if r1.Priority != r2.Priority { return false } if !isDuplicateName(r1.Target, r2.Target) { return false } if len(r1.Value) != len(r2.Value) { return false } if !areSVCBPairArraysEqual(r1.Value, r2.Value) { return false } return true } func (r1 *IPSECKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*IPSECKEY) if !ok { return false } _ = r2 if r1.Precedence != r2.Precedence { return false } if r1.GatewayType != r2.GatewayType { return false } if r1.Algorithm != r2.Algorithm { return false } switch r1.GatewayType { case IPSECGatewayIPv4, IPSECGatewayIPv6: if !r1.GatewayAddr.Equal(r2.GatewayAddr) { return false } case IPSECGatewayHost: if !isDuplicateName(r1.GatewayHost, r2.GatewayHost) { return false } } if r1.PublicKey != r2.PublicKey { return false } return true } func (r1 *ISDN) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*ISDN) if !ok { return false } _ = r2 if r1.Address != r2.Address { return false } if r1.SubAddress != r2.SubAddress { return false } return true } func (r1 *KEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*KEY) if !ok { return false } _ = r2 if r1.Flags != r2.Flags { return false } if r1.Protocol != r2.Protocol { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.PublicKey != r2.PublicKey { return false } return true } func (r1 *KX) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*KX) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if !isDuplicateName(r1.Exchanger, r2.Exchanger) { return false } return true } func (r1 *L32) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*L32) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if !r1.Locator32.Equal(r2.Locator32) { return false } return true } func (r1 *L64) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*L64) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if r1.Locator64 != r2.Locator64 { return false } return true } func (r1 *LOC) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*LOC) if !ok { return false } _ = r2 if r1.Version != r2.Version { return false } if r1.Size != r2.Size { return false } if r1.HorizPre != r2.HorizPre { return false } if r1.VertPre != r2.VertPre { return false } if r1.Latitude != r2.Latitude { return false } if r1.Longitude != r2.Longitude { return false } if r1.Altitude != r2.Altitude { return false } return true } func (r1 *LP) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*LP) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if !isDuplicateName(r1.Fqdn, r2.Fqdn) { return false } return true } func (r1 *MB) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MB) if !ok { return false } _ = r2 if !isDuplicateName(r1.Mb, r2.Mb) { return false } return true } func (r1 *MD) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MD) if !ok { return false } _ = r2 if !isDuplicateName(r1.Md, r2.Md) { return false } return true } func (r1 *MF) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MF) if !ok { return false } _ = r2 if !isDuplicateName(r1.Mf, r2.Mf) { return false } return true } func (r1 *MG) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MG) if !ok { return false } _ = r2 if !isDuplicateName(r1.Mg, r2.Mg) { return false } return true } func (r1 *MINFO) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MINFO) if !ok { return false } _ = r2 if !isDuplicateName(r1.Rmail, r2.Rmail) { return false } if !isDuplicateName(r1.Email, r2.Email) { return false } return true } func (r1 *MR) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MR) if !ok { return false } _ = r2 if !isDuplicateName(r1.Mr, r2.Mr) { return false } return true } func (r1 *MX) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*MX) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if !isDuplicateName(r1.Mx, r2.Mx) { return false } return true } func (r1 *NAPTR) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NAPTR) if !ok { return false } _ = r2 if r1.Order != r2.Order { return false } if r1.Preference != r2.Preference { return false } if r1.Flags != r2.Flags { return false } if r1.Service != r2.Service { return false } if r1.Regexp != r2.Regexp { return false } if !isDuplicateName(r1.Replacement, r2.Replacement) { return false } return true } func (r1 *NID) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NID) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if r1.NodeID != r2.NodeID { return false } return true } func (r1 *NIMLOC) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NIMLOC) if !ok { return false } _ = r2 if r1.Locator != r2.Locator { return false } return true } func (r1 *NINFO) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NINFO) if !ok { return false } _ = r2 if len(r1.ZSData) != len(r2.ZSData) { return false } for i := 0; i < len(r1.ZSData); i++ { if r1.ZSData[i] != r2.ZSData[i] { return false } } return true } func (r1 *NS) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NS) if !ok { return false } _ = r2 if !isDuplicateName(r1.Ns, r2.Ns) { return false } return true } func (r1 *NSAPPTR) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NSAPPTR) if !ok { return false } _ = r2 if !isDuplicateName(r1.Ptr, r2.Ptr) { return false } return true } func (r1 *NSEC) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NSEC) if !ok { return false } _ = r2 if !isDuplicateName(r1.NextDomain, r2.NextDomain) { return false } if len(r1.TypeBitMap) != len(r2.TypeBitMap) { return false } for i := 0; i < len(r1.TypeBitMap); i++ { if r1.TypeBitMap[i] != r2.TypeBitMap[i] { return false } } return true } func (r1 *NSEC3) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NSEC3) if !ok { return false } _ = r2 if r1.Hash != r2.Hash { return false } if r1.Flags != r2.Flags { return false } if r1.Iterations != r2.Iterations { return false } if r1.SaltLength != r2.SaltLength { return false } if r1.Salt != r2.Salt { return false } if r1.HashLength != r2.HashLength { return false } if r1.NextDomain != r2.NextDomain { return false } if len(r1.TypeBitMap) != len(r2.TypeBitMap) { return false } for i := 0; i < len(r1.TypeBitMap); i++ { if r1.TypeBitMap[i] != r2.TypeBitMap[i] { return false } } return true } func (r1 *NSEC3PARAM) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NSEC3PARAM) if !ok { return false } _ = r2 if r1.Hash != r2.Hash { return false } if r1.Flags != r2.Flags { return false } if r1.Iterations != r2.Iterations { return false } if r1.SaltLength != r2.SaltLength { return false } if r1.Salt != r2.Salt { return false } return true } func (r1 *NULL) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NULL) if !ok { return false } _ = r2 if r1.Data != r2.Data { return false } return true } func (r1 *NXNAME) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NXNAME) if !ok { return false } _ = r2 return true } func (r1 *NXT) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*NXT) if !ok { return false } _ = r2 if !isDuplicateName(r1.NextDomain, r2.NextDomain) { return false } if len(r1.TypeBitMap) != len(r2.TypeBitMap) { return false } for i := 0; i < len(r1.TypeBitMap); i++ { if r1.TypeBitMap[i] != r2.TypeBitMap[i] { return false } } return true } func (r1 *OPENPGPKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*OPENPGPKEY) if !ok { return false } _ = r2 if r1.PublicKey != r2.PublicKey { return false } return true } func (r1 *PTR) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*PTR) if !ok { return false } _ = r2 if !isDuplicateName(r1.Ptr, r2.Ptr) { return false } return true } func (r1 *PX) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*PX) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if !isDuplicateName(r1.Map822, r2.Map822) { return false } if !isDuplicateName(r1.Mapx400, r2.Mapx400) { return false } return true } func (r1 *RESINFO) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RESINFO) if !ok { return false } _ = r2 if len(r1.Txt) != len(r2.Txt) { return false } for i := 0; i < len(r1.Txt); i++ { if r1.Txt[i] != r2.Txt[i] { return false } } return true } func (r1 *RFC3597) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RFC3597) if !ok { return false } _ = r2 if r1.Rdata != r2.Rdata { return false } return true } func (r1 *RKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RKEY) if !ok { return false } _ = r2 if r1.Flags != r2.Flags { return false } if r1.Protocol != r2.Protocol { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.PublicKey != r2.PublicKey { return false } return true } func (r1 *RP) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RP) if !ok { return false } _ = r2 if !isDuplicateName(r1.Mbox, r2.Mbox) { return false } if !isDuplicateName(r1.Txt, r2.Txt) { return false } return true } func (r1 *RRSIG) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RRSIG) if !ok { return false } _ = r2 if r1.TypeCovered != r2.TypeCovered { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.Labels != r2.Labels { return false } if r1.OrigTtl != r2.OrigTtl { return false } if r1.Expiration != r2.Expiration { return false } if r1.Inception != r2.Inception { return false } if r1.KeyTag != r2.KeyTag { return false } if !isDuplicateName(r1.SignerName, r2.SignerName) { return false } if r1.Signature != r2.Signature { return false } return true } func (r1 *RT) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*RT) if !ok { return false } _ = r2 if r1.Preference != r2.Preference { return false } if !isDuplicateName(r1.Host, r2.Host) { return false } return true } func (r1 *SIG) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SIG) if !ok { return false } _ = r2 if r1.TypeCovered != r2.TypeCovered { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.Labels != r2.Labels { return false } if r1.OrigTtl != r2.OrigTtl { return false } if r1.Expiration != r2.Expiration { return false } if r1.Inception != r2.Inception { return false } if r1.KeyTag != r2.KeyTag { return false } if !isDuplicateName(r1.SignerName, r2.SignerName) { return false } if r1.Signature != r2.Signature { return false } return true } func (r1 *SMIMEA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SMIMEA) if !ok { return false } _ = r2 if r1.Usage != r2.Usage { return false } if r1.Selector != r2.Selector { return false } if r1.MatchingType != r2.MatchingType { return false } if r1.Certificate != r2.Certificate { return false } return true } func (r1 *SOA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SOA) if !ok { return false } _ = r2 if !isDuplicateName(r1.Ns, r2.Ns) { return false } if !isDuplicateName(r1.Mbox, r2.Mbox) { return false } if r1.Serial != r2.Serial { return false } if r1.Refresh != r2.Refresh { return false } if r1.Retry != r2.Retry { return false } if r1.Expire != r2.Expire { return false } if r1.Minttl != r2.Minttl { return false } return true } func (r1 *SPF) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SPF) if !ok { return false } _ = r2 if len(r1.Txt) != len(r2.Txt) { return false } for i := 0; i < len(r1.Txt); i++ { if r1.Txt[i] != r2.Txt[i] { return false } } return true } func (r1 *SRV) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SRV) if !ok { return false } _ = r2 if r1.Priority != r2.Priority { return false } if r1.Weight != r2.Weight { return false } if r1.Port != r2.Port { return false } if !isDuplicateName(r1.Target, r2.Target) { return false } return true } func (r1 *SSHFP) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SSHFP) if !ok { return false } _ = r2 if r1.Algorithm != r2.Algorithm { return false } if r1.Type != r2.Type { return false } if r1.FingerPrint != r2.FingerPrint { return false } return true } func (r1 *SVCB) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SVCB) if !ok { return false } _ = r2 if r1.Priority != r2.Priority { return false } if !isDuplicateName(r1.Target, r2.Target) { return false } if len(r1.Value) != len(r2.Value) { return false } if !areSVCBPairArraysEqual(r1.Value, r2.Value) { return false } return true } func (r1 *TA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*TA) if !ok { return false } _ = r2 if r1.KeyTag != r2.KeyTag { return false } if r1.Algorithm != r2.Algorithm { return false } if r1.DigestType != r2.DigestType { return false } if r1.Digest != r2.Digest { return false } return true } func (r1 *TALINK) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*TALINK) if !ok { return false } _ = r2 if !isDuplicateName(r1.PreviousName, r2.PreviousName) { return false } if !isDuplicateName(r1.NextName, r2.NextName) { return false } return true } func (r1 *TKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*TKEY) if !ok { return false } _ = r2 if !isDuplicateName(r1.Algorithm, r2.Algorithm) { return false } if r1.Inception != r2.Inception { return false } if r1.Expiration != r2.Expiration { return false } if r1.Mode != r2.Mode { return false } if r1.Error != r2.Error { return false } if r1.KeySize != r2.KeySize { return false } if r1.Key != r2.Key { return false } if r1.OtherLen != r2.OtherLen { return false } if r1.OtherData != r2.OtherData { return false } return true } func (r1 *TLSA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*TLSA) if !ok { return false } _ = r2 if r1.Usage != r2.Usage { return false } if r1.Selector != r2.Selector { return false } if r1.MatchingType != r2.MatchingType { return false } if r1.Certificate != r2.Certificate { return false } return true } func (r1 *TSIG) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*TSIG) if !ok { return false } _ = r2 if !isDuplicateName(r1.Algorithm, r2.Algorithm) { return false } if r1.TimeSigned != r2.TimeSigned { return false } if r1.Fudge != r2.Fudge { return false } if r1.MACSize != r2.MACSize { return false } if r1.MAC != r2.MAC { return false } if r1.OrigId != r2.OrigId { return false } if r1.Error != r2.Error { return false } if r1.OtherLen != r2.OtherLen { return false } if r1.OtherData != r2.OtherData { return false } return true } func (r1 *TXT) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*TXT) if !ok { return false } _ = r2 if len(r1.Txt) != len(r2.Txt) { return false } for i := 0; i < len(r1.Txt); i++ { if r1.Txt[i] != r2.Txt[i] { return false } } return true } func (r1 *UID) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*UID) if !ok { return false } _ = r2 if r1.Uid != r2.Uid { return false } return true } func (r1 *UINFO) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*UINFO) if !ok { return false } _ = r2 if r1.Uinfo != r2.Uinfo { return false } return true } func (r1 *URI) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*URI) if !ok { return false } _ = r2 if r1.Priority != r2.Priority { return false } if r1.Weight != r2.Weight { return false } if r1.Target != r2.Target { return false } return true } func (r1 *X25) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*X25) if !ok { return false } _ = r2 if r1.PSDNAddress != r2.PSDNAddress { return false } return true } func (r1 *ZONEMD) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*ZONEMD) if !ok { return false } _ = r2 if r1.Serial != r2.Serial { return false } if r1.Scheme != r2.Scheme { return false } if r1.Hash != r2.Hash { return false } if r1.Digest != r2.Digest { return false } return true } golang-github-miekg-dns-1.1.64/zmsg.go000066400000000000000000002001301476742671700175360ustar00rootroot00000000000000// Code generated by "go run msg_generate.go"; DO NOT EDIT. package dns // pack*() functions func (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDataA(rr.A, msg, off) if err != nil { return off, err } return off, nil } func (rr *AAAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDataAAAA(rr.AAAA, msg, off) if err != nil { return off, err } return off, nil } func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Subtype, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Hostname, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *AMTRELAY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Precedence, msg, off) if err != nil { return off, err } off, err = packUint8(rr.GatewayType, msg, off) if err != nil { return off, err } off, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false) if err != nil { return off, err } return off, nil } func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { return off, nil } func (rr *APL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDataApl(rr.Prefixes, msg, off) if err != nil { return off, err } return off, nil } func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringTxt(rr.Txt, msg, off) if err != nil { return off, err } return off, nil } func (rr *CAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Flag, msg, off) if err != nil { return off, err } off, err = packString(rr.Tag, msg, off) if err != nil { return off, err } off, err = packStringOctet(rr.Value, msg, off) if err != nil { return off, err } return off, nil } func (rr *CDNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Flags, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } return off, nil } func (rr *CDS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { return off, err } return off, nil } func (rr *CERT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Type, msg, off) if err != nil { return off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packStringBase64(rr.Certificate, msg, off) if err != nil { return off, err } return off, nil } func (rr *CNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Target, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *CSYNC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint32(rr.Serial, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Flags, msg, off) if err != nil { return off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { return off, err } return off, nil } func (rr *DHCID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringBase64(rr.Digest, msg, off) if err != nil { return off, err } return off, nil } func (rr *DLV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { return off, err } return off, nil } func (rr *DNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *DNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Flags, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } return off, nil } func (rr *DS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { return off, err } return off, nil } func (rr *EID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringHex(rr.Endpoint, msg, off) if err != nil { return off, err } return off, nil } func (rr *EUI48) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint48(rr.Address, msg, off) if err != nil { return off, err } return off, nil } func (rr *EUI64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint64(rr.Address, msg, off) if err != nil { return off, err } return off, nil } func (rr *GID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint32(rr.Gid, msg, off) if err != nil { return off, err } return off, nil } func (rr *GPOS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packString(rr.Longitude, msg, off) if err != nil { return off, err } off, err = packString(rr.Latitude, msg, off) if err != nil { return off, err } off, err = packString(rr.Altitude, msg, off) if err != nil { return off, err } return off, nil } func (rr *HINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packString(rr.Cpu, msg, off) if err != nil { return off, err } off, err = packString(rr.Os, msg, off) if err != nil { return off, err } return off, nil } func (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.HitLength, msg, off) if err != nil { return off, err } off, err = packUint8(rr.PublicKeyAlgorithm, msg, off) if err != nil { return off, err } off, err = packUint16(rr.PublicKeyLength, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Hit, msg, off) if err != nil { return off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *HTTPS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Priority, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { return off, err } off, err = packDataSVCB(rr.Value, msg, off) if err != nil { return off, err } return off, nil } func (rr *IPSECKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Precedence, msg, off) if err != nil { return off, err } off, err = packUint8(rr.GatewayType, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false) if err != nil { return off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } return off, nil } func (rr *ISDN) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packString(rr.Address, msg, off) if err != nil { return off, err } off, err = packString(rr.SubAddress, msg, off) if err != nil { return off, err } return off, nil } func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Flags, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } return off, nil } func (rr *KX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Exchanger, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *L32) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packDataA(rr.Locator32, msg, off) if err != nil { return off, err } return off, nil } func (rr *L64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packUint64(rr.Locator64, msg, off) if err != nil { return off, err } return off, nil } func (rr *LOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Version, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Size, msg, off) if err != nil { return off, err } off, err = packUint8(rr.HorizPre, msg, off) if err != nil { return off, err } off, err = packUint8(rr.VertPre, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Latitude, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Longitude, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Altitude, msg, off) if err != nil { return off, err } return off, nil } func (rr *LP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Fqdn, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *MB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Mb, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *MD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Md, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *MF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Mf, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *MG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Mg, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *MINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Rmail, msg, off, compression, compress) if err != nil { return off, err } off, err = packDomainName(rr.Email, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *MR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Mr, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *MX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Mx, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *NAPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Order, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packString(rr.Flags, msg, off) if err != nil { return off, err } off, err = packString(rr.Service, msg, off) if err != nil { return off, err } off, err = packString(rr.Regexp, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Replacement, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *NID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packUint64(rr.NodeID, msg, off) if err != nil { return off, err } return off, nil } func (rr *NIMLOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringHex(rr.Locator, msg, off) if err != nil { return off, err } return off, nil } func (rr *NINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringTxt(rr.ZSData, msg, off) if err != nil { return off, err } return off, nil } func (rr *NS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Ns, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *NSAPPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Ptr, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *NSEC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.NextDomain, msg, off, compression, false) if err != nil { return off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { return off, err } return off, nil } func (rr *NSEC3) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Hash, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Flags, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Iterations, msg, off) if err != nil { return off, err } off, err = packUint8(rr.SaltLength, msg, off) if err != nil { return off, err } // Only pack salt if value is not "-", i.e. empty if rr.Salt != "-" { off, err = packStringHex(rr.Salt, msg, off) if err != nil { return off, err } } off, err = packUint8(rr.HashLength, msg, off) if err != nil { return off, err } off, err = packStringBase32(rr.NextDomain, msg, off) if err != nil { return off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { return off, err } return off, nil } func (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Hash, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Flags, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Iterations, msg, off) if err != nil { return off, err } off, err = packUint8(rr.SaltLength, msg, off) if err != nil { return off, err } // Only pack salt if value is not "-", i.e. empty if rr.Salt != "-" { off, err = packStringHex(rr.Salt, msg, off) if err != nil { return off, err } } return off, nil } func (rr *NULL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringAny(rr.Data, msg, off) if err != nil { return off, err } return off, nil } func (rr *NXNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { return off, nil } func (rr *NXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.NextDomain, msg, off, compression, false) if err != nil { return off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { return off, err } return off, nil } func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } return off, nil } func (rr *OPT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDataOpt(rr.Option, msg, off) if err != nil { return off, err } return off, nil } func (rr *PTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Ptr, msg, off, compression, compress) if err != nil { return off, err } return off, nil } func (rr *PX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Map822, msg, off, compression, false) if err != nil { return off, err } off, err = packDomainName(rr.Mapx400, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *RESINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringTxt(rr.Txt, msg, off) if err != nil { return off, err } return off, nil } func (rr *RFC3597) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringHex(rr.Rdata, msg, off) if err != nil { return off, err } return off, nil } func (rr *RKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Flags, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { return off, err } return off, nil } func (rr *RP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Mbox, msg, off, compression, false) if err != nil { return off, err } off, err = packDomainName(rr.Txt, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *RRSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.TypeCovered, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Labels, msg, off) if err != nil { return off, err } off, err = packUint32(rr.OrigTtl, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { return off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.SignerName, msg, off, compression, false) if err != nil { return off, err } off, err = packStringBase64(rr.Signature, msg, off) if err != nil { return off, err } return off, nil } func (rr *RT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Preference, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Host, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *SIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.TypeCovered, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Labels, msg, off) if err != nil { return off, err } off, err = packUint32(rr.OrigTtl, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { return off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.SignerName, msg, off, compression, false) if err != nil { return off, err } off, err = packStringBase64(rr.Signature, msg, off) if err != nil { return off, err } return off, nil } func (rr *SMIMEA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Usage, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Selector, msg, off) if err != nil { return off, err } off, err = packUint8(rr.MatchingType, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Certificate, msg, off) if err != nil { return off, err } return off, nil } func (rr *SOA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Ns, msg, off, compression, compress) if err != nil { return off, err } off, err = packDomainName(rr.Mbox, msg, off, compression, compress) if err != nil { return off, err } off, err = packUint32(rr.Serial, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Refresh, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Retry, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Expire, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Minttl, msg, off) if err != nil { return off, err } return off, nil } func (rr *SPF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringTxt(rr.Txt, msg, off) if err != nil { return off, err } return off, nil } func (rr *SRV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Priority, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Weight, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Port, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Type, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.FingerPrint, msg, off) if err != nil { return off, err } return off, nil } func (rr *SVCB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Priority, msg, off) if err != nil { return off, err } off, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { return off, err } off, err = packDataSVCB(rr.Value, msg, off) if err != nil { return off, err } return off, nil } func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.KeyTag, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { return off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { return off, err } return off, nil } func (rr *TALINK) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.PreviousName, msg, off, compression, false) if err != nil { return off, err } off, err = packDomainName(rr.NextName, msg, off, compression, false) if err != nil { return off, err } return off, nil } func (rr *TKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Algorithm, msg, off, compression, false) if err != nil { return off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { return off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Mode, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Error, msg, off) if err != nil { return off, err } off, err = packUint16(rr.KeySize, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Key, msg, off) if err != nil { return off, err } off, err = packUint16(rr.OtherLen, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.OtherData, msg, off) if err != nil { return off, err } return off, nil } func (rr *TLSA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint8(rr.Usage, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Selector, msg, off) if err != nil { return off, err } off, err = packUint8(rr.MatchingType, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Certificate, msg, off) if err != nil { return off, err } return off, nil } func (rr *TSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packDomainName(rr.Algorithm, msg, off, compression, false) if err != nil { return off, err } off, err = packUint48(rr.TimeSigned, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Fudge, msg, off) if err != nil { return off, err } off, err = packUint16(rr.MACSize, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.MAC, msg, off) if err != nil { return off, err } off, err = packUint16(rr.OrigId, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Error, msg, off) if err != nil { return off, err } off, err = packUint16(rr.OtherLen, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.OtherData, msg, off) if err != nil { return off, err } return off, nil } func (rr *TXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringTxt(rr.Txt, msg, off) if err != nil { return off, err } return off, nil } func (rr *UID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint32(rr.Uid, msg, off) if err != nil { return off, err } return off, nil } func (rr *UINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packString(rr.Uinfo, msg, off) if err != nil { return off, err } return off, nil } func (rr *URI) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Priority, msg, off) if err != nil { return off, err } off, err = packUint16(rr.Weight, msg, off) if err != nil { return off, err } off, err = packStringOctet(rr.Target, msg, off) if err != nil { return off, err } return off, nil } func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packString(rr.PSDNAddress, msg, off) if err != nil { return off, err } return off, nil } func (rr *ZONEMD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint32(rr.Serial, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Scheme, msg, off) if err != nil { return off, err } off, err = packUint8(rr.Hash, msg, off) if err != nil { return off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { return off, err } return off, nil } // unpack*() functions func (rr *A) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.A, off, err = unpackDataA(msg, off) if err != nil { return off, err } return off, nil } func (rr *AAAA) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.AAAA, off, err = unpackDataAAAA(msg, off) if err != nil { return off, err } return off, nil } func (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Subtype, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Hostname, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *AMTRELAY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Precedence, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.GatewayType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } if off == len(msg) { return off, nil } rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) if err != nil { return off, err } return off, nil } func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart return off, nil } func (rr *APL) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Prefixes, off, err = unpackDataApl(msg, off) if err != nil { return off, err } return off, nil } func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { return off, err } return off, nil } func (rr *CAA) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Flag, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Tag, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Value, off, err = unpackStringOctet(msg, off) if err != nil { return off, err } return off, nil } func (rr *CDNSKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Flags, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *CDS) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *CERT) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Type, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Certificate, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *CNAME) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *CSYNC) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Serial, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { return off, err } return off, nil } func (rr *DHCID) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Digest, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *DLV) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *DNAME) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *DNSKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Flags, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *DS) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *EID) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Endpoint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *EUI48) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Address, off, err = unpackUint48(msg, off) if err != nil { return off, err } return off, nil } func (rr *EUI64) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Address, off, err = unpackUint64(msg, off) if err != nil { return off, err } return off, nil } func (rr *GID) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Gid, off, err = unpackUint32(msg, off) if err != nil { return off, err } return off, nil } func (rr *GPOS) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Longitude, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Latitude, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Altitude, off, err = unpackString(msg, off) if err != nil { return off, err } return off, nil } func (rr *HINFO) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Cpu, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Os, off, err = unpackString(msg, off) if err != nil { return off, err } return off, nil } func (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.HitLength, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKeyAlgorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKeyLength, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Hit, off, err = unpackStringHex(msg, off, off+int(rr.HitLength)) if err != nil { return off, err } rr.PublicKey, off, err = unpackStringBase64(msg, off, off+int(rr.PublicKeyLength)) if err != nil { return off, err } rr.RendezvousServers, off, err = unpackDataDomainNames(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *HTTPS) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Priority, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Value, off, err = unpackDataSVCB(msg, off) if err != nil { return off, err } return off, nil } func (rr *IPSECKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Precedence, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.GatewayType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } if off == len(msg) { return off, nil } rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *ISDN) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Address, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.SubAddress, off, err = unpackString(msg, off) if err != nil { return off, err } return off, nil } func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Flags, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *KX) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Exchanger, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *L32) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Locator32, off, err = unpackDataA(msg, off) if err != nil { return off, err } return off, nil } func (rr *L64) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Locator64, off, err = unpackUint64(msg, off) if err != nil { return off, err } return off, nil } func (rr *LOC) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Version, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Size, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.HorizPre, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.VertPre, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Latitude, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Longitude, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Altitude, off, err = unpackUint32(msg, off) if err != nil { return off, err } return off, nil } func (rr *LP) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Fqdn, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MB) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Mb, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MD) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Md, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MF) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Mf, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MG) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Mg, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MINFO) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Rmail, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Email, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MR) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Mr, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *MX) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Mx, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *NAPTR) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Order, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Service, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Regexp, off, err = unpackString(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Replacement, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *NID) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.NodeID, off, err = unpackUint64(msg, off) if err != nil { return off, err } return off, nil } func (rr *NIMLOC) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Locator, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *NINFO) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.ZSData, off, err = unpackStringTxt(msg, off) if err != nil { return off, err } return off, nil } func (rr *NS) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Ns, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *NSAPPTR) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Ptr, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *NSEC) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.NextDomain, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { return off, err } return off, nil } func (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Hash, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Iterations, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.SaltLength, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength)) if err != nil { return off, err } rr.HashLength, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.NextDomain, off, err = unpackStringBase32(msg, off, off+int(rr.HashLength)) if err != nil { return off, err } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { return off, err } return off, nil } func (rr *NSEC3PARAM) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Hash, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Iterations, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.SaltLength, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength)) if err != nil { return off, err } return off, nil } func (rr *NULL) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Data, off, err = unpackStringAny(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *NXNAME) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart return off, nil } func (rr *NXT) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.NextDomain, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { return off, err } return off, nil } func (rr *OPENPGPKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *OPT) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Option, off, err = unpackDataOpt(msg, off) if err != nil { return off, err } return off, nil } func (rr *PTR) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Ptr, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *PX) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Map822, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Mapx400, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *RESINFO) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { return off, err } return off, nil } func (rr *RFC3597) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Rdata, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *RKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Flags, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *RP) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Mbox, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Txt, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *RRSIG) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.TypeCovered, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Labels, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.OrigTtl, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Expiration, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Inception, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.SignerName, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *RT) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Preference, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Host, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *SIG) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.TypeCovered, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Labels, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.OrigTtl, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Expiration, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Inception, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.SignerName, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *SMIMEA) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Usage, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Selector, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.MatchingType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *SOA) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Ns, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Mbox, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Serial, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Refresh, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Retry, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Expire, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Minttl, off, err = unpackUint32(msg, off) if err != nil { return off, err } return off, nil } func (rr *SPF) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { return off, err } return off, nil } func (rr *SRV) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Priority, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Weight, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Port, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *SSHFP) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Type, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.FingerPrint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *SVCB) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Priority, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Value, off, err = unpackDataSVCB(msg, off) if err != nil { return off, err } return off, nil } func (rr *TA) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *TALINK) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.PreviousName, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.NextName, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } return off, nil } func (rr *TKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Algorithm, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Inception, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Expiration, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Mode, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Error, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.KeySize, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Key, off, err = unpackStringHex(msg, off, off+int(rr.KeySize)) if err != nil { return off, err } rr.OtherLen, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen)) if err != nil { return off, err } return off, nil } func (rr *TLSA) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Usage, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Selector, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.MatchingType, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } func (rr *TSIG) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Algorithm, off, err = UnpackDomainName(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.TimeSigned, off, err = unpackUint48(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Fudge, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.MACSize, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.MAC, off, err = unpackStringHex(msg, off, off+int(rr.MACSize)) if err != nil { return off, err } rr.OrigId, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Error, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.OtherLen, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen)) if err != nil { return off, err } return off, nil } func (rr *TXT) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { return off, err } return off, nil } func (rr *UID) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Uid, off, err = unpackUint32(msg, off) if err != nil { return off, err } return off, nil } func (rr *UINFO) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Uinfo, off, err = unpackString(msg, off) if err != nil { return off, err } return off, nil } func (rr *URI) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Priority, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Weight, off, err = unpackUint16(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Target, off, err = unpackStringOctet(msg, off) if err != nil { return off, err } return off, nil } func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.PSDNAddress, off, err = unpackString(msg, off) if err != nil { return off, err } return off, nil } func (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart rr.Serial, off, err = unpackUint32(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Scheme, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Hash, off, err = unpackUint8(msg, off) if err != nil { return off, err } if off == len(msg) { return off, nil } rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { return off, err } return off, nil } golang-github-miekg-dns-1.1.64/ztypes.go000066400000000000000000001001551476742671700201220ustar00rootroot00000000000000// Code generated by "go run types_generate.go"; DO NOT EDIT. package dns import ( "encoding/base64" "net" ) // TypeToRR is a map of constructors for each RR type. var TypeToRR = map[uint16]func() RR{ TypeA: func() RR { return new(A) }, TypeAAAA: func() RR { return new(AAAA) }, TypeAFSDB: func() RR { return new(AFSDB) }, TypeAMTRELAY: func() RR { return new(AMTRELAY) }, TypeANY: func() RR { return new(ANY) }, TypeAPL: func() RR { return new(APL) }, TypeAVC: func() RR { return new(AVC) }, TypeCAA: func() RR { return new(CAA) }, TypeCDNSKEY: func() RR { return new(CDNSKEY) }, TypeCDS: func() RR { return new(CDS) }, TypeCERT: func() RR { return new(CERT) }, TypeCNAME: func() RR { return new(CNAME) }, TypeCSYNC: func() RR { return new(CSYNC) }, TypeDHCID: func() RR { return new(DHCID) }, TypeDLV: func() RR { return new(DLV) }, TypeDNAME: func() RR { return new(DNAME) }, TypeDNSKEY: func() RR { return new(DNSKEY) }, TypeDS: func() RR { return new(DS) }, TypeEID: func() RR { return new(EID) }, TypeEUI48: func() RR { return new(EUI48) }, TypeEUI64: func() RR { return new(EUI64) }, TypeGID: func() RR { return new(GID) }, TypeGPOS: func() RR { return new(GPOS) }, TypeHINFO: func() RR { return new(HINFO) }, TypeHIP: func() RR { return new(HIP) }, TypeHTTPS: func() RR { return new(HTTPS) }, TypeIPSECKEY: func() RR { return new(IPSECKEY) }, TypeISDN: func() RR { return new(ISDN) }, TypeKEY: func() RR { return new(KEY) }, TypeKX: func() RR { return new(KX) }, TypeL32: func() RR { return new(L32) }, TypeL64: func() RR { return new(L64) }, TypeLOC: func() RR { return new(LOC) }, TypeLP: func() RR { return new(LP) }, TypeMB: func() RR { return new(MB) }, TypeMD: func() RR { return new(MD) }, TypeMF: func() RR { return new(MF) }, TypeMG: func() RR { return new(MG) }, TypeMINFO: func() RR { return new(MINFO) }, TypeMR: func() RR { return new(MR) }, TypeMX: func() RR { return new(MX) }, TypeNAPTR: func() RR { return new(NAPTR) }, TypeNID: func() RR { return new(NID) }, TypeNIMLOC: func() RR { return new(NIMLOC) }, TypeNINFO: func() RR { return new(NINFO) }, TypeNS: func() RR { return new(NS) }, TypeNSAPPTR: func() RR { return new(NSAPPTR) }, TypeNSEC: func() RR { return new(NSEC) }, TypeNSEC3: func() RR { return new(NSEC3) }, TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, TypeNULL: func() RR { return new(NULL) }, TypeNXNAME: func() RR { return new(NXNAME) }, TypeNXT: func() RR { return new(NXT) }, TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) }, TypeOPT: func() RR { return new(OPT) }, TypePTR: func() RR { return new(PTR) }, TypePX: func() RR { return new(PX) }, TypeRESINFO: func() RR { return new(RESINFO) }, TypeRKEY: func() RR { return new(RKEY) }, TypeRP: func() RR { return new(RP) }, TypeRRSIG: func() RR { return new(RRSIG) }, TypeRT: func() RR { return new(RT) }, TypeSIG: func() RR { return new(SIG) }, TypeSMIMEA: func() RR { return new(SMIMEA) }, TypeSOA: func() RR { return new(SOA) }, TypeSPF: func() RR { return new(SPF) }, TypeSRV: func() RR { return new(SRV) }, TypeSSHFP: func() RR { return new(SSHFP) }, TypeSVCB: func() RR { return new(SVCB) }, TypeTA: func() RR { return new(TA) }, TypeTALINK: func() RR { return new(TALINK) }, TypeTKEY: func() RR { return new(TKEY) }, TypeTLSA: func() RR { return new(TLSA) }, TypeTSIG: func() RR { return new(TSIG) }, TypeTXT: func() RR { return new(TXT) }, TypeUID: func() RR { return new(UID) }, TypeUINFO: func() RR { return new(UINFO) }, TypeURI: func() RR { return new(URI) }, TypeX25: func() RR { return new(X25) }, TypeZONEMD: func() RR { return new(ZONEMD) }, } // TypeToString is a map of strings for each RR type. var TypeToString = map[uint16]string{ TypeA: "A", TypeAAAA: "AAAA", TypeAFSDB: "AFSDB", TypeAMTRELAY: "AMTRELAY", TypeANY: "ANY", TypeAPL: "APL", TypeATMA: "ATMA", TypeAVC: "AVC", TypeAXFR: "AXFR", TypeCAA: "CAA", TypeCDNSKEY: "CDNSKEY", TypeCDS: "CDS", TypeCERT: "CERT", TypeCNAME: "CNAME", TypeCSYNC: "CSYNC", TypeDHCID: "DHCID", TypeDLV: "DLV", TypeDNAME: "DNAME", TypeDNSKEY: "DNSKEY", TypeDS: "DS", TypeEID: "EID", TypeEUI48: "EUI48", TypeEUI64: "EUI64", TypeGID: "GID", TypeGPOS: "GPOS", TypeHINFO: "HINFO", TypeHIP: "HIP", TypeHTTPS: "HTTPS", TypeIPSECKEY: "IPSECKEY", TypeISDN: "ISDN", TypeIXFR: "IXFR", TypeKEY: "KEY", TypeKX: "KX", TypeL32: "L32", TypeL64: "L64", TypeLOC: "LOC", TypeLP: "LP", TypeMAILA: "MAILA", TypeMAILB: "MAILB", TypeMB: "MB", TypeMD: "MD", TypeMF: "MF", TypeMG: "MG", TypeMINFO: "MINFO", TypeMR: "MR", TypeMX: "MX", TypeNAPTR: "NAPTR", TypeNID: "NID", TypeNIMLOC: "NIMLOC", TypeNINFO: "NINFO", TypeNS: "NS", TypeNSEC: "NSEC", TypeNSEC3: "NSEC3", TypeNSEC3PARAM: "NSEC3PARAM", TypeNULL: "NULL", TypeNXNAME: "NXNAME", TypeNXT: "NXT", TypeNone: "None", TypeOPENPGPKEY: "OPENPGPKEY", TypeOPT: "OPT", TypePTR: "PTR", TypePX: "PX", TypeRESINFO: "RESINFO", TypeRKEY: "RKEY", TypeRP: "RP", TypeRRSIG: "RRSIG", TypeRT: "RT", TypeReserved: "Reserved", TypeSIG: "SIG", TypeSMIMEA: "SMIMEA", TypeSOA: "SOA", TypeSPF: "SPF", TypeSRV: "SRV", TypeSSHFP: "SSHFP", TypeSVCB: "SVCB", TypeTA: "TA", TypeTALINK: "TALINK", TypeTKEY: "TKEY", TypeTLSA: "TLSA", TypeTSIG: "TSIG", TypeTXT: "TXT", TypeUID: "UID", TypeUINFO: "UINFO", TypeUNSPEC: "UNSPEC", TypeURI: "URI", TypeX25: "X25", TypeZONEMD: "ZONEMD", TypeNSAPPTR: "NSAP-PTR", } func (rr *A) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } func (rr *AMTRELAY) Header() *RR_Header { return &rr.Hdr } func (rr *ANY) Header() *RR_Header { return &rr.Hdr } func (rr *APL) Header() *RR_Header { return &rr.Hdr } func (rr *AVC) Header() *RR_Header { return &rr.Hdr } func (rr *CAA) Header() *RR_Header { return &rr.Hdr } func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr } func (rr *CDS) Header() *RR_Header { return &rr.Hdr } func (rr *CERT) Header() *RR_Header { return &rr.Hdr } func (rr *CNAME) Header() *RR_Header { return &rr.Hdr } func (rr *CSYNC) Header() *RR_Header { return &rr.Hdr } func (rr *DHCID) Header() *RR_Header { return &rr.Hdr } func (rr *DLV) Header() *RR_Header { return &rr.Hdr } func (rr *DNAME) Header() *RR_Header { return &rr.Hdr } func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr } func (rr *DS) Header() *RR_Header { return &rr.Hdr } func (rr *EID) Header() *RR_Header { return &rr.Hdr } func (rr *EUI48) Header() *RR_Header { return &rr.Hdr } func (rr *EUI64) Header() *RR_Header { return &rr.Hdr } func (rr *GID) Header() *RR_Header { return &rr.Hdr } func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } func (rr *HIP) Header() *RR_Header { return &rr.Hdr } func (rr *HTTPS) Header() *RR_Header { return &rr.Hdr } func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr } func (rr *ISDN) Header() *RR_Header { return &rr.Hdr } func (rr *KEY) Header() *RR_Header { return &rr.Hdr } func (rr *KX) Header() *RR_Header { return &rr.Hdr } func (rr *L32) Header() *RR_Header { return &rr.Hdr } func (rr *L64) Header() *RR_Header { return &rr.Hdr } func (rr *LOC) Header() *RR_Header { return &rr.Hdr } func (rr *LP) Header() *RR_Header { return &rr.Hdr } func (rr *MB) Header() *RR_Header { return &rr.Hdr } func (rr *MD) Header() *RR_Header { return &rr.Hdr } func (rr *MF) Header() *RR_Header { return &rr.Hdr } func (rr *MG) Header() *RR_Header { return &rr.Hdr } func (rr *MINFO) Header() *RR_Header { return &rr.Hdr } func (rr *MR) Header() *RR_Header { return &rr.Hdr } func (rr *MX) Header() *RR_Header { return &rr.Hdr } func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr } func (rr *NID) Header() *RR_Header { return &rr.Hdr } func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr } func (rr *NINFO) Header() *RR_Header { return &rr.Hdr } func (rr *NS) Header() *RR_Header { return &rr.Hdr } func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr } func (rr *NULL) Header() *RR_Header { return &rr.Hdr } func (rr *NXNAME) Header() *RR_Header { return &rr.Hdr } func (rr *NXT) Header() *RR_Header { return &rr.Hdr } func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr } func (rr *OPT) Header() *RR_Header { return &rr.Hdr } func (rr *PTR) Header() *RR_Header { return &rr.Hdr } func (rr *PX) Header() *RR_Header { return &rr.Hdr } func (rr *RESINFO) Header() *RR_Header { return &rr.Hdr } func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr } func (rr *RKEY) Header() *RR_Header { return &rr.Hdr } func (rr *RP) Header() *RR_Header { return &rr.Hdr } func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr } func (rr *RT) Header() *RR_Header { return &rr.Hdr } func (rr *SIG) Header() *RR_Header { return &rr.Hdr } func (rr *SMIMEA) Header() *RR_Header { return &rr.Hdr } func (rr *SOA) Header() *RR_Header { return &rr.Hdr } func (rr *SPF) Header() *RR_Header { return &rr.Hdr } func (rr *SRV) Header() *RR_Header { return &rr.Hdr } func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr } func (rr *SVCB) Header() *RR_Header { return &rr.Hdr } func (rr *TA) Header() *RR_Header { return &rr.Hdr } func (rr *TALINK) Header() *RR_Header { return &rr.Hdr } func (rr *TKEY) Header() *RR_Header { return &rr.Hdr } func (rr *TLSA) Header() *RR_Header { return &rr.Hdr } func (rr *TSIG) Header() *RR_Header { return &rr.Hdr } func (rr *TXT) Header() *RR_Header { return &rr.Hdr } func (rr *UID) Header() *RR_Header { return &rr.Hdr } func (rr *UINFO) Header() *RR_Header { return &rr.Hdr } func (rr *URI) Header() *RR_Header { return &rr.Hdr } func (rr *X25) Header() *RR_Header { return &rr.Hdr } func (rr *ZONEMD) Header() *RR_Header { return &rr.Hdr } // len() functions func (rr *A) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) if len(rr.A) != 0 { l += net.IPv4len } return l } func (rr *AAAA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) if len(rr.AAAA) != 0 { l += net.IPv6len } return l } func (rr *AFSDB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Subtype l += domainNameLen(rr.Hostname, off+l, compression, false) return l } func (rr *AMTRELAY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Precedence l++ // GatewayType switch rr.GatewayType { case AMTRELAYIPv4: l += net.IPv4len case AMTRELAYIPv6: l += net.IPv6len case AMTRELAYHost: l += len(rr.GatewayHost) + 1 } return l } func (rr *ANY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) return l } func (rr *APL) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Prefixes { l += x.len() } return l } func (rr *AVC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } func (rr *CAA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Flag l += len(rr.Tag) + 1 l += len(rr.Value) return l } func (rr *CERT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Type l += 2 // KeyTag l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) return l } func (rr *CNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Target, off+l, compression, true) return l } func (rr *DHCID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.Digest)) return l } func (rr *DNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Target, off+l, compression, false) return l } func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Flags l++ // Protocol l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } func (rr *DS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // KeyTag l++ // Algorithm l++ // DigestType l += len(rr.Digest) / 2 return l } func (rr *EID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Endpoint) / 2 return l } func (rr *EUI48) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 6 // Address return l } func (rr *EUI64) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 8 // Address return l } func (rr *GID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Gid return l } func (rr *GPOS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Longitude) + 1 l += len(rr.Latitude) + 1 l += len(rr.Altitude) + 1 return l } func (rr *HINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Cpu) + 1 l += len(rr.Os) + 1 return l } func (rr *HIP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // HitLength l++ // PublicKeyAlgorithm l += 2 // PublicKeyLength l += len(rr.Hit) / 2 l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) for _, x := range rr.RendezvousServers { l += domainNameLen(x, off+l, compression, false) } return l } func (rr *IPSECKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Precedence l++ // GatewayType l++ // Algorithm switch rr.GatewayType { case IPSECGatewayIPv4: l += net.IPv4len case IPSECGatewayIPv6: l += net.IPv6len case IPSECGatewayHost: l += len(rr.GatewayHost) + 1 } l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } func (rr *ISDN) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Address) + 1 l += len(rr.SubAddress) + 1 return l } func (rr *KX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Exchanger, off+l, compression, false) return l } func (rr *L32) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference if len(rr.Locator32) != 0 { l += net.IPv4len } return l } func (rr *L64) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // Locator64 return l } func (rr *LOC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Version l++ // Size l++ // HorizPre l++ // VertPre l += 4 // Latitude l += 4 // Longitude l += 4 // Altitude return l } func (rr *LP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Fqdn, off+l, compression, false) return l } func (rr *MB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mb, off+l, compression, true) return l } func (rr *MD) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Md, off+l, compression, true) return l } func (rr *MF) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mf, off+l, compression, true) return l } func (rr *MG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mg, off+l, compression, true) return l } func (rr *MINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Rmail, off+l, compression, true) l += domainNameLen(rr.Email, off+l, compression, true) return l } func (rr *MR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mr, off+l, compression, true) return l } func (rr *MX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Mx, off+l, compression, true) return l } func (rr *NAPTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Order l += 2 // Preference l += len(rr.Flags) + 1 l += len(rr.Service) + 1 l += len(rr.Regexp) + 1 l += domainNameLen(rr.Replacement, off+l, compression, false) return l } func (rr *NID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // NodeID return l } func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Locator) / 2 return l } func (rr *NINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.ZSData { l += len(x) + 1 } return l } func (rr *NS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ns, off+l, compression, true) return l } func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ptr, off+l, compression, false) return l } func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Hash l++ // Flags l += 2 // Iterations l++ // SaltLength l += len(rr.Salt) / 2 return l } func (rr *NULL) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Data) return l } func (rr *NXNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) return l } func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } func (rr *PTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ptr, off+l, compression, true) return l } func (rr *PX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Map822, off+l, compression, false) l += domainNameLen(rr.Mapx400, off+l, compression, false) return l } func (rr *RESINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } func (rr *RFC3597) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Rdata) / 2 return l } func (rr *RKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Flags l++ // Protocol l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } func (rr *RP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mbox, off+l, compression, false) l += domainNameLen(rr.Txt, off+l, compression, false) return l } func (rr *RRSIG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // TypeCovered l++ // Algorithm l++ // Labels l += 4 // OrigTtl l += 4 // Expiration l += 4 // Inception l += 2 // KeyTag l += domainNameLen(rr.SignerName, off+l, compression, false) l += base64.StdEncoding.DecodedLen(len(rr.Signature)) return l } func (rr *RT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Host, off+l, compression, false) return l } func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Usage l++ // Selector l++ // MatchingType l += len(rr.Certificate) / 2 return l } func (rr *SOA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ns, off+l, compression, true) l += domainNameLen(rr.Mbox, off+l, compression, true) l += 4 // Serial l += 4 // Refresh l += 4 // Retry l += 4 // Expire l += 4 // Minttl return l } func (rr *SPF) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } func (rr *SRV) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority l += 2 // Weight l += 2 // Port l += domainNameLen(rr.Target, off+l, compression, false) return l } func (rr *SSHFP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Algorithm l++ // Type l += len(rr.FingerPrint) / 2 return l } func (rr *SVCB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority l += domainNameLen(rr.Target, off+l, compression, false) for _, x := range rr.Value { l += 4 + int(x.len()) } return l } func (rr *TA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // KeyTag l++ // Algorithm l++ // DigestType l += len(rr.Digest) / 2 return l } func (rr *TALINK) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.PreviousName, off+l, compression, false) l += domainNameLen(rr.NextName, off+l, compression, false) return l } func (rr *TKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Algorithm, off+l, compression, false) l += 4 // Inception l += 4 // Expiration l += 2 // Mode l += 2 // Error l += 2 // KeySize l += len(rr.Key) / 2 l += 2 // OtherLen l += len(rr.OtherData) / 2 return l } func (rr *TLSA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Usage l++ // Selector l++ // MatchingType l += len(rr.Certificate) / 2 return l } func (rr *TSIG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Algorithm, off+l, compression, false) l += 6 // TimeSigned l += 2 // Fudge l += 2 // MACSize l += len(rr.MAC) / 2 l += 2 // OrigId l += 2 // Error l += 2 // OtherLen l += len(rr.OtherData) / 2 return l } func (rr *TXT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } func (rr *UID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Uid return l } func (rr *UINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Uinfo) + 1 return l } func (rr *URI) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority l += 2 // Weight l += len(rr.Target) return l } func (rr *X25) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.PSDNAddress) + 1 return l } func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Serial l++ // Scheme l++ // Hash l += len(rr.Digest) / 2 return l } // copy() functions func (rr *A) copy() RR { return &A{rr.Hdr, cloneSlice(rr.A)} } func (rr *AAAA) copy() RR { return &AAAA{rr.Hdr, cloneSlice(rr.AAAA)} } func (rr *AFSDB) copy() RR { return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname} } func (rr *AMTRELAY) copy() RR { return &AMTRELAY{ rr.Hdr, rr.Precedence, rr.GatewayType, cloneSlice(rr.GatewayAddr), rr.GatewayHost, } } func (rr *ANY) copy() RR { return &ANY{rr.Hdr} } func (rr *APL) copy() RR { Prefixes := make([]APLPrefix, len(rr.Prefixes)) for i, e := range rr.Prefixes { Prefixes[i] = e.copy() } return &APL{rr.Hdr, Prefixes} } func (rr *AVC) copy() RR { return &AVC{rr.Hdr, cloneSlice(rr.Txt)} } func (rr *CAA) copy() RR { return &CAA{ rr.Hdr, rr.Flag, rr.Tag, rr.Value, } } func (rr *CDNSKEY) copy() RR { return &CDNSKEY{*rr.DNSKEY.copy().(*DNSKEY)} } func (rr *CDS) copy() RR { return &CDS{*rr.DS.copy().(*DS)} } func (rr *CERT) copy() RR { return &CERT{ rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate, } } func (rr *CNAME) copy() RR { return &CNAME{rr.Hdr, rr.Target} } func (rr *CSYNC) copy() RR { return &CSYNC{ rr.Hdr, rr.Serial, rr.Flags, cloneSlice(rr.TypeBitMap), } } func (rr *DHCID) copy() RR { return &DHCID{rr.Hdr, rr.Digest} } func (rr *DLV) copy() RR { return &DLV{*rr.DS.copy().(*DS)} } func (rr *DNAME) copy() RR { return &DNAME{rr.Hdr, rr.Target} } func (rr *DNSKEY) copy() RR { return &DNSKEY{ rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey, } } func (rr *DS) copy() RR { return &DS{ rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest, } } func (rr *EID) copy() RR { return &EID{rr.Hdr, rr.Endpoint} } func (rr *EUI48) copy() RR { return &EUI48{rr.Hdr, rr.Address} } func (rr *EUI64) copy() RR { return &EUI64{rr.Hdr, rr.Address} } func (rr *GID) copy() RR { return &GID{rr.Hdr, rr.Gid} } func (rr *GPOS) copy() RR { return &GPOS{ rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude, } } func (rr *HINFO) copy() RR { return &HINFO{rr.Hdr, rr.Cpu, rr.Os} } func (rr *HIP) copy() RR { return &HIP{ rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, cloneSlice(rr.RendezvousServers), } } func (rr *HTTPS) copy() RR { return &HTTPS{*rr.SVCB.copy().(*SVCB)} } func (rr *IPSECKEY) copy() RR { return &IPSECKEY{ rr.Hdr, rr.Precedence, rr.GatewayType, rr.Algorithm, cloneSlice(rr.GatewayAddr), rr.GatewayHost, rr.PublicKey, } } func (rr *ISDN) copy() RR { return &ISDN{rr.Hdr, rr.Address, rr.SubAddress} } func (rr *KEY) copy() RR { return &KEY{*rr.DNSKEY.copy().(*DNSKEY)} } func (rr *KX) copy() RR { return &KX{rr.Hdr, rr.Preference, rr.Exchanger} } func (rr *L32) copy() RR { return &L32{rr.Hdr, rr.Preference, cloneSlice(rr.Locator32)} } func (rr *L64) copy() RR { return &L64{rr.Hdr, rr.Preference, rr.Locator64} } func (rr *LOC) copy() RR { return &LOC{ rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude, } } func (rr *LP) copy() RR { return &LP{rr.Hdr, rr.Preference, rr.Fqdn} } func (rr *MB) copy() RR { return &MB{rr.Hdr, rr.Mb} } func (rr *MD) copy() RR { return &MD{rr.Hdr, rr.Md} } func (rr *MF) copy() RR { return &MF{rr.Hdr, rr.Mf} } func (rr *MG) copy() RR { return &MG{rr.Hdr, rr.Mg} } func (rr *MINFO) copy() RR { return &MINFO{rr.Hdr, rr.Rmail, rr.Email} } func (rr *MR) copy() RR { return &MR{rr.Hdr, rr.Mr} } func (rr *MX) copy() RR { return &MX{rr.Hdr, rr.Preference, rr.Mx} } func (rr *NAPTR) copy() RR { return &NAPTR{ rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement, } } func (rr *NID) copy() RR { return &NID{rr.Hdr, rr.Preference, rr.NodeID} } func (rr *NIMLOC) copy() RR { return &NIMLOC{rr.Hdr, rr.Locator} } func (rr *NINFO) copy() RR { return &NINFO{rr.Hdr, cloneSlice(rr.ZSData)} } func (rr *NS) copy() RR { return &NS{rr.Hdr, rr.Ns} } func (rr *NSAPPTR) copy() RR { return &NSAPPTR{rr.Hdr, rr.Ptr} } func (rr *NSEC) copy() RR { return &NSEC{rr.Hdr, rr.NextDomain, cloneSlice(rr.TypeBitMap)} } func (rr *NSEC3) copy() RR { return &NSEC3{ rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, cloneSlice(rr.TypeBitMap), } } func (rr *NSEC3PARAM) copy() RR { return &NSEC3PARAM{ rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, } } func (rr *NULL) copy() RR { return &NULL{rr.Hdr, rr.Data} } func (rr *NXNAME) copy() RR { return &NXNAME{rr.Hdr} } func (rr *NXT) copy() RR { return &NXT{*rr.NSEC.copy().(*NSEC)} } func (rr *OPENPGPKEY) copy() RR { return &OPENPGPKEY{rr.Hdr, rr.PublicKey} } func (rr *OPT) copy() RR { Option := make([]EDNS0, len(rr.Option)) for i, e := range rr.Option { Option[i] = e.copy() } return &OPT{rr.Hdr, Option} } func (rr *PTR) copy() RR { return &PTR{rr.Hdr, rr.Ptr} } func (rr *PX) copy() RR { return &PX{ rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400, } } func (rr *RESINFO) copy() RR { return &RESINFO{rr.Hdr, cloneSlice(rr.Txt)} } func (rr *RFC3597) copy() RR { return &RFC3597{rr.Hdr, rr.Rdata} } func (rr *RKEY) copy() RR { return &RKEY{ rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey, } } func (rr *RP) copy() RR { return &RP{rr.Hdr, rr.Mbox, rr.Txt} } func (rr *RRSIG) copy() RR { return &RRSIG{ rr.Hdr, rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature, } } func (rr *RT) copy() RR { return &RT{rr.Hdr, rr.Preference, rr.Host} } func (rr *SIG) copy() RR { return &SIG{*rr.RRSIG.copy().(*RRSIG)} } func (rr *SMIMEA) copy() RR { return &SMIMEA{ rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate, } } func (rr *SOA) copy() RR { return &SOA{ rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl, } } func (rr *SPF) copy() RR { return &SPF{rr.Hdr, cloneSlice(rr.Txt)} } func (rr *SRV) copy() RR { return &SRV{ rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target, } } func (rr *SSHFP) copy() RR { return &SSHFP{ rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint, } } func (rr *SVCB) copy() RR { Value := make([]SVCBKeyValue, len(rr.Value)) for i, e := range rr.Value { Value[i] = e.copy() } return &SVCB{ rr.Hdr, rr.Priority, rr.Target, Value, } } func (rr *TA) copy() RR { return &TA{ rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest, } } func (rr *TALINK) copy() RR { return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName} } func (rr *TKEY) copy() RR { return &TKEY{ rr.Hdr, rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData, } } func (rr *TLSA) copy() RR { return &TLSA{ rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate, } } func (rr *TSIG) copy() RR { return &TSIG{ rr.Hdr, rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData, } } func (rr *TXT) copy() RR { return &TXT{rr.Hdr, cloneSlice(rr.Txt)} } func (rr *UID) copy() RR { return &UID{rr.Hdr, rr.Uid} } func (rr *UINFO) copy() RR { return &UINFO{rr.Hdr, rr.Uinfo} } func (rr *URI) copy() RR { return &URI{ rr.Hdr, rr.Priority, rr.Weight, rr.Target, } } func (rr *X25) copy() RR { return &X25{rr.Hdr, rr.PSDNAddress} } func (rr *ZONEMD) copy() RR { return &ZONEMD{ rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest, } }