diff --git a/conn_id_generator.go b/conn_id_generator.go index ff961508..80e7ef2e 100644 --- a/conn_id_generator.go +++ b/conn_id_generator.go @@ -76,7 +76,7 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect if !ok { return nil } - if connID.Equal(sentWithDestConnID) { + if connID.Equal(sentWithDestConnID) && !RetireBugBackwardsCompatibilityMode { return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID)) } m.retireConnectionID(connID) @@ -89,6 +89,9 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect } func (m *connIDGenerator) issueNewConnID() error { + if RetireBugBackwardsCompatibilityMode { + return nil + } connID, err := protocol.GenerateConnectionID(m.connIDLen) if err != nil { return err diff --git a/conn_id_generator_test.go b/conn_id_generator_test.go index fc36c766..46a50084 100644 --- a/conn_id_generator_test.go +++ b/conn_id_generator_test.go @@ -62,6 +62,15 @@ var _ = Describe("Connection ID Generator", func() { } }) + It("doesn't issue new connection IDs in RetireBugBackwardsCompatibilityMode", func() { + RetireBugBackwardsCompatibilityMode = true + defer func() { RetireBugBackwardsCompatibilityMode = false }() + + Expect(g.SetMaxActiveConnIDs(4)).To(Succeed()) + Expect(retiredConnIDs).To(BeEmpty()) + Expect(addedConnIDs).To(BeEmpty()) + }) + It("limits the number of connection IDs that it issues", func() { Expect(g.SetMaxActiveConnIDs(9999999)).To(Succeed()) Expect(retiredConnIDs).To(BeEmpty()) @@ -81,6 +90,18 @@ var _ = Describe("Connection ID Generator", func() { Expect(g.Retire(f.SequenceNumber, f.ConnectionID)).To(MatchError(fmt.Sprintf("PROTOCOL_VIOLATION: tried to retire connection ID %d (%s), which was used as the Destination Connection ID on this packet", f.SequenceNumber, f.ConnectionID))) }) + It("doesn't error if the peers tries to retire a connection ID in a packet with that connection ID in RetireBugBackwardsCompatibilityMode", func() { + Expect(g.SetMaxActiveConnIDs(4)).To(Succeed()) + Expect(queuedFrames).ToNot(BeEmpty()) + Expect(queuedFrames[0]).To(BeAssignableToTypeOf(&wire.NewConnectionIDFrame{})) + + RetireBugBackwardsCompatibilityMode = true + defer func() { RetireBugBackwardsCompatibilityMode = false }() + + f := queuedFrames[0].(*wire.NewConnectionIDFrame) + Expect(g.Retire(f.SequenceNumber, f.ConnectionID)).To(Succeed()) + }) + It("issues new connection IDs, when old ones are retired", func() { Expect(g.SetMaxActiveConnIDs(5)).To(Succeed()) queuedFrames = nil diff --git a/interface.go b/interface.go index 1abd89ec..d1034f72 100644 --- a/interface.go +++ b/interface.go @@ -13,6 +13,16 @@ import ( "github.com/lucas-clemente/quic-go/quictrace" ) +// RetireBugBackwardsCompatibilityMode controls a backwards compatibility mode, necessary due to a bug in +// quic-go v0.17.2 (and earlier), where under certain circumstances, an endpoint would retire the connection +// ID it is currently using. See https://github.com/lucas-clemente/quic-go/issues/2658. +// The bug has now been fixed, and new deployments have nothing to worry about. +// Deployments that already have quic-go <= v0.17.2 deployed should active RetireBugBackwardsCompatibilityMode. +// If activated, quic-go will take steps to avoid the bug from triggering when connected to endpoints that are still +// running quic-go <= v0.17.2. +// This flag will be removed in a future version of quic-go. +var RetireBugBackwardsCompatibilityMode bool + // The StreamID is the ID of a QUIC stream. type StreamID = protocol.StreamID