diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 202ae236..4be16cd4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" schedule: interval: "monthly" + groups: + all: + patterns: + - "*" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2e30ddf0..73211a7f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,8 @@ Thanks for you pull request, do note the following: -* If your PR introduces backward incompatible changes it will very likely not be merged. +- 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. +- We support the last two major Go versions, if your PR uses features from a too new Go version, it + will not be merged. + +- If this text is not replaced (deleted, or preferably replaced with something sensible), your PR will be closed. diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c0674c78..56a62f2b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ 1.22.x, 1.23.x ] + go: [ 1.22.x, 1.23.x, 1.24.x ] steps: - name: Set up Go diff --git a/README.md b/README.md index 8d5a2a47..7e09e1d1 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ [![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) +DNS version 2 is now available at . This version should be 2x faster across +the board. Further optimizations welcome. + +The version here will only see specific fixes and nothing else. At some point this repo will be archived. + # Alternative (more granular) approach to a DNS library > Less is more. @@ -17,95 +22,97 @@ avoiding breaking changes wherever reasonable. We support the last two versions # Goals -* KISS; -* Fast; -* Small API. If it's easy to code in Go, don't make a function for it. +- 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/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 (migrated to v2) +- 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 +- https://github.com/jkerdreux-imt/owns 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 +- 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 - +Miek Gieben - 2010-2012 - DNS Authors 2012- # Building @@ -125,77 +132,83 @@ Example programs can be found in the `github.com/miekg/exdns` repository. ## Supported RFCs -*all of them* +_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 +- 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 +- 8490 - DNS Stateful Operations +- 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 +- 9567 - DNS Error Reporting +- 9606 - DNS Resolver Information +- 9660 - DNS Zone Version (ZONEVERSION) Option +- Draft - Compact Denial of Existence in DNSSEC ## Loosely Based Upon -* ldns - -* NSD - -* Net::DNS - -* GRONG - +- ldns - +- NSD - +- Net::DNS - +- GRONG - diff --git a/client.go b/client.go index 9549fa92..a0f96187 100644 --- a/client.go +++ b/client.go @@ -57,8 +57,8 @@ type Client struct { // 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 + ReadTimeout time.Duration // net.Conn.SetReadDeadline value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + WriteTimeout time.Duration // net.Conn.SetWriteDeadline 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. @@ -92,6 +92,9 @@ func (c *Client) dialTimeout() time.Duration { } func (c *Client) readTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } if c.ReadTimeout != 0 { return c.ReadTimeout } @@ -99,6 +102,9 @@ func (c *Client) readTimeout() time.Duration { } func (c *Client) writeTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } if c.WriteTimeout != 0 { return c.WriteTimeout } diff --git a/dnssec.go b/dnssec.go index 1be87eae..ef04f490 100644 --- a/dnssec.go +++ b/dnssec.go @@ -12,7 +12,6 @@ import ( _ "crypto/sha256" // need its init function _ "crypto/sha512" // need its init function "encoding/asn1" - "encoding/binary" "encoding/hex" "math/big" "sort" @@ -134,38 +133,27 @@ func (k *DNSKEY) KeyTag() uint16 { 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 + 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) } @@ -250,14 +238,6 @@ func (d *DS) ToCDS() *CDS { // 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 { - 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 - } - h0 := rrset[0].Header() rr.Hdr.Rrtype = TypeRRSIG rr.Hdr.Name = h0.Name @@ -272,6 +252,18 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { 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 @@ -370,9 +362,12 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { if rr.Algorithm != k.Algorithm { return ErrKey } - if !strings.EqualFold(rr.SignerName, k.Hdr.Name) { + + signerName := CanonicalName(rr.SignerName) + if !equal(signerName, k.Hdr.Name) { return ErrKey } + if k.Protocol != 3 { return ErrKey } @@ -384,9 +379,18 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { } // 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 and - // class matches the RRSIG record. - if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered { + // 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 } @@ -400,7 +404,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag - sigwire.SignerName = CanonicalName(rr.SignerName) + sigwire.SignerName = signerName // Create the desired binary blob signeddata := make([]byte, DefaultMsgSize) n, err := packSigWire(sigwire, signeddata) diff --git a/dnssec_test.go b/dnssec_test.go index 1511278c..572c4d70 100644 --- a/dnssec_test.go +++ b/dnssec_test.go @@ -129,7 +129,10 @@ func TestSignVerify(t *testing.T) { key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 - privkey, _ := key.Generate(512) + privkey, err := key.Generate(1024) + if err != nil { + t.Fatal("failure to generate private key:", err) + } // Fill in the values of the Sig, before signing sig := new(RRSIG) @@ -155,6 +158,120 @@ func TestSignVerify(t *testing.T) { } } +// 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, err := key.Generate(1024) + if err != nil { + t.Fatal("failure to generate private key:", err) + } + + 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} @@ -167,7 +284,10 @@ func Test65534(t *testing.T) { key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 - privkey, _ := key.Generate(512) + privkey, err := key.Generate(1024) + if err != nil { + t.Fatal("failure to generate private key:", err) + } sig := new(RRSIG) sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} @@ -250,7 +370,10 @@ func TestKeyRSA(t *testing.T) { key.Flags = 256 key.Protocol = 3 key.Algorithm = RSASHA256 - priv, _ := key.Generate(512) + priv, err := key.Generate(1024) + if err != nil { + t.Fatal("failure to generate private key:", err) + } soa := new(SOA) soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} @@ -855,16 +978,3 @@ func TestParseKeyReadError(t *testing.T) { 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) - } -} diff --git a/edns.go b/edns.go index 783da914..89318b75 100644 --- a/edns.go +++ b/edns.go @@ -19,15 +19,17 @@ const ( EDNS0DHU = 0x6 // DS Hash Understood EDNS0N3U = 0x7 // NSEC3 Hash Understood EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871) - EDNS0EXPIRE = 0x9 // EDNS0 Expire + 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) - EDNS0ZONEVERSION = 0x000 // EDNS0 Zone Version (See RFC ...) https://www.ietf.org/archive/id/draft-ietf-dnsop-zoneversion-11.html + 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) + EDNS0REPORTING = 0x12 // EDNS0 reporting (See RFC 9567) + EDNS0ZONEVERSION = 0x13 // EDNS0 Zone Version (See RFC 9660) 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. @@ -59,9 +61,11 @@ func makeDataOpt(code uint16) EDNS0 { case EDNS0EDE: return new(EDNS0_EDE) case EDNS0ESU: - return &EDNS0_ESU{Code: EDNS0ESU} + return new(EDNS0_ESU) + case EDNS0REPORTING: + return new(EDNS0_REPORTING) case EDNS0ZONEVERSION: - return &EDNS0_ZONEVERSION{} + return new(EDNS0_ZONEVERSION) default: e := new(EDNS0_LOCAL) e.Code = code @@ -69,8 +73,7 @@ func makeDataOpt(code uint16) EDNS0 { } } -// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. -// See RFC 6891. +// 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"` @@ -78,13 +81,16 @@ type OPT struct { func (rr *OPT) String() string { s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; " + s += "flags:" if rr.Do() { - s += "flags: do; " - } else { - s += "flags:; " + s += " do" } - if rr.Hdr.Ttl&0x7FFF != 0 { - s += fmt.Sprintf("MBZ: 0x%04x, ", rr.Hdr.Ttl&0x7FFF) + if rr.Co() { + s += " co" + } + s += "; " + if z := rr.Z(); z != 0 { + s += fmt.Sprintf("MBZ: 0x%04x, ", z) } s += "udp: " + strconv.Itoa(int(rr.UDPSize())) @@ -126,6 +132,10 @@ func (rr *OPT) String() string { s += "\n; EDE: " + o.String() case *EDNS0_ESU: s += "\n; ESU: " + o.String() + case *EDNS0_REPORTING: + s += "\n; REPORT-CHANNEL: " + o.String() + case *EDNS0_ZONEVERSION: + s += "\n; ZONEVERSION: " + o.String() } } return s @@ -147,8 +157,6 @@ func (*OPT) parse(c *zlexer, origin string) *ParseError { func (rr *OPT) isDuplicate(r2 RR) bool { return false } -// return the old value -> delete SetVersion? - // Version returns the EDNS version used. Only zero is defined. func (rr *OPT) Version() uint8 { return uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16) @@ -201,14 +209,34 @@ func (rr *OPT) SetDo(do ...bool) { } } -// Z returns the Z part of the OPT RR as a uint16 with only the 15 least significant bits used. -func (rr *OPT) Z() uint16 { - return uint16(rr.Hdr.Ttl & 0x7FFF) +// Co returns the value of the CO (Compact Answers OK) bit. +func (rr *OPT) Co() bool { + return rr.Hdr.Ttl&_CO == _CO } -// SetZ sets the Z part of the OPT RR, note only the 15 least significant bits of z are used. +// 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&^0x7FFF | uint32(z&0x7FFF) + 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. @@ -239,8 +267,8 @@ type EDNS0 interface { // e.Nsid = "AA" // o.Option = append(o.Option, e) type EDNS0_NSID struct { - Code uint16 // Always EDNS0NSID - Nsid string // This string needs to be hex encoded + Code uint16 // always EDNS0NSID + Nsid string // string needs to be hex encoded } func (e *EDNS0_NSID) pack() ([]byte, error) { @@ -278,7 +306,7 @@ func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} // 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 + Code uint16 // always EDNS0SUBNET Family uint16 // 1 for IP, 2 for IP6 SourceNetmask uint8 SourceScope uint8 @@ -289,41 +317,54 @@ type EDNS0_SUBNET struct { 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") + return nil, errors.New("bad address family") } + b := make([]byte, 4) + b[3] = e.SourceScope + return b, nil case 1: if e.SourceNetmask > net.IPv4len*8 { - return nil, errors.New("dns: bad netmask") + return nil, errors.New("bad netmask") } - if len(e.Address.To4()) != net.IPv4len { - return nil, errors.New("dns: bad address") + ip4 := e.Address.To4() + if len(ip4) != net.IPv4len { + return nil, errors.New("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]...) + b := make([]byte, 4+needLength) + binary.BigEndian.PutUint16(b[0:], e.Family) + b[2] = e.SourceNetmask + b[3] = e.SourceScope + if needLength > 0 { + ip := ip4.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) + copy(b[4:], ip[:needLength]) + } + return b, nil case 2: if e.SourceNetmask > net.IPv6len*8 { - return nil, errors.New("dns: bad netmask") + return nil, errors.New("bad netmask") } if len(e.Address) != net.IPv6len { - return nil, errors.New("dns: bad address") + return nil, errors.New("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]...) + b := make([]byte, 4+needLength) + binary.BigEndian.PutUint16(b[0:], e.Family) + b[2] = e.SourceNetmask + b[3] = e.SourceScope + if needLength > 0 { + ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) + copy(b[4:], ip[:needLength]) + } + return b, nil default: - return nil, errors.New("dns: bad address family") + return nil, errors.New("bad address family") } - return b, nil } func (e *EDNS0_SUBNET) unpack(b []byte) error { @@ -338,25 +379,25 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error { // "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") + return errors.New("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") + return errors.New("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") + return errors.New("bad netmask") } addr := make(net.IP, net.IPv6len) copy(addr, b[4:]) e.Address = addr default: - return errors.New("dns: bad address family") + return errors.New("bad address family") } return nil } @@ -402,8 +443,8 @@ func (e *EDNS0_SUBNET) copy() EDNS0 { // // 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 + Code uint16 // always EDNS0COOKIE + Cookie string // hex encoded cookie data } func (e *EDNS0_COOKIE) pack() ([]byte, error) { @@ -433,7 +474,7 @@ func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.C // e.Lease = 120 // in seconds // o.Option = append(o.Option, e) type EDNS0_UL struct { - Code uint16 // Always EDNS0UL + Code uint16 // always EDNS0UL Lease uint32 KeyLease uint32 } @@ -472,7 +513,7 @@ func (e *EDNS0_UL) unpack(b []byte) error { // 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 + Code uint16 // always EDNS0LLQ Version uint16 Opcode uint16 Error uint16 @@ -518,7 +559,7 @@ func (e *EDNS0_LLQ) copy() EDNS0 { // EDNS0_DAU implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975. type EDNS0_DAU struct { - Code uint16 // Always EDNS0DAU + Code uint16 // always EDNS0DAU AlgCode []uint8 } @@ -542,7 +583,7 @@ 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 + Code uint16 // always EDNS0DHU AlgCode []uint8 } @@ -566,7 +607,7 @@ 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 + Code uint16 // always EDNS0N3U AlgCode []uint8 } @@ -591,7 +632,7 @@ 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 + 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. } @@ -671,7 +712,7 @@ func (e *EDNS0_LOCAL) unpack(b []byte) error { // 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 + 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, @@ -701,7 +742,7 @@ func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error { case 2: e.Timeout = binary.BigEndian.Uint16(b) default: - return fmt.Errorf("dns: length mismatch, want 0/2 but got %d", len(b)) + return fmt.Errorf("length mismatch, want 0/2 but got %d", len(b)) } return nil } @@ -844,11 +885,10 @@ func (e *EDNS0_EDE) unpack(b []byte) error { // The EDNS0_ESU option for ENUM Source-URI Extension. type EDNS0_ESU struct { - Code uint16 + Code uint16 // always EDNS0ESU Uri string } -// Option implements the EDNS0 interface. 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} } @@ -858,9 +898,73 @@ func (e *EDNS0_ESU) unpack(b []byte) error { return nil } -// The EDNS0_ZONEVERSION option is used to returns a unique version for this zone. See RFC xxxx. -type EDNS0_ZONEVERSION struct { - LabelCount uint8 - Type uint8 - Version string +// EDNS0_REPORTING implements the EDNS0 Reporting Channel option (RFC 9567). +type EDNS0_REPORTING struct { + Code uint16 // always EDNS0REPORTING + AgentDomain string +} + +func (e *EDNS0_REPORTING) Option() uint16 { return EDNS0REPORTING } +func (e *EDNS0_REPORTING) String() string { return e.AgentDomain } +func (e *EDNS0_REPORTING) copy() EDNS0 { return &EDNS0_REPORTING{e.Code, e.AgentDomain} } +func (e *EDNS0_REPORTING) pack() ([]byte, error) { + b := make([]byte, 255) + off1, err := PackDomainName(Fqdn(e.AgentDomain), b, 0, nil, false) + if err != nil { + return nil, fmt.Errorf("bad agent domain: %w", err) + } + return b[:off1], nil +} +func (e *EDNS0_REPORTING) unpack(b []byte) error { + domain, _, err := UnpackDomainName(b, 0) + if err != nil { + return fmt.Errorf("bad agent domain: %w", err) + } + e.AgentDomain = domain + return nil +} + +// EDNS0_ZONEVERSION implements the EDNS0 Zone Version option (RFC 9660). +type EDNS0_ZONEVERSION struct { + // always EDNS0ZONEVERSION (19) + Code uint16 + // An unsigned 1-octet Label Count indicating + // the number of labels for the name of the zone that VERSION value refers to. + LabelCount uint8 + // An unsigned 1-octet type number distinguishing the format and meaning of version. + // 0 SOA-SERIAL, 1-245 Unassigned, 246-255 Reserved for private use, see RFC 9660. + Type uint8 + // An opaque octet string conveying the zone version data (VERSION). + Version string +} + +func (e *EDNS0_ZONEVERSION) Option() uint16 { return EDNS0ZONEVERSION } +func (e *EDNS0_ZONEVERSION) String() string { return e.Version } +func (e *EDNS0_ZONEVERSION) copy() EDNS0 { + return &EDNS0_ZONEVERSION{e.Code, e.LabelCount, e.Type, e.Version} +} +func (e *EDNS0_ZONEVERSION) pack() ([]byte, error) { + b := []byte{ + // first octet label count + e.LabelCount, + // second octet is type + e.Type, + } + if len(e.Version) > 0 { + b = append(b, []byte(e.Version)...) + } + return b, nil +} +func (e *EDNS0_ZONEVERSION) unpack(b []byte) error { + if len(b) < 2 { + return ErrBuf + } + e.LabelCount = b[0] + e.Type = b[1] + if len(b) > 2 { + e.Version = string(b[2:]) + } else { + e.Version = "" + } + return nil } diff --git a/edns_test.go b/edns_test.go index b7c15f7e..b9853dd2 100644 --- a/edns_test.go +++ b/edns_test.go @@ -56,6 +56,39 @@ func TestOPTTtl(t *testing.T) { 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") } @@ -141,6 +174,7 @@ func TestZ(t *testing.T) { 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()) } @@ -149,8 +183,8 @@ func TestZ(t *testing.T) { t.Errorf("expected Z of 5, got %d", e.Z()) } e.SetZ(0xFFFF) - if e.Z() != 0x7FFF { - t.Errorf("expected Z of 0x7FFFF, got %d", e.Z()) + 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()) diff --git a/go.mod b/go.mod index cb9c336d..0c347ef0 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,17 @@ module github.com/miekg/dns -go 1.19 +go 1.25.0 require ( - golang.org/x/net v0.27.0 - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.22.0 - golang.org/x/tools v0.22.0 + golang.org/x/net v0.58.0 + golang.org/x/sync v0.22.0 + golang.org/x/sys v0.47.0 ) -require golang.org/x/mod v0.18.0 // indirect +// used for go:generate +tool golang.org/x/tools/go/packages + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/tools v0.48.0 // indirect +) diff --git a/go.sum b/go.sum index c835f72e..5c87ce2a 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,12 @@ -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +github.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.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/listen_no_reuseport.go b/listen_no_socket_options.go similarity index 61% rename from listen_no_reuseport.go rename to listen_no_socket_options.go index 8cebb2f1..9e4010bd 100644 --- a/listen_no_reuseport.go +++ b/listen_no_socket_options.go @@ -3,9 +3,15 @@ package dns -import "net" +import ( + "fmt" + "net" +) -const supportsReusePort = false +const ( + supportsReusePort = false + supportsReuseAddr = false +) func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) { if reuseport || reuseaddr { @@ -15,8 +21,6 @@ func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, e return net.Listen(network, addr) } -const supportsReuseAddr = false - func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) { if reuseport || reuseaddr { // TODO(tmthrgd): return an error? @@ -24,3 +28,13 @@ func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, 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") +} diff --git a/listen_reuseport.go b/listen_socket_options.go similarity index 66% rename from listen_reuseport.go rename to listen_socket_options.go index 41326f20..35dfc949 100644 --- a/listen_reuseport.go +++ b/listen_socket_options.go @@ -39,10 +39,40 @@ func reuseaddrControl(network, address string, c syscall.RawConn) error { 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: @@ -56,6 +86,7 @@ func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, var lc net.ListenConfig switch { case reuseaddr && reuseport: + lc.Control = reuseaddrandportControl case reuseport: lc.Control = reuseportControl case reuseaddr: diff --git a/msg.go b/msg.go index 5fa7f9e8..382a8083 100644 --- a/msg.go +++ b/msg.go @@ -136,18 +136,19 @@ var OpcodeToString = map[int]string{ // 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 + 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", + RcodeStatefulTypeNotImplemented: "DSOTYPENI", + RcodeBadSig: "BADSIG", // Also known as RcodeBadVers, see RFC 6891 // RcodeBadVers: "BADVERS", RcodeBadKey: "BADKEY", RcodeBadTime: "BADTIME", @@ -337,11 +338,13 @@ loop: return off + 2, nil } + // Trailing root label if off < len(msg) { msg[off] = 0 + return off + 1, nil } - return off + 1, nil + return off, ErrBuf } // isRootLabel returns whether s or bs, from off to end, is the root @@ -871,10 +874,9 @@ func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { // 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)) + // // println("dns: extra bytes in dns packet", off, "<", len(msg)) // } return err - } // Unpack unpacks a binary message to a Msg structure. @@ -1123,23 +1125,28 @@ func unpackQuestion(msg []byte, off int) (Question, int, error) { ) q.Name, off, err = UnpackDomainName(msg, off) if err != nil { - return q, off, err + return q, off, fmt.Errorf("bad question name: %w", err) } if off == len(msg) { return q, off, nil } q.Qtype, off, err = unpackUint16(msg, off) if err != nil { - return q, off, err + return q, off, fmt.Errorf("bad question qtype: %w", err) } if off == len(msg) { return q, off, nil } q.Qclass, off, err = unpackUint16(msg, off) + if err != nil { + return q, off, fmt.Errorf("bad question qclass: %w", err) + } + if off == len(msg) { return q, off, nil } - return q, off, err + + return q, off, nil } func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { @@ -1177,27 +1184,27 @@ func unpackMsgHdr(msg []byte, off int) (Header, int, error) { ) dh.Id, off, err = unpackUint16(msg, off) if err != nil { - return dh, off, err + return dh, off, fmt.Errorf("bad header id: %w", err) } dh.Bits, off, err = unpackUint16(msg, off) if err != nil { - return dh, off, err + return dh, off, fmt.Errorf("bad header bits: %w", err) } dh.Qdcount, off, err = unpackUint16(msg, off) if err != nil { - return dh, off, err + return dh, off, fmt.Errorf("bad header question count: %w", err) } dh.Ancount, off, err = unpackUint16(msg, off) if err != nil { - return dh, off, err + return dh, off, fmt.Errorf("bad header answer count: %w", err) } dh.Nscount, off, err = unpackUint16(msg, off) if err != nil { - return dh, off, err + return dh, off, fmt.Errorf("bad header ns count: %w", err) } dh.Arcount, off, err = unpackUint16(msg, off) if err != nil { - return dh, off, err + return dh, off, fmt.Errorf("bad header extra count: %w", err) } return dh, off, nil } diff --git a/msg_generate.go b/msg_generate.go index e3614193..b98fe9e7 100644 --- a/msg_generate.go +++ b/msg_generate.go @@ -24,6 +24,7 @@ var packageHdr = ` package dns +import "fmt" ` // getTypeStruct will take a type and the package scope, and return the @@ -208,10 +209,15 @@ _ = 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 + + err_name := name + if name != st.Field(i).Name() { + err_name += "." + st.Field(i).Name() + } + fmt.Fprintf(b, `if err != nil { +return off, fmt.Errorf("%s: %%w", err) } -`) +`, err_name) } // size-* are special, because they reference a struct member we should use for the length. diff --git a/parse_test.go b/parse_test.go index f0d68a44..e4ed216e 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1250,8 +1250,8 @@ func TestNewPrivateKey(t *testing.T) { algorithms := []algorithm{ {ECDSAP256SHA256, 256}, {ECDSAP384SHA384, 384}, - {RSASHA1, 512}, - {RSASHA256, 512}, + {RSASHA1, 1024}, + {RSASHA256, 1024}, {ED25519, 256}, } diff --git a/reverse.go b/reverse.go index 28151af8..6f5b3ea7 100644 --- a/reverse.go +++ b/reverse.go @@ -23,9 +23,12 @@ var StringToAlgorithm = reverseInt8(AlgorithmToString) // StringToHash is a map of names to hash IDs. var StringToHash = reverseInt8(HashToString) -// StringToCertType is the reverseof CertTypeToString. +// StringToCertType is the reverse of CertTypeToString. var StringToCertType = reverseInt16(CertTypeToString) +// StringToStatefulType is the reverse of StatefulTypeToString. +var StringToStatefulType = reverseInt16(StatefulTypeToString) + // Reverse a map func reverseInt8(m map[uint8]string) map[string]uint8 { n := make(map[string]uint8, len(m)) diff --git a/scan.go b/scan.go index e26e8027..f7c6525d 100644 --- a/scan.go +++ b/scan.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/fs" + "math" "os" "path" "path/filepath" @@ -108,6 +109,8 @@ type ttlState struct { // 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. +// Is you need a partial resource record with no rdata - for instance - for dynamic updates, see the [ANY] +// documentation. 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"), "") @@ -1229,7 +1232,7 @@ func typeToInt(token string) (uint16, bool) { // stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds. func stringToTTL(token string) (uint32, bool) { - var s, i uint32 + var s, i uint for _, c := range token { switch c { case 's', 'S': @@ -1249,12 +1252,15 @@ func stringToTTL(token string) (uint32, bool) { i = 0 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': i *= 10 - i += uint32(c) - '0' + i += uint(c) - '0' default: return 0, false } } - return s + i, true + if s+i > math.MaxUint32 { + return 0, false + } + return uint32(s + i), true } // Parse LOC records' [.][mM] into a @@ -1316,6 +1322,13 @@ func toAbsoluteName(name, origin string) (absolute string, ok bool) { return origin, true } + // this can happen when we have a comment after a RR that has a domain, '... MX 20 ; this is wrong'. + // technically a newline can be in a domain name, but this is clearly an error and the newline only shows + // because of the scanning and the comment. + if name == "\n" { + return "", false + } + // require a valid domain name _, ok = IsDomainName(name) if !ok || name == "" { diff --git a/scan_rr.go b/scan_rr.go index c1a76995..ac885f66 100644 --- a/scan_rr.go +++ b/scan_rr.go @@ -1620,6 +1620,16 @@ func (rr *NINFO) parse(c *zlexer, o string) *ParseError { 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) diff --git a/scan_test.go b/scan_test.go index c4f7e7f4..e46b3684 100644 --- a/scan_test.go +++ b/scan_test.go @@ -226,6 +226,22 @@ func TestZoneParserAddressAAAA(t *testing.T) { } } +func TestZoneParserTargetBad(t *testing.T) { + records := []string{ + "bad.example.org. CNAME ; bad cname", + "bad.example.org. HTTPS 10 ; bad https", + "bad.example.org. MX 10 ; bad mx", + "bad.example.org. SRV 1 0 80 ; bad srv", + } + + for _, record := range records { + const expect = "bad " + 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 TestZoneParserAddressBad(t *testing.T) { records := []string{ "1.bad.example.org. 600 IN A ::1", diff --git a/server.go b/server.go index 81580d1e..50478b32 100644 --- a/server.go +++ b/server.go @@ -194,7 +194,9 @@ type DecorateWriter func(Writer) Writer // rejected (or ignored) by the MsgAcceptFunc, or passed to this function. type MsgInvalidFunc func(m []byte, err error) -func DefaultMsgInvalidFunc(m []byte, err error) {} +var DefaultMsgInvalidFunc MsgInvalidFunc = defaultMsgInvalidFunc + +func defaultMsgInvalidFunc(m []byte, err error) {} // A Server defines parameters for running an DNS server. type Server struct { @@ -226,6 +228,7 @@ type Server struct { // 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 @@ -331,7 +334,7 @@ func (srv *Server) ListenAndServe() error { 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") + return errors.New("neither Certificates nor GetCertificate set in config") } network := strings.TrimSuffix(srv.Net, "-tls") l, err := listenTCP(network, addr, srv.ReusePort, srv.ReuseAddr) diff --git a/server_test.go b/server_test.go index 4fc2af32..a9e2339b 100644 --- a/server_test.go +++ b/server_test.go @@ -996,6 +996,101 @@ func TestServerStartStopRace(t *testing.T) { 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") @@ -1329,7 +1424,6 @@ func TestResponseWriteSinglePacket(t *testing.T) { m.SetQuestion("miek.nl.", TypeTXT) m.Response = true err := rw.WriteMsg(m) - if err != nil { t.Fatalf("failed to write: %v", err) } diff --git a/sig0.go b/sig0.go index 2c4b1035..057bb578 100644 --- a/sig0.go +++ b/sig0.go @@ -7,7 +7,6 @@ import ( "crypto/rsa" "encoding/binary" "math/big" - "strings" "time" ) @@ -151,7 +150,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { } // If key has come from the DNS name compression might // have mangled the case of the name - if !strings.EqualFold(signername, k.Header().Name) { + if !equal(signername, k.Header().Name) { return &Error{err: "signer name doesn't match key name"} } sigend := offset diff --git a/sig0_test.go b/sig0_test.go index 5b991d22..a8bf867c 100644 --- a/sig0_test.go +++ b/sig0_test.go @@ -25,7 +25,7 @@ func TestSIG0(t *testing.T) { keysize = 256 case ECDSAP384SHA384: keysize = 384 - case RSASHA512: + case RSASHA1, RSASHA256, RSASHA512: keysize = 1024 } pk, err := keyrr.Generate(keysize) diff --git a/svcb.go b/svcb.go index 310c7d11..598103c1 100644 --- a/svcb.go +++ b/svcb.go @@ -214,11 +214,7 @@ func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue { } } -// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-08). -// -// NOTE: The HTTPS/SVCB RFCs are in the draft stage. -// The API, including constants and types related to SVCBKeyValues, may -// change in future versions in accordance with the latest drafts. +// 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 @@ -226,12 +222,8 @@ type SVCB struct { Value []SVCBKeyValue `dns:"pairs"` } -// HTTPS RR. Everything valid for SVCB applies to HTTPS as well. +// 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. -// -// NOTE: The HTTPS/SVCB RFCs are in the draft stage. -// The API, including constants and types related to SVCBKeyValues, may -// change in future versions in accordance with the latest drafts. type HTTPS struct { SVCB } @@ -306,7 +298,7 @@ func (s *SVCBMandatory) pack() ([]byte, error) { 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") + return errors.New("bad svcbmandatory: value length is not a multiple of 2") } codes := make([]SVCBKey, 0, len(b)/2) for i := 0; i < len(b); i += 2 { @@ -403,10 +395,10 @@ func (s *SVCBAlpn) pack() ([]byte, error) { b := make([]byte, 0, 10*len(s.Alpn)) for _, e := range s.Alpn { if e == "" { - return nil, errors.New("dns: svcbalpn: empty alpn-id") + return nil, errors.New("bad svcbalpn: empty alpn-id") } if len(e) > 255 { - return nil, errors.New("dns: svcbalpn: alpn-id too long") + return nil, errors.New("bad svcbalpn: alpn-id too long") } b = append(b, byte(len(e))) b = append(b, e...) @@ -421,7 +413,7 @@ func (s *SVCBAlpn) unpack(b []byte) error { length := int(b[i]) i++ if i+length > len(b) { - return errors.New("dns: svcbalpn: alpn array overflowing") + return errors.New("bad svcbalpn: alpn array overflowing") } alpn = append(alpn, string(b[i:i+length])) i += length @@ -441,13 +433,13 @@ func (s *SVCBAlpn) parse(b string) error { for p := 0; p < len(b); { c, q := nextByte(b, p) if q == 0 { - return errors.New("dns: svcbalpn: unterminated escape") + return errors.New("bad 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") + return errors.New("bad svcbalpn: empty protocol identifier") } alpn = append(alpn, string(a)) a = []byte{} @@ -457,10 +449,10 @@ func (s *SVCBAlpn) parse(b string) error { if c == '\\' { dc, dq := nextByte(b, p) if dq == 0 { - return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list") + return errors.New("bad svcbalpn: unterminated escape decoding comma-separated list") } if dc != '\\' && dc != ',' { - return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list") + return errors.New("bad svcbalpn: bad escaped character decoding comma-separated list") } p += dq c = dc @@ -469,7 +461,7 @@ func (s *SVCBAlpn) parse(b string) error { } // Add the final alpn. if len(a) == 0 { - return errors.New("dns: svcbalpn: last protocol identifier empty") + return errors.New("bad svcbalpn: last protocol identifier empty") } s.Alpn = append(alpn, string(a)) return nil @@ -507,14 +499,14 @@ 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 errors.New("bad 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 errors.New("bad svcbnodefaultalpn: no-default-alpn must have no value") } return nil } @@ -537,7 +529,7 @@ 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") + return errors.New("bad svcbport: port length is not exactly 2 octets") } s.Port = binary.BigEndian.Uint16(b) return nil @@ -552,7 +544,7 @@ func (s *SVCBPort) pack() ([]byte, error) { 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") + return errors.New("bad svcbport: port out of range") } s.Port = uint16(port) return nil @@ -585,7 +577,7 @@ func (s *SVCBIPv4Hint) pack() ([]byte, error) { for _, e := range s.Hint { x := e.To4() if x == nil { - return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6") + return nil, errors.New("bad svcbipv4hint: expected ipv4, hint is ipv6") } b = append(b, x...) } @@ -594,7 +586,7 @@ func (s *SVCBIPv4Hint) pack() ([]byte, error) { 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") + return errors.New("bad svcbipv4hint: ipv4 address byte array length is not a multiple of 4") } b = cloneSlice(b) x := make([]net.IP, 0, len(b)/4) @@ -619,10 +611,10 @@ func (s *SVCBIPv4Hint) String() string { func (s *SVCBIPv4Hint) parse(b string) error { if b == "" { - return errors.New("dns: svcbipv4hint: empty hint") + return errors.New("bad svcbipv4hint: empty hint") } if strings.Contains(b, ":") { - return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6") + return errors.New("bad svcbipv4hint: expected ipv4, got ipv6") } hint := make([]net.IP, 0, strings.Count(b, ",")+1) @@ -631,7 +623,7 @@ func (s *SVCBIPv4Hint) parse(b string) error { e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e).To4() if ip == nil { - return errors.New("dns: svcbipv4hint: bad ip") + return errors.New("bad svcbipv4hint: bad ip") } hint = append(hint, ip) } @@ -679,7 +671,7 @@ func (s *SVCBECHConfig) unpack(b []byte) error { func (s *SVCBECHConfig) parse(b string) error { x, err := fromBase64([]byte(b)) if err != nil { - return errors.New("dns: svcbech: bad base64 ech") + return errors.New("bad svcbech: bad base64 ech") } s.ECH = x return nil @@ -707,7 +699,7 @@ 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") + return nil, errors.New("bad svcbipv6hint: expected ipv6, hint is ipv4") } b = append(b, e...) } @@ -716,14 +708,14 @@ func (s *SVCBIPv6Hint) pack() ([]byte, error) { 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") + return errors.New("bas 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") + return errors.New("bad svcbipv6hint: expected ipv6, got ipv4") } x = append(x, ip) } @@ -744,7 +736,7 @@ func (s *SVCBIPv6Hint) String() string { func (s *SVCBIPv6Hint) parse(b string) error { if b == "" { - return errors.New("dns: svcbipv6hint: empty hint") + return errors.New("bad svcbipv6hint: empty hint") } hint := make([]net.IP, 0, strings.Count(b, ",")+1) @@ -753,10 +745,10 @@ func (s *SVCBIPv6Hint) parse(b string) error { e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e) if ip == nil { - return errors.New("dns: svcbipv6hint: bad ip") + return errors.New("bad svcbipv6hint: bad ip") } if ip.To4() != nil { - return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6") + return errors.New("bad svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6") } hint = append(hint, ip) } @@ -808,7 +800,7 @@ func (s *SVCBDoHPath) unpack(b []byte) error { func (s *SVCBDoHPath) parse(b string) error { template, err := svcbParseParam(b) if err != nil { - return fmt.Errorf("dns: svcbdohpath: %w", err) + return fmt.Errorf("bad svcbdohpath: %w", err) } s.Template = string(template) return nil @@ -846,14 +838,14 @@ 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 errors.New("bad 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 errors.New("bad svcbotthp: svcbotthp must have no value") } return nil } @@ -886,7 +878,7 @@ func (s *SVCBLocal) unpack(b []byte) error { 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) + return fmt.Errorf("bad svcblocal: svcb private/experimental key %w", err) } s.Data = data return nil diff --git a/tools.go b/tools.go deleted file mode 100644 index ccf8f6bf..00000000 --- a/tools.go +++ /dev/null @@ -1,10 +0,0 @@ -//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" diff --git a/types.go b/types.go index 7a34c14c..f5067cd4 100644 --- a/types.go +++ b/types.go @@ -101,6 +101,7 @@ const ( TypeCAA uint16 = 257 TypeAVC uint16 = 258 TypeAMTRELAY uint16 = 260 + TypeRESINFO uint16 = 261 TypeTKEY uint16 = 249 TypeTSIG uint16 = 250 @@ -125,33 +126,35 @@ const ( 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] + 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] + RcodeStatefulTypeNotImplemented = 11 // DSOTypeNI - DSO-TYPE not implemented [DNS Stateful Operations] https://www.rfc-editor.org/rfc/rfc8490.html#section-10.2 + 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 + OpcodeQuery = 0 + OpcodeIQuery = 1 + OpcodeStatus = 2 + OpcodeNotify = 4 + OpcodeUpdate = 5 + OpcodeStateful = 6 ) // Used in ZONEMD https://tools.ietf.org/html/rfc8976 @@ -178,6 +181,19 @@ const ( AMTRELAYHost = IPSECGatewayHost ) +// Stateful types as defined in RFC 8490. +const ( + StatefulTypeKeepAlive uint16 = iota + 1 + StatefulTypeRetryDelay + StatefulTypeEncryptionPadding +) + +var StatefulTypeToString = map[uint16]string{ + StatefulTypeKeepAlive: "KeepAlive", + StatefulTypeRetryDelay: "RetryDelay", + StatefulTypeEncryptionPadding: "EncryptionPadding", +} + // Header is the wire format for the DNS packet header. type Header struct { Id uint16 @@ -267,11 +283,20 @@ func (q *Question) String() (s string) { return s } -// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY -// is named "*" there. +// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY is named "*" there. +// The ANY records can be (ab)used to create resource records without any rdata, that +// can be used in dynamic update requests. Basic use pattern: +// +// a := &ANY{RR_Header{ +// Name: "example.org.", +// Rrtype: TypeA, +// Class: ClassINET, +// }} +// +// Results in an A record without rdata. type ANY struct { Hdr RR_Header - // Does not have any rdata + // Does not have any rdata. } func (rr *ANY) String() string { return rr.Hdr.String() } @@ -876,7 +901,7 @@ func (rr *LOC) String() string { 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 := float64(rr.Altitude) / 100 alt -= LOC_ALTITUDEBASE if rr.Altitude%100 != 0 { s += fmt.Sprintf("%.2fm ", alt) @@ -1508,6 +1533,15 @@ func (rr *ZONEMD) String() string { " " + 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 diff --git a/udp.go b/udp.go index c018ad43..d2267185 100644 --- a/udp.go +++ b/udp.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build !windows && !darwin +// +build !windows,!darwin package dns diff --git a/udp_windows.go b/udp_no_control.go similarity index 85% rename from udp_windows.go rename to udp_no_control.go index a259b67e..ca3d4a63 100644 --- a/udp_windows.go +++ b/udp_no_control.go @@ -1,9 +1,11 @@ -//go:build windows -// +build windows +//go:build windows || darwin +// +build windows darwin // TODO(tmthrgd): Remove this Windows-specific code if go.dev/issue/7175 and // go.dev/issue/7174 are ever fixed. +// NOTICE(stek29): darwin supports PKTINFO in sendmsg, but it unbinds sockets, see https://github.com/miekg/dns/issues/724 + package dns import "net" diff --git a/update.go b/update.go index 16f9ee85..2fef1461 100644 --- a/update.go +++ b/update.go @@ -2,6 +2,7 @@ package dns // NameUsed sets the RRs in the prereq section to // "Name is in use" RRs. RFC 2136 section 2.4.4. +// See [ANY] on how to make RRs without rdata. func (u *Msg) NameUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) @@ -41,6 +42,7 @@ func (u *Msg) Used(rr []RR) { // RRsetUsed sets the RRs in the prereq section to // "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1. +// See [ANY] on how to make RRs without rdata. func (u *Msg) RRsetUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) @@ -53,6 +55,7 @@ func (u *Msg) RRsetUsed(rr []RR) { // RRsetNotUsed sets the RRs in the prereq section to // "RRset does not exist" RRs. RFC 2136 section 2.4.3. +// See [ANY] on how to make RRs without rdata. func (u *Msg) RRsetNotUsed(rr []RR) { if u.Answer == nil { u.Answer = make([]RR, 0, len(rr)) @@ -64,6 +67,7 @@ func (u *Msg) RRsetNotUsed(rr []RR) { } // Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1. +// See [ANY] on how to make RRs without rdata. func (u *Msg) Insert(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") @@ -78,6 +82,7 @@ func (u *Msg) Insert(rr []RR) { } // RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2. +// See [ANY] on how to make RRs without rdata. func (u *Msg) RemoveRRset(rr []RR) { if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) @@ -89,6 +94,7 @@ func (u *Msg) RemoveRRset(rr []RR) { } // RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3 +// See [ANY] on how to make RRs without rdata. func (u *Msg) RemoveName(rr []RR) { if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) @@ -99,6 +105,7 @@ func (u *Msg) RemoveName(rr []RR) { } // Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4 +// See [ANY] on how to make RRs without rdata. func (u *Msg) Remove(rr []RR) { if u.Ns == nil { u.Ns = make([]RR, 0, len(rr)) diff --git a/version.go b/version.go index 00c8629f..78cfcc39 100644 --- a/version.go +++ b/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 62} +var Version = v{1, 1, 73} // v holds the version of this library. type v struct { diff --git a/xfr.go b/xfr.go index 5cfbb516..97a64247 100644 --- a/xfr.go +++ b/xfr.go @@ -251,10 +251,13 @@ func (t *Transfer) ReadMsg() (*Msg, error) { if err := m.Unpack(p); err != nil { return nil, err } - if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil { + + if tp := t.tsigProvider(); 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 + if ts := m.IsTsig(); ts != nil { + t.tsigRequestMAC = ts.MAC + } } return m, err } diff --git a/xfr_test.go b/xfr_test.go index 04801a2e..c580ceef 100644 --- a/xfr_test.go +++ b/xfr_test.go @@ -2,6 +2,7 @@ package dns import ( "crypto/tls" + "errors" "testing" "time" ) @@ -221,6 +222,29 @@ func axfrTestingSuiteWithCustomTsig(t *testing.T, addrstr string, provider TsigP } } +func axfrTestingSuiteWithMsgNotSigned(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.") + + c, err := tr.In(m, addrstr) + if err != nil { + t.Fatal("failed to zone transfer in", err) + } + + for msg := range c { + if !errors.Is(msg.Error, ErrNoSig) { + t.Fatal("expecting ErrNoSig error") + } + } +} + func TestCustomTsigProvider(t *testing.T) { HandleFunc("miek.nl.", SingleEnvelopeXfrServer) defer HandleRemove("miek.nl.") @@ -235,3 +259,16 @@ func TestCustomTsigProvider(t *testing.T) { axfrTestingSuiteWithCustomTsig(t, addrstr, tsigSecretProvider(tsigSecret)) } + +func TestTSIGNotSigned(t *testing.T) { + HandleFunc("miek.nl.", SingleEnvelopeXfrServer) + defer HandleRemove("miek.nl.") + + s, addrstr, _, err := RunLocalTCPServer(":0") + if err != nil { + t.Fatalf("unable to run test server: %s", err) + } + defer s.Shutdown() + + axfrTestingSuiteWithMsgNotSigned(t, addrstr, tsigSecretProvider(tsigSecret)) +} diff --git a/zduplicate.go b/zduplicate.go index 330c0539..ebd9e029 100644 --- a/zduplicate.go +++ b/zduplicate.go @@ -957,6 +957,23 @@ func (r1 *PX) isDuplicate(_r2 RR) bool { 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 { diff --git a/zmsg.go b/zmsg.go index 5a6cf4c6..b2b49b15 100644 --- a/zmsg.go +++ b/zmsg.go @@ -2,6 +2,8 @@ package dns +import "fmt" + // pack*() functions func (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { @@ -762,6 +764,14 @@ func (rr *PX) pack(msg []byte, off int, compression compressionMap, compress boo 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 { @@ -1214,7 +1224,7 @@ func (rr *A) unpack(msg []byte, off int) (off1 int, err error) { rr.A, off, err = unpackDataA(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("A: %w", err) } return off, nil } @@ -1225,7 +1235,7 @@ func (rr *AAAA) unpack(msg []byte, off int) (off1 int, err error) { rr.AAAA, off, err = unpackDataAAAA(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("AAAA: %w", err) } return off, nil } @@ -1236,14 +1246,14 @@ func (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) { rr.Subtype, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("AFSDB.Subtype: %w", err) } if off == len(msg) { return off, nil } rr.Hostname, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("AFSDB.Hostname: %w", err) } return off, nil } @@ -1254,14 +1264,14 @@ func (rr *AMTRELAY) unpack(msg []byte, off int) (off1 int, err error) { rr.Precedence, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("AMTRELAY.Precedence: %w", err) } if off == len(msg) { return off, nil } rr.GatewayType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("AMTRELAY.GatewayType: %w", err) } if off == len(msg) { return off, nil @@ -1271,7 +1281,7 @@ func (rr *AMTRELAY) unpack(msg []byte, off int) (off1 int, err error) { } rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) if err != nil { - return off, err + return off, fmt.Errorf("AMTRELAY.GatewayHost: %w", err) } return off, nil } @@ -1289,7 +1299,7 @@ func (rr *APL) unpack(msg []byte, off int) (off1 int, err error) { rr.Prefixes, off, err = unpackDataApl(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("APL.Prefixes: %w", err) } return off, nil } @@ -1300,7 +1310,7 @@ func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) { rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("AVC.Txt: %w", err) } return off, nil } @@ -1311,21 +1321,21 @@ func (rr *CAA) unpack(msg []byte, off int) (off1 int, err error) { rr.Flag, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CAA.Flag: %w", err) } if off == len(msg) { return off, nil } rr.Tag, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CAA.Tag: %w", err) } if off == len(msg) { return off, nil } rr.Value, off, err = unpackStringOctet(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CAA.Value: %w", err) } return off, nil } @@ -1336,28 +1346,28 @@ func (rr *CDNSKEY) unpack(msg []byte, off int) (off1 int, err error) { rr.Flags, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CDNSKEY.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CDNSKEY.Protocol: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CDNSKEY.Algorithm: %w", 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, fmt.Errorf("CDNSKEY.PublicKey: %w", err) } return off, nil } @@ -1368,28 +1378,28 @@ func (rr *CDS) unpack(msg []byte, off int) (off1 int, err error) { rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CDS.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CDS.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CDS.DigestType: %w", 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, fmt.Errorf("CDS.Digest: %w", err) } return off, nil } @@ -1400,28 +1410,28 @@ func (rr *CERT) unpack(msg []byte, off int) (off1 int, err error) { rr.Type, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CERT.Type: %w", err) } if off == len(msg) { return off, nil } rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CERT.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CERT.Algorithm: %w", 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, fmt.Errorf("CERT.Certificate: %w", err) } return off, nil } @@ -1432,7 +1442,7 @@ func (rr *CNAME) unpack(msg []byte, off int) (off1 int, err error) { rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CNAME.Target: %w", err) } return off, nil } @@ -1443,21 +1453,21 @@ func (rr *CSYNC) unpack(msg []byte, off int) (off1 int, err error) { rr.Serial, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CSYNC.Serial: %w", err) } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CSYNC.Flags: %w", err) } if off == len(msg) { return off, nil } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("CSYNC.TypeBitMap: %w", err) } return off, nil } @@ -1468,7 +1478,7 @@ func (rr *DHCID) unpack(msg []byte, off int) (off1 int, err error) { rr.Digest, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("DHCID.Digest: %w", err) } return off, nil } @@ -1479,28 +1489,28 @@ func (rr *DLV) unpack(msg []byte, off int) (off1 int, err error) { rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DLV.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DLV.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DLV.DigestType: %w", 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, fmt.Errorf("DLV.Digest: %w", err) } return off, nil } @@ -1511,7 +1521,7 @@ func (rr *DNAME) unpack(msg []byte, off int) (off1 int, err error) { rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DNAME.Target: %w", err) } return off, nil } @@ -1522,28 +1532,28 @@ func (rr *DNSKEY) unpack(msg []byte, off int) (off1 int, err error) { rr.Flags, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DNSKEY.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DNSKEY.Protocol: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DNSKEY.Algorithm: %w", 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, fmt.Errorf("DNSKEY.PublicKey: %w", err) } return off, nil } @@ -1554,28 +1564,28 @@ func (rr *DS) unpack(msg []byte, off int) (off1 int, err error) { rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DS.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DS.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("DS.DigestType: %w", 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, fmt.Errorf("DS.Digest: %w", err) } return off, nil } @@ -1586,7 +1596,7 @@ func (rr *EID) unpack(msg []byte, off int) (off1 int, err error) { rr.Endpoint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("EID.Endpoint: %w", err) } return off, nil } @@ -1597,7 +1607,7 @@ func (rr *EUI48) unpack(msg []byte, off int) (off1 int, err error) { rr.Address, off, err = unpackUint48(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("EUI48.Address: %w", err) } return off, nil } @@ -1608,7 +1618,7 @@ func (rr *EUI64) unpack(msg []byte, off int) (off1 int, err error) { rr.Address, off, err = unpackUint64(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("EUI64.Address: %w", err) } return off, nil } @@ -1619,7 +1629,7 @@ func (rr *GID) unpack(msg []byte, off int) (off1 int, err error) { rr.Gid, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("GID.Gid: %w", err) } return off, nil } @@ -1630,21 +1640,21 @@ func (rr *GPOS) unpack(msg []byte, off int) (off1 int, err error) { rr.Longitude, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("GPOS.Longitude: %w", err) } if off == len(msg) { return off, nil } rr.Latitude, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("GPOS.Latitude: %w", err) } if off == len(msg) { return off, nil } rr.Altitude, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("GPOS.Altitude: %w", err) } return off, nil } @@ -1655,14 +1665,14 @@ func (rr *HINFO) unpack(msg []byte, off int) (off1 int, err error) { rr.Cpu, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HINFO.Cpu: %w", err) } if off == len(msg) { return off, nil } rr.Os, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HINFO.Os: %w", err) } return off, nil } @@ -1673,21 +1683,21 @@ func (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) { rr.HitLength, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HIP.HitLength: %w", err) } if off == len(msg) { return off, nil } rr.PublicKeyAlgorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HIP.PublicKeyAlgorithm: %w", err) } if off == len(msg) { return off, nil } rr.PublicKeyLength, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HIP.PublicKeyLength: %w", err) } if off == len(msg) { return off, nil @@ -1702,7 +1712,7 @@ func (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) { } rr.RendezvousServers, off, err = unpackDataDomainNames(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("HIP.RendezvousServers: %w", err) } return off, nil } @@ -1713,21 +1723,21 @@ func (rr *HTTPS) unpack(msg []byte, off int) (off1 int, err error) { rr.Priority, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HTTPS.Priority: %w", err) } if off == len(msg) { return off, nil } rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HTTPS.Target: %w", err) } if off == len(msg) { return off, nil } rr.Value, off, err = unpackDataSVCB(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("HTTPS.Value: %w", err) } return off, nil } @@ -1738,21 +1748,21 @@ func (rr *IPSECKEY) unpack(msg []byte, off int) (off1 int, err error) { rr.Precedence, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("IPSECKEY.Precedence: %w", err) } if off == len(msg) { return off, nil } rr.GatewayType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("IPSECKEY.GatewayType: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("IPSECKEY.Algorithm: %w", err) } if off == len(msg) { return off, nil @@ -1762,14 +1772,14 @@ func (rr *IPSECKEY) unpack(msg []byte, off int) (off1 int, err error) { } rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) if err != nil { - return off, err + return off, fmt.Errorf("IPSECKEY.GatewayHost: %w", 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, fmt.Errorf("IPSECKEY.PublicKey: %w", err) } return off, nil } @@ -1780,14 +1790,14 @@ func (rr *ISDN) unpack(msg []byte, off int) (off1 int, err error) { rr.Address, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("ISDN.Address: %w", err) } if off == len(msg) { return off, nil } rr.SubAddress, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("ISDN.SubAddress: %w", err) } return off, nil } @@ -1798,28 +1808,28 @@ func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) { rr.Flags, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("KEY.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("KEY.Protocol: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("KEY.Algorithm: %w", 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, fmt.Errorf("KEY.PublicKey: %w", err) } return off, nil } @@ -1830,14 +1840,14 @@ func (rr *KX) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("KX.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Exchanger, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("KX.Exchanger: %w", err) } return off, nil } @@ -1848,14 +1858,14 @@ func (rr *L32) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("L32.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Locator32, off, err = unpackDataA(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("L32.Locator32: %w", err) } return off, nil } @@ -1866,14 +1876,14 @@ func (rr *L64) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("L64.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Locator64, off, err = unpackUint64(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("L64.Locator64: %w", err) } return off, nil } @@ -1884,49 +1894,49 @@ func (rr *LOC) unpack(msg []byte, off int) (off1 int, err error) { rr.Version, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.Version: %w", err) } if off == len(msg) { return off, nil } rr.Size, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.Size: %w", err) } if off == len(msg) { return off, nil } rr.HorizPre, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.HorizPre: %w", err) } if off == len(msg) { return off, nil } rr.VertPre, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.VertPre: %w", err) } if off == len(msg) { return off, nil } rr.Latitude, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.Latitude: %w", err) } if off == len(msg) { return off, nil } rr.Longitude, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.Longitude: %w", err) } if off == len(msg) { return off, nil } rr.Altitude, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LOC.Altitude: %w", err) } return off, nil } @@ -1937,14 +1947,14 @@ func (rr *LP) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LP.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Fqdn, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("LP.Fqdn: %w", err) } return off, nil } @@ -1955,7 +1965,7 @@ func (rr *MB) unpack(msg []byte, off int) (off1 int, err error) { rr.Mb, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MB.Mb: %w", err) } return off, nil } @@ -1966,7 +1976,7 @@ func (rr *MD) unpack(msg []byte, off int) (off1 int, err error) { rr.Md, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MD.Md: %w", err) } return off, nil } @@ -1977,7 +1987,7 @@ func (rr *MF) unpack(msg []byte, off int) (off1 int, err error) { rr.Mf, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MF.Mf: %w", err) } return off, nil } @@ -1988,7 +1998,7 @@ func (rr *MG) unpack(msg []byte, off int) (off1 int, err error) { rr.Mg, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MG.Mg: %w", err) } return off, nil } @@ -1999,14 +2009,14 @@ func (rr *MINFO) unpack(msg []byte, off int) (off1 int, err error) { rr.Rmail, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MINFO.Rmail: %w", err) } if off == len(msg) { return off, nil } rr.Email, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MINFO.Email: %w", err) } return off, nil } @@ -2017,7 +2027,7 @@ func (rr *MR) unpack(msg []byte, off int) (off1 int, err error) { rr.Mr, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MR.Mr: %w", err) } return off, nil } @@ -2028,14 +2038,14 @@ func (rr *MX) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MX.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Mx, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("MX.Mx: %w", err) } return off, nil } @@ -2046,42 +2056,42 @@ func (rr *NAPTR) unpack(msg []byte, off int) (off1 int, err error) { rr.Order, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NAPTR.Order: %w", err) } if off == len(msg) { return off, nil } rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NAPTR.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NAPTR.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Service, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NAPTR.Service: %w", err) } if off == len(msg) { return off, nil } rr.Regexp, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NAPTR.Regexp: %w", err) } if off == len(msg) { return off, nil } rr.Replacement, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NAPTR.Replacement: %w", err) } return off, nil } @@ -2092,14 +2102,14 @@ func (rr *NID) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NID.Preference: %w", err) } if off == len(msg) { return off, nil } rr.NodeID, off, err = unpackUint64(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NID.NodeID: %w", err) } return off, nil } @@ -2110,7 +2120,7 @@ func (rr *NIMLOC) unpack(msg []byte, off int) (off1 int, err error) { rr.Locator, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("NIMLOC.Locator: %w", err) } return off, nil } @@ -2121,7 +2131,7 @@ func (rr *NINFO) unpack(msg []byte, off int) (off1 int, err error) { rr.ZSData, off, err = unpackStringTxt(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NINFO.ZSData: %w", err) } return off, nil } @@ -2132,7 +2142,7 @@ func (rr *NS) unpack(msg []byte, off int) (off1 int, err error) { rr.Ns, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NS.Ns: %w", err) } return off, nil } @@ -2143,7 +2153,7 @@ func (rr *NSAPPTR) unpack(msg []byte, off int) (off1 int, err error) { rr.Ptr, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSAPPTR.Ptr: %w", err) } return off, nil } @@ -2154,14 +2164,14 @@ func (rr *NSEC) unpack(msg []byte, off int) (off1 int, err error) { rr.NextDomain, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC.NextDomain: %w", err) } if off == len(msg) { return off, nil } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC.TypeBitMap: %w", err) } return off, nil } @@ -2172,28 +2182,28 @@ func (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) { rr.Hash, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3.Hash: %w", err) } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Iterations, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3.Iterations: %w", err) } if off == len(msg) { return off, nil } rr.SaltLength, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3.SaltLength: %w", err) } if off == len(msg) { return off, nil @@ -2204,7 +2214,7 @@ func (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) { } rr.HashLength, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3.HashLength: %w", err) } if off == len(msg) { return off, nil @@ -2215,7 +2225,7 @@ func (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) { } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3.TypeBitMap: %w", err) } return off, nil } @@ -2226,28 +2236,28 @@ func (rr *NSEC3PARAM) unpack(msg []byte, off int) (off1 int, err error) { rr.Hash, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3PARAM.Hash: %w", err) } if off == len(msg) { return off, nil } rr.Flags, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3PARAM.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Iterations, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3PARAM.Iterations: %w", err) } if off == len(msg) { return off, nil } rr.SaltLength, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NSEC3PARAM.SaltLength: %w", err) } if off == len(msg) { return off, nil @@ -2265,7 +2275,7 @@ func (rr *NULL) unpack(msg []byte, off int) (off1 int, err error) { rr.Data, off, err = unpackStringAny(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("NULL.Data: %w", err) } return off, nil } @@ -2283,14 +2293,14 @@ func (rr *NXT) unpack(msg []byte, off int) (off1 int, err error) { rr.NextDomain, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NXT.NextDomain: %w", err) } if off == len(msg) { return off, nil } rr.TypeBitMap, off, err = unpackDataNsec(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("NXT.TypeBitMap: %w", err) } return off, nil } @@ -2301,7 +2311,7 @@ func (rr *OPENPGPKEY) unpack(msg []byte, off int) (off1 int, err error) { rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("OPENPGPKEY.PublicKey: %w", err) } return off, nil } @@ -2312,7 +2322,7 @@ func (rr *OPT) unpack(msg []byte, off int) (off1 int, err error) { rr.Option, off, err = unpackDataOpt(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("OPT.Option: %w", err) } return off, nil } @@ -2323,7 +2333,7 @@ func (rr *PTR) unpack(msg []byte, off int) (off1 int, err error) { rr.Ptr, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("PTR.Ptr: %w", err) } return off, nil } @@ -2334,21 +2344,32 @@ func (rr *PX) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("PX.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Map822, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("PX.Map822: %w", err) } if off == len(msg) { return off, nil } rr.Mapx400, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("PX.Mapx400: %w", 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, fmt.Errorf("RESINFO.Txt: %w", err) } return off, nil } @@ -2359,7 +2380,7 @@ func (rr *RFC3597) unpack(msg []byte, off int) (off1 int, err error) { rr.Rdata, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) if err != nil { - return off, err + return off, fmt.Errorf("RFC3597.Rdata: %w", err) } return off, nil } @@ -2370,28 +2391,28 @@ func (rr *RKEY) unpack(msg []byte, off int) (off1 int, err error) { rr.Flags, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RKEY.Flags: %w", err) } if off == len(msg) { return off, nil } rr.Protocol, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RKEY.Protocol: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RKEY.Algorithm: %w", 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, fmt.Errorf("RKEY.PublicKey: %w", err) } return off, nil } @@ -2402,14 +2423,14 @@ func (rr *RP) unpack(msg []byte, off int) (off1 int, err error) { rr.Mbox, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RP.Mbox: %w", err) } if off == len(msg) { return off, nil } rr.Txt, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RP.Txt: %w", err) } return off, nil } @@ -2420,63 +2441,63 @@ func (rr *RRSIG) unpack(msg []byte, off int) (off1 int, err error) { rr.TypeCovered, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.TypeCovered: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.Labels, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.Labels: %w", err) } if off == len(msg) { return off, nil } rr.OrigTtl, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.OrigTtl: %w", err) } if off == len(msg) { return off, nil } rr.Expiration, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.Expiration: %w", err) } if off == len(msg) { return off, nil } rr.Inception, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.Inception: %w", err) } if off == len(msg) { return off, nil } rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.SignerName, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RRSIG.SignerName: %w", 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, fmt.Errorf("RRSIG.Signature: %w", err) } return off, nil } @@ -2487,14 +2508,14 @@ func (rr *RT) unpack(msg []byte, off int) (off1 int, err error) { rr.Preference, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RT.Preference: %w", err) } if off == len(msg) { return off, nil } rr.Host, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("RT.Host: %w", err) } return off, nil } @@ -2505,63 +2526,63 @@ func (rr *SIG) unpack(msg []byte, off int) (off1 int, err error) { rr.TypeCovered, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.TypeCovered: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.Labels, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.Labels: %w", err) } if off == len(msg) { return off, nil } rr.OrigTtl, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.OrigTtl: %w", err) } if off == len(msg) { return off, nil } rr.Expiration, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.Expiration: %w", err) } if off == len(msg) { return off, nil } rr.Inception, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.Inception: %w", err) } if off == len(msg) { return off, nil } rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.SignerName, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SIG.SignerName: %w", 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, fmt.Errorf("SIG.Signature: %w", err) } return off, nil } @@ -2572,28 +2593,28 @@ func (rr *SMIMEA) unpack(msg []byte, off int) (off1 int, err error) { rr.Usage, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SMIMEA.Usage: %w", err) } if off == len(msg) { return off, nil } rr.Selector, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SMIMEA.Selector: %w", err) } if off == len(msg) { return off, nil } rr.MatchingType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SMIMEA.MatchingType: %w", 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, fmt.Errorf("SMIMEA.Certificate: %w", err) } return off, nil } @@ -2604,49 +2625,49 @@ func (rr *SOA) unpack(msg []byte, off int) (off1 int, err error) { rr.Ns, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Ns: %w", err) } if off == len(msg) { return off, nil } rr.Mbox, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Mbox: %w", err) } if off == len(msg) { return off, nil } rr.Serial, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Serial: %w", err) } if off == len(msg) { return off, nil } rr.Refresh, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Refresh: %w", err) } if off == len(msg) { return off, nil } rr.Retry, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Retry: %w", err) } if off == len(msg) { return off, nil } rr.Expire, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Expire: %w", err) } if off == len(msg) { return off, nil } rr.Minttl, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SOA.Minttl: %w", err) } return off, nil } @@ -2657,7 +2678,7 @@ func (rr *SPF) unpack(msg []byte, off int) (off1 int, err error) { rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SPF.Txt: %w", err) } return off, nil } @@ -2668,28 +2689,28 @@ func (rr *SRV) unpack(msg []byte, off int) (off1 int, err error) { rr.Priority, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SRV.Priority: %w", err) } if off == len(msg) { return off, nil } rr.Weight, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SRV.Weight: %w", err) } if off == len(msg) { return off, nil } rr.Port, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SRV.Port: %w", err) } if off == len(msg) { return off, nil } rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SRV.Target: %w", err) } return off, nil } @@ -2700,21 +2721,21 @@ func (rr *SSHFP) unpack(msg []byte, off int) (off1 int, err error) { rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SSHFP.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.Type, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SSHFP.Type: %w", 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, fmt.Errorf("SSHFP.FingerPrint: %w", err) } return off, nil } @@ -2725,21 +2746,21 @@ func (rr *SVCB) unpack(msg []byte, off int) (off1 int, err error) { rr.Priority, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SVCB.Priority: %w", err) } if off == len(msg) { return off, nil } rr.Target, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SVCB.Target: %w", err) } if off == len(msg) { return off, nil } rr.Value, off, err = unpackDataSVCB(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("SVCB.Value: %w", err) } return off, nil } @@ -2750,28 +2771,28 @@ func (rr *TA) unpack(msg []byte, off int) (off1 int, err error) { rr.KeyTag, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TA.KeyTag: %w", err) } if off == len(msg) { return off, nil } rr.Algorithm, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TA.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.DigestType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TA.DigestType: %w", 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, fmt.Errorf("TA.Digest: %w", err) } return off, nil } @@ -2782,14 +2803,14 @@ func (rr *TALINK) unpack(msg []byte, off int) (off1 int, err error) { rr.PreviousName, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TALINK.PreviousName: %w", err) } if off == len(msg) { return off, nil } rr.NextName, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TALINK.NextName: %w", err) } return off, nil } @@ -2800,42 +2821,42 @@ func (rr *TKEY) unpack(msg []byte, off int) (off1 int, err error) { rr.Algorithm, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.Inception, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.Inception: %w", err) } if off == len(msg) { return off, nil } rr.Expiration, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.Expiration: %w", err) } if off == len(msg) { return off, nil } rr.Mode, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.Mode: %w", err) } if off == len(msg) { return off, nil } rr.Error, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.Error: %w", err) } if off == len(msg) { return off, nil } rr.KeySize, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.KeySize: %w", err) } if off == len(msg) { return off, nil @@ -2846,7 +2867,7 @@ func (rr *TKEY) unpack(msg []byte, off int) (off1 int, err error) { } rr.OtherLen, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TKEY.OtherLen: %w", err) } if off == len(msg) { return off, nil @@ -2864,28 +2885,28 @@ func (rr *TLSA) unpack(msg []byte, off int) (off1 int, err error) { rr.Usage, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TLSA.Usage: %w", err) } if off == len(msg) { return off, nil } rr.Selector, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TLSA.Selector: %w", err) } if off == len(msg) { return off, nil } rr.MatchingType, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TLSA.MatchingType: %w", 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, fmt.Errorf("TLSA.Certificate: %w", err) } return off, nil } @@ -2896,28 +2917,28 @@ func (rr *TSIG) unpack(msg []byte, off int) (off1 int, err error) { rr.Algorithm, off, err = UnpackDomainName(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.Algorithm: %w", err) } if off == len(msg) { return off, nil } rr.TimeSigned, off, err = unpackUint48(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.TimeSigned: %w", err) } if off == len(msg) { return off, nil } rr.Fudge, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.Fudge: %w", err) } if off == len(msg) { return off, nil } rr.MACSize, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.MACSize: %w", err) } if off == len(msg) { return off, nil @@ -2928,21 +2949,21 @@ func (rr *TSIG) unpack(msg []byte, off int) (off1 int, err error) { } rr.OrigId, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.OrigId: %w", err) } if off == len(msg) { return off, nil } rr.Error, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.Error: %w", err) } if off == len(msg) { return off, nil } rr.OtherLen, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TSIG.OtherLen: %w", err) } if off == len(msg) { return off, nil @@ -2960,7 +2981,7 @@ func (rr *TXT) unpack(msg []byte, off int) (off1 int, err error) { rr.Txt, off, err = unpackStringTxt(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("TXT.Txt: %w", err) } return off, nil } @@ -2971,7 +2992,7 @@ func (rr *UID) unpack(msg []byte, off int) (off1 int, err error) { rr.Uid, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("UID.Uid: %w", err) } return off, nil } @@ -2982,7 +3003,7 @@ func (rr *UINFO) unpack(msg []byte, off int) (off1 int, err error) { rr.Uinfo, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("UINFO.Uinfo: %w", err) } return off, nil } @@ -2993,21 +3014,21 @@ func (rr *URI) unpack(msg []byte, off int) (off1 int, err error) { rr.Priority, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("URI.Priority: %w", err) } if off == len(msg) { return off, nil } rr.Weight, off, err = unpackUint16(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("URI.Weight: %w", err) } if off == len(msg) { return off, nil } rr.Target, off, err = unpackStringOctet(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("URI.Target: %w", err) } return off, nil } @@ -3018,7 +3039,7 @@ func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) { rr.PSDNAddress, off, err = unpackString(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("X25.PSDNAddress: %w", err) } return off, nil } @@ -3029,28 +3050,28 @@ func (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) { rr.Serial, off, err = unpackUint32(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("ZONEMD.Serial: %w", err) } if off == len(msg) { return off, nil } rr.Scheme, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("ZONEMD.Scheme: %w", err) } if off == len(msg) { return off, nil } rr.Hash, off, err = unpackUint8(msg, off) if err != nil { - return off, err + return off, fmt.Errorf("ZONEMD.Hash: %w", 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, fmt.Errorf("ZONEMD.Digest: %w", err) } return off, nil } diff --git a/ztypes.go b/ztypes.go index 11f13ecf..cea79ae7 100644 --- a/ztypes.go +++ b/ztypes.go @@ -66,6 +66,7 @@ var TypeToRR = map[uint16]func() RR{ 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) }, @@ -154,6 +155,7 @@ var TypeToString = map[uint16]string{ TypeOPT: "OPT", TypePTR: "PTR", TypePX: "PX", + TypeRESINFO: "RESINFO", TypeRKEY: "RKEY", TypeRP: "RP", TypeRRSIG: "RRSIG", @@ -238,6 +240,7 @@ 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 } @@ -622,6 +625,14 @@ func (rr *PX) len(off int, compression map[string]struct{}) int { 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 @@ -1148,6 +1159,10 @@ func (rr *PX) copy() RR { } } +func (rr *RESINFO) copy() RR { + return &RESINFO{rr.Hdr, cloneSlice(rr.Txt)} +} + func (rr *RFC3597) copy() RR { return &RFC3597{rr.Hdr, rr.Rdata} }