From cbe4275da1c1c2dfcbaf7c37224cbb9a1e49e706 Mon Sep 17 00:00:00 2001 From: Erik Geiser <70747329+rtpt-erikgeiser@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:27:49 +0100 Subject: [PATCH 1/6] Allow SO_REUSEADDR and SO_REUSEPORT to be set simultaneously (#1623) --- ...euseport.go => listen_no_socket_options.go | 22 ++++- ...n_reuseport.go => listen_socket_options.go | 31 ++++++ server_test.go | 96 ++++++++++++++++++- 3 files changed, 144 insertions(+), 5 deletions(-) rename listen_no_reuseport.go => listen_no_socket_options.go (61%) rename listen_reuseport.go => listen_socket_options.go (66%) 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 8cebb2f17..9e4010bdc 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 41326f20b..35dfc9498 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/server_test.go b/server_test.go index 4fc2af329..a9e2339b4 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) } From 8a00a2f968962669182eca425f8ab26830f1aa18 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Fri, 24 Jan 2025 02:28:10 -0800 Subject: [PATCH 2/6] add zdns as a user of miekg/dns (#1608) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8d5a2a478..9831c37ba 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/wintbiit/NineDNS * https://linuxcontainers.org/incus/ * https://ifconfig.es +* https://github.com/zmap/zdns Send pull request if you want to be listed here. From ca39c8b728b2f1ea8cd18c82dc6805c0377feac1 Mon Sep 17 00:00:00 2001 From: Raymond Nook <59678453+developStorm@users.noreply.github.com> Date: Fri, 24 Jan 2025 02:33:32 -0800 Subject: [PATCH 3/6] fix: input validation for RRSIG.Verify() (#1618) * fix: input validation for RRSIG.verify() * fix: use labels.go/equal instead of strings.EqualFold for domain comparison --- dnssec.go | 42 +++++++++++++------ dnssec_test.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++++ sig0.go | 3 +- 3 files changed, 141 insertions(+), 15 deletions(-) diff --git a/dnssec.go b/dnssec.go index 1be87eae6..ffdafcebd 100644 --- a/dnssec.go +++ b/dnssec.go @@ -250,14 +250,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 +264,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 +374,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 +391,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 +416,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 1511278c0..4f04e85f6 100644 --- a/dnssec_test.go +++ b/dnssec_test.go @@ -155,6 +155,117 @@ 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, _ := key.Generate(512) + + normalSoa := getSoa() + + // Fill in the normal values of the Sig, before signing + sig := new(RRSIG) + sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} + sig.TypeCovered = TypeSOA + sig.Labels = uint8(CountLabel(normalSoa.Hdr.Name)) + sig.OrigTtl = normalSoa.Hdr.Ttl + sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" + sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" + sig.KeyTag = key.KeyTag() // Get the keyfrom the Key + sig.SignerName = key.Hdr.Name + sig.Algorithm = RSASHA256 + + for i, rr := range []RR{rrNameMismatch, rrClassMismatch, rrTypeMismatch, rrLabelLessThan} { + if i != 0 { // Just for the rrNameMismatch case, we need the name to mismatch + sig := sig.copy().(*RRSIG) + sig.SignerName = rr.Header().Name + sig.Hdr.Name = rr.Header().Name + key := key.copy().(*DNSKEY) + key.Hdr.Name = rr.Header().Name + } + + if err := sig.signAsIs(privkey.(*rsa.PrivateKey), []RR{rr}); err != nil { + t.Error("failure to sign the record:", err) + continue + } + + if err := sig.Verify(key, []RR{rr}); err == nil { + t.Error("should not validate: ", rr) + continue + } else { + t.Logf("expected failure: %v for RR name %s, class %d, type %d, rrsig labels %d", err, rr.Header().Name, rr.Header().Class, rr.Header().Rrtype, CountLabel(rr.Header().Name)) + } + } + + // The RRSIG RR's Signer's Name field MUST be the name of the zone that contains the RRset. + // The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner name, + // algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset. + sigMismatchName := sig.copy().(*RRSIG) + sigMismatchName.SignerName = "example.com." + soaMismatchName := getSoa() + soaMismatchName.Hdr.Name = "example.com." + keyMismatchName := key.copy().(*DNSKEY) + keyMismatchName.Hdr.Name = "example.com." + if err := sigMismatchName.signAsIs(privkey.(*rsa.PrivateKey), []RR{soaMismatchName}); err != nil { + t.Error("failure to sign the record:", err) + } else if err := sigMismatchName.Verify(keyMismatchName, []RR{soaMismatchName}); err == nil { + t.Error("should not validate: ", soaMismatchName, ", RRSIG's signer's name does not match the owner name") + } else { + t.Logf("expected failure: %v for signer %s and owner %s", err, sigMismatchName.SignerName, sigMismatchName.Hdr.Name) + } + + sigMismatchAlgo := sig.copy().(*RRSIG) + sigMismatchAlgo.Algorithm = RSASHA1 + sigMismatchKeyTag := sig.copy().(*RRSIG) + sigMismatchKeyTag.KeyTag = 12345 + for _, sigMismatch := range []*RRSIG{sigMismatchAlgo, sigMismatchKeyTag} { + if err := sigMismatch.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil { + t.Error("failure to sign the record:", err) + } else if err := sigMismatch.Verify(key, []RR{normalSoa}); err == nil { + t.Error("should not validate: ", normalSoa) + } else { + t.Logf("expected failure: %v for signer %s algo %d keytag %d", err, sigMismatch.SignerName, sigMismatch.Algorithm, sigMismatch.KeyTag) + } + } + + // The matching DNSKEY RR MUST have the Zone Flag bit (DNSKEY RDATA Flag bit 7) set. + keyZoneBitWrong := key.copy().(*DNSKEY) + keyZoneBitWrong.Flags = key.Flags &^ ZONE + if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil { + t.Error("failure to sign the record:", err) + } else if err := sig.Verify(keyZoneBitWrong, []RR{normalSoa}); err == nil { + t.Error("should not validate: ", normalSoa) + } else { + t.Logf("expected failure: %v for key flags %d", err, keyZoneBitWrong.Flags) + } +} + func Test65534(t *testing.T) { t6 := new(RFC3597) t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0} diff --git a/sig0.go b/sig0.go index 2c4b10352..057bb5787 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 From fb0c220a7f9050cdfd3e04f79babd61d82e1d30f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:34:03 +0100 Subject: [PATCH 4/6] Bump golang.org/x/net from 0.28.0 to 0.31.0 (#1622) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.28.0 to 0.31.0. - [Commits](https://github.com/golang/net/compare/v0.28.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e339698a3..c43d1ddb6 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/miekg/dns go 1.19 require ( - golang.org/x/net v0.28.0 + golang.org/x/net v0.31.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.23.0 + golang.org/x/sys v0.27.0 golang.org/x/tools v0.22.0 ) diff --git a/go.sum b/go.sum index eb350ad14..6b5c4724c 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ 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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= 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.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.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= From c705e5aa58725bec0e408a4df2554f220e076d32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:34:30 +0100 Subject: [PATCH 5/6] Bump golang.org/x/sys from 0.23.0 to 0.27.0 (#1619) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.23.0 to 0.27.0. - [Commits](https://github.com/golang/sys/compare/v0.23.0...v0.27.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 97c29ffb8fbdcc2ee5c9bd842f441a18eab0dc90 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 24 Jan 2025 12:05:25 +0100 Subject: [PATCH 6/6] Release 1.1.63 --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 00c8629f2..e290e3dff 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, 63} // v holds the version of this library. type v struct {