golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/conn_id_test.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"net/netip"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  func TestConnIDClientHandshake(t *testing.T) {
    19  	tc := newTestConn(t, clientSide)
    20  	// On initialization, the client chooses local and remote IDs.
    21  	//
    22  	// The order in which we allocate the two isn't actually important,
    23  	// but test is a lot simpler if we assume.
    24  	if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
    25  		t.Errorf("after initialization: srcConnID = %x, want %x", got, want)
    26  	}
    27  	dstConnID, _ := tc.conn.connIDState.dstConnID()
    28  	if got, want := dstConnID, testLocalConnID(-1); !bytes.Equal(got, want) {
    29  		t.Errorf("after initialization: dstConnID = %x, want %x", got, want)
    30  	}
    31  
    32  	// The server's first Initial packet provides the client with a
    33  	// non-transient remote connection ID.
    34  	tc.writeFrames(packetTypeInitial,
    35  		debugFrameCrypto{
    36  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
    37  		})
    38  	dstConnID, _ = tc.conn.connIDState.dstConnID()
    39  	if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
    40  		t.Errorf("after receiving Initial: dstConnID = %x, want %x", got, want)
    41  	}
    42  
    43  	wantLocal := []connID{{
    44  		cid: testLocalConnID(0),
    45  		seq: 0,
    46  	}}
    47  	if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
    48  		t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
    49  	}
    50  	wantRemote := []remoteConnID{{
    51  		connID: connID{
    52  			cid: testPeerConnID(0),
    53  			seq: 0,
    54  		},
    55  	}}
    56  	if got := tc.conn.connIDState.remote; !remoteConnIDListEqual(got, wantRemote) {
    57  		t.Errorf("remote ids: %v, want %v", fmtRemoteConnIDList(got), fmtRemoteConnIDList(wantRemote))
    58  	}
    59  }
    60  
    61  func TestConnIDServerHandshake(t *testing.T) {
    62  	tc := newTestConn(t, serverSide)
    63  	// On initialization, the server is provided with the client-chosen
    64  	// transient connection ID, and allocates an ID of its own.
    65  	// The Initial packet sets the remote connection ID.
    66  	tc.writeFrames(packetTypeInitial,
    67  		debugFrameCrypto{
    68  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][:1],
    69  		})
    70  	if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
    71  		t.Errorf("after initClient: srcConnID = %q, want %q", got, want)
    72  	}
    73  	dstConnID, _ := tc.conn.connIDState.dstConnID()
    74  	if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
    75  		t.Errorf("after initClient: dstConnID = %q, want %q", got, want)
    76  	}
    77  
    78  	// The Initial flight of CRYPTO data includes transport parameters,
    79  	// which cause us to allocate another local connection ID.
    80  	tc.writeFrames(packetTypeInitial,
    81  		debugFrameCrypto{
    82  			off:  1,
    83  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][1:],
    84  		})
    85  	wantLocal := []connID{{
    86  		cid: testPeerConnID(-1),
    87  		seq: -1,
    88  	}, {
    89  		cid: testLocalConnID(0),
    90  		seq: 0,
    91  	}, {
    92  		cid: testLocalConnID(1),
    93  		seq: 1,
    94  	}}
    95  	if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
    96  		t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
    97  	}
    98  	wantRemote := []remoteConnID{{
    99  		connID: connID{
   100  			cid: testPeerConnID(0),
   101  			seq: 0,
   102  		},
   103  	}}
   104  	if got := tc.conn.connIDState.remote; !remoteConnIDListEqual(got, wantRemote) {
   105  		t.Errorf("remote ids: %v, want %v", fmtRemoteConnIDList(got), fmtRemoteConnIDList(wantRemote))
   106  	}
   107  
   108  	// The client's first Handshake packet permits the server to discard the
   109  	// transient connection ID.
   110  	tc.writeFrames(packetTypeHandshake,
   111  		debugFrameCrypto{
   112  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
   113  		})
   114  	wantLocal = []connID{{
   115  		cid: testLocalConnID(0),
   116  		seq: 0,
   117  	}, {
   118  		cid: testLocalConnID(1),
   119  		seq: 1,
   120  	}}
   121  	if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
   122  		t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
   123  	}
   124  }
   125  
   126  func connIDListEqual(a, b []connID) bool {
   127  	if len(a) != len(b) {
   128  		return false
   129  	}
   130  	for i := range a {
   131  		if a[i].seq != b[i].seq {
   132  			return false
   133  		}
   134  		if !bytes.Equal(a[i].cid, b[i].cid) {
   135  			return false
   136  		}
   137  	}
   138  	return true
   139  }
   140  
   141  func remoteConnIDListEqual(a, b []remoteConnID) bool {
   142  	if len(a) != len(b) {
   143  		return false
   144  	}
   145  	for i := range a {
   146  		if a[i].seq != b[i].seq {
   147  			return false
   148  		}
   149  		if !bytes.Equal(a[i].cid, b[i].cid) {
   150  			return false
   151  		}
   152  		if a[i].resetToken != b[i].resetToken {
   153  			return false
   154  		}
   155  	}
   156  	return true
   157  }
   158  
   159  func fmtConnIDList(s []connID) string {
   160  	var strs []string
   161  	for _, cid := range s {
   162  		strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x}]", cid.seq, cid.cid))
   163  	}
   164  	return "{" + strings.Join(strs, " ") + "}"
   165  }
   166  
   167  func fmtRemoteConnIDList(s []remoteConnID) string {
   168  	var strs []string
   169  	for _, cid := range s {
   170  		strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x} token:{%x}]", cid.seq, cid.cid, cid.resetToken))
   171  	}
   172  	return "{" + strings.Join(strs, " ") + "}"
   173  }
   174  
   175  func TestNewRandomConnID(t *testing.T) {
   176  	cid, err := newRandomConnID(0)
   177  	if len(cid) != connIDLen || err != nil {
   178  		t.Fatalf("newConnID() = %x, %v; want %v bytes", cid, connIDLen, err)
   179  	}
   180  }
   181  
   182  func TestConnIDPeerRequestsManyIDs(t *testing.T) {
   183  	// "An endpoint SHOULD ensure that its peer has a sufficient number
   184  	// of available and unused connection IDs."
   185  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
   186  	//
   187  	// "An endpoint MAY limit the total number of connection IDs
   188  	// issued for each connection [...]"
   189  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-6
   190  	//
   191  	// Peer requests 100 connection IDs.
   192  	// We give them 4 in total.
   193  	tc := newTestConn(t, serverSide, func(p *transportParameters) {
   194  		p.activeConnIDLimit = 100
   195  	})
   196  	tc.ignoreFrame(frameTypeAck)
   197  	tc.ignoreFrame(frameTypeCrypto)
   198  
   199  	tc.writeFrames(packetTypeInitial,
   200  		debugFrameCrypto{
   201  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
   202  		})
   203  	tc.wantFrame("provide additional connection ID 1",
   204  		packetType1RTT, debugFrameNewConnectionID{
   205  			seq:    1,
   206  			connID: testLocalConnID(1),
   207  			token:  testLocalStatelessResetToken(1),
   208  		})
   209  	tc.wantFrame("provide additional connection ID 2",
   210  		packetType1RTT, debugFrameNewConnectionID{
   211  			seq:    2,
   212  			connID: testLocalConnID(2),
   213  			token:  testLocalStatelessResetToken(2),
   214  		})
   215  	tc.wantFrame("provide additional connection ID 3",
   216  		packetType1RTT, debugFrameNewConnectionID{
   217  			seq:    3,
   218  			connID: testLocalConnID(3),
   219  			token:  testLocalStatelessResetToken(3),
   220  		})
   221  	tc.wantIdle("connection ID limit reached, no more to provide")
   222  }
   223  
   224  func TestConnIDPeerProvidesTooManyIDs(t *testing.T) {
   225  	// "An endpoint MUST NOT provide more connection IDs than the peer's limit."
   226  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
   227  	tc := newTestConn(t, serverSide)
   228  	tc.handshake()
   229  	tc.ignoreFrame(frameTypeAck)
   230  
   231  	tc.writeFrames(packetType1RTT,
   232  		debugFrameNewConnectionID{
   233  			seq:    2,
   234  			connID: testLocalConnID(2),
   235  		})
   236  	tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
   237  		packetType1RTT, debugFrameConnectionCloseTransport{
   238  			code: errConnectionIDLimit,
   239  		})
   240  }
   241  
   242  func TestConnIDPeerTemporarilyExceedsActiveConnIDLimit(t *testing.T) {
   243  	// "An endpoint MAY send connection IDs that temporarily exceed a peer's limit
   244  	// if the NEW_CONNECTION_ID frame also requires the retirement of any excess [...]"
   245  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
   246  	tc := newTestConn(t, serverSide)
   247  	tc.handshake()
   248  	tc.ignoreFrame(frameTypeAck)
   249  
   250  	tc.writeFrames(packetType1RTT,
   251  		debugFrameNewConnectionID{
   252  			retirePriorTo: 2,
   253  			seq:           2,
   254  			connID:        testPeerConnID(2),
   255  		}, debugFrameNewConnectionID{
   256  			retirePriorTo: 2,
   257  			seq:           3,
   258  			connID:        testPeerConnID(3),
   259  		})
   260  	tc.wantFrame("peer requested we retire conn id 0",
   261  		packetType1RTT, debugFrameRetireConnectionID{
   262  			seq: 0,
   263  		})
   264  	tc.wantFrame("peer requested we retire conn id 1",
   265  		packetType1RTT, debugFrameRetireConnectionID{
   266  			seq: 1,
   267  		})
   268  }
   269  
   270  func TestConnIDPeerRetiresConnID(t *testing.T) {
   271  	// "An endpoint SHOULD supply a new connection ID when the peer retires a connection ID."
   272  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-6
   273  	for _, side := range []connSide{
   274  		clientSide,
   275  		serverSide,
   276  	} {
   277  		t.Run(side.String(), func(t *testing.T) {
   278  			tc := newTestConn(t, side)
   279  			tc.handshake()
   280  			tc.ignoreFrame(frameTypeAck)
   281  
   282  			tc.writeFrames(packetType1RTT,
   283  				debugFrameRetireConnectionID{
   284  					seq: 0,
   285  				})
   286  			tc.wantFrame("provide replacement connection ID",
   287  				packetType1RTT, debugFrameNewConnectionID{
   288  					seq:           2,
   289  					retirePriorTo: 1,
   290  					connID:        testLocalConnID(2),
   291  					token:         testLocalStatelessResetToken(2),
   292  				})
   293  		})
   294  	}
   295  }
   296  
   297  func TestConnIDPeerWithZeroLengthConnIDSendsNewConnectionID(t *testing.T) {
   298  	// "An endpoint that selects a zero-length connection ID during the handshake
   299  	// cannot issue a new connection ID."
   300  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-8
   301  	tc := newTestConn(t, clientSide, func(p *transportParameters) {
   302  		p.initialSrcConnID = []byte{}
   303  	})
   304  	tc.peerConnID = []byte{}
   305  	tc.ignoreFrame(frameTypeAck)
   306  	tc.uncheckedHandshake()
   307  
   308  	tc.writeFrames(packetType1RTT,
   309  		debugFrameNewConnectionID{
   310  			seq:    1,
   311  			connID: testPeerConnID(1),
   312  		})
   313  	tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
   314  		packetType1RTT, debugFrameConnectionCloseTransport{
   315  			code: errProtocolViolation,
   316  		})
   317  }
   318  
   319  func TestConnIDPeerRequestsRetirement(t *testing.T) {
   320  	// "Upon receipt of an increased Retire Prior To field, the peer MUST
   321  	// stop using the corresponding connection IDs and retire them with
   322  	// RETIRE_CONNECTION_ID frames [...]"
   323  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-5
   324  	tc := newTestConn(t, clientSide)
   325  	tc.handshake()
   326  	tc.ignoreFrame(frameTypeAck)
   327  
   328  	tc.writeFrames(packetType1RTT,
   329  		debugFrameNewConnectionID{
   330  			seq:           2,
   331  			retirePriorTo: 1,
   332  			connID:        testPeerConnID(2),
   333  		})
   334  	tc.wantFrame("peer asked for conn id 0 to be retired",
   335  		packetType1RTT, debugFrameRetireConnectionID{
   336  			seq: 0,
   337  		})
   338  	if got, want := tc.lastPacket.dstConnID, testPeerConnID(1); !bytes.Equal(got, want) {
   339  		t.Fatalf("used destination conn id {%x}, want {%x}", got, want)
   340  	}
   341  }
   342  
   343  func TestConnIDPeerDoesNotAcknowledgeRetirement(t *testing.T) {
   344  	// "An endpoint SHOULD limit the number of connection IDs it has retired locally
   345  	// for which RETIRE_CONNECTION_ID frames have not yet been acknowledged."
   346  	// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-6
   347  	tc := newTestConn(t, clientSide)
   348  	tc.handshake()
   349  	tc.ignoreFrame(frameTypeAck)
   350  	tc.ignoreFrame(frameTypeRetireConnectionID)
   351  
   352  	// Send a number of NEW_CONNECTION_ID frames, each retiring an old one.
   353  	for seq := int64(0); seq < 7; seq++ {
   354  		tc.writeFrames(packetType1RTT,
   355  			debugFrameNewConnectionID{
   356  				seq:           seq + 2,
   357  				retirePriorTo: seq + 1,
   358  				connID:        testPeerConnID(seq + 2),
   359  			})
   360  		// We're ignoring the RETIRE_CONNECTION_ID frames.
   361  	}
   362  	tc.wantFrame("number of retired, unacked conn ids is too large",
   363  		packetType1RTT, debugFrameConnectionCloseTransport{
   364  			code: errConnectionIDLimit,
   365  		})
   366  }
   367  
   368  func TestConnIDRepeatedNewConnectionIDFrame(t *testing.T) {
   369  	// "Receipt of the same [NEW_CONNECTION_ID] frame multiple times
   370  	// MUST NOT be treated as a connection error.
   371  	// https://www.rfc-editor.org/rfc/rfc9000#section-19.15-7
   372  	tc := newTestConn(t, clientSide)
   373  	tc.handshake()
   374  	tc.ignoreFrame(frameTypeAck)
   375  
   376  	for i := 0; i < 4; i++ {
   377  		tc.writeFrames(packetType1RTT,
   378  			debugFrameNewConnectionID{
   379  				seq:           2,
   380  				retirePriorTo: 1,
   381  				connID:        testPeerConnID(2),
   382  			})
   383  	}
   384  	tc.wantFrame("peer asked for conn id to be retired",
   385  		packetType1RTT, debugFrameRetireConnectionID{
   386  			seq: 0,
   387  		})
   388  	tc.wantIdle("repeated NEW_CONNECTION_ID frames are not an error")
   389  }
   390  
   391  func TestConnIDForSequenceNumberChanges(t *testing.T) {
   392  	// "[...] if a sequence number is used for different connection IDs,
   393  	// the endpoint MAY treat that receipt as a connection error
   394  	// of type PROTOCOL_VIOLATION."
   395  	// https://www.rfc-editor.org/rfc/rfc9000#section-19.15-8
   396  	tc := newTestConn(t, clientSide)
   397  	tc.handshake()
   398  	tc.ignoreFrame(frameTypeAck)
   399  	tc.ignoreFrame(frameTypeRetireConnectionID)
   400  
   401  	tc.writeFrames(packetType1RTT,
   402  		debugFrameNewConnectionID{
   403  			seq:           2,
   404  			retirePriorTo: 1,
   405  			connID:        testPeerConnID(2),
   406  		})
   407  	tc.writeFrames(packetType1RTT,
   408  		debugFrameNewConnectionID{
   409  			seq:           2,
   410  			retirePriorTo: 1,
   411  			connID:        testPeerConnID(3),
   412  		})
   413  	tc.wantFrame("connection ID for sequence 0 has changed",
   414  		packetType1RTT, debugFrameConnectionCloseTransport{
   415  			code: errProtocolViolation,
   416  		})
   417  }
   418  
   419  func TestConnIDRetirePriorToAfterNewConnID(t *testing.T) {
   420  	// "Receiving a value in the Retire Prior To field that is greater than
   421  	// that in the Sequence Number field MUST be treated as a connection error
   422  	// of type FRAME_ENCODING_ERROR.
   423  	// https://www.rfc-editor.org/rfc/rfc9000#section-19.15-9
   424  	tc := newTestConn(t, serverSide)
   425  	tc.handshake()
   426  	tc.ignoreFrame(frameTypeAck)
   427  
   428  	tc.writeFrames(packetType1RTT,
   429  		debugFrameNewConnectionID{
   430  			retirePriorTo: 3,
   431  			seq:           2,
   432  			connID:        testPeerConnID(2),
   433  		})
   434  	tc.wantFrame("invalid NEW_CONNECTION_ID: retired the new conn id",
   435  		packetType1RTT, debugFrameConnectionCloseTransport{
   436  			code: errFrameEncoding,
   437  		})
   438  }
   439  
   440  func TestConnIDAlreadyRetired(t *testing.T) {
   441  	// "An endpoint that receives a NEW_CONNECTION_ID frame with a
   442  	// sequence number smaller than the Retire Prior To field of a
   443  	// previously received NEW_CONNECTION_ID frame MUST send a
   444  	// corresponding RETIRE_CONNECTION_ID frame [...]"
   445  	// https://www.rfc-editor.org/rfc/rfc9000#section-19.15-11
   446  	tc := newTestConn(t, clientSide)
   447  	tc.handshake()
   448  	tc.ignoreFrame(frameTypeAck)
   449  
   450  	tc.writeFrames(packetType1RTT,
   451  		debugFrameNewConnectionID{
   452  			seq:           4,
   453  			retirePriorTo: 3,
   454  			connID:        testPeerConnID(4),
   455  		})
   456  	tc.wantFrame("peer asked for conn id to be retired",
   457  		packetType1RTT, debugFrameRetireConnectionID{
   458  			seq: 0,
   459  		})
   460  	tc.wantFrame("peer asked for conn id to be retired",
   461  		packetType1RTT, debugFrameRetireConnectionID{
   462  			seq: 1,
   463  		})
   464  	tc.writeFrames(packetType1RTT,
   465  		debugFrameNewConnectionID{
   466  			seq:           2,
   467  			retirePriorTo: 0,
   468  			connID:        testPeerConnID(2),
   469  		})
   470  	tc.wantFrame("NEW_CONNECTION_ID was for an already-retired ID",
   471  		packetType1RTT, debugFrameRetireConnectionID{
   472  			seq: 2,
   473  		})
   474  }
   475  
   476  func TestConnIDRepeatedRetireConnectionIDFrame(t *testing.T) {
   477  	tc := newTestConn(t, clientSide)
   478  	tc.handshake()
   479  	tc.ignoreFrame(frameTypeAck)
   480  
   481  	for i := 0; i < 4; i++ {
   482  		tc.writeFrames(packetType1RTT,
   483  			debugFrameRetireConnectionID{
   484  				seq: 0,
   485  			})
   486  	}
   487  	tc.wantFrame("issue new conn id after peer retires one",
   488  		packetType1RTT, debugFrameNewConnectionID{
   489  			retirePriorTo: 1,
   490  			seq:           2,
   491  			connID:        testLocalConnID(2),
   492  			token:         testLocalStatelessResetToken(2),
   493  		})
   494  	tc.wantIdle("repeated RETIRE_CONNECTION_ID frames are not an error")
   495  }
   496  
   497  func TestConnIDRetiredUnsent(t *testing.T) {
   498  	// "Receipt of a RETIRE_CONNECTION_ID frame containing a sequence number
   499  	// greater than any previously sent to the peer MUST be treated as a
   500  	// connection error of type PROTOCOL_VIOLATION."
   501  	// https://www.rfc-editor.org/rfc/rfc9000#section-19.16-7
   502  	tc := newTestConn(t, clientSide)
   503  	tc.handshake()
   504  	tc.ignoreFrame(frameTypeAck)
   505  
   506  	tc.writeFrames(packetType1RTT,
   507  		debugFrameRetireConnectionID{
   508  			seq: 2,
   509  		})
   510  	tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
   511  		packetType1RTT, debugFrameConnectionCloseTransport{
   512  			code: errProtocolViolation,
   513  		})
   514  }
   515  
   516  func TestConnIDUsePreferredAddressConnID(t *testing.T) {
   517  	// Peer gives us a connection ID in the preferred address transport parameter.
   518  	// We don't use the preferred address at this time, but we should use the
   519  	// connection ID. (It isn't tied to any specific address.)
   520  	//
   521  	// This test will probably need updating if/when we start using the preferred address.
   522  	cid := testPeerConnID(10)
   523  	tc := newTestConn(t, serverSide, func(p *transportParameters) {
   524  		p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
   525  		p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
   526  		p.preferredAddrConnID = cid
   527  		p.preferredAddrResetToken = make([]byte, 16)
   528  	})
   529  	tc.uncheckedHandshake()
   530  	tc.ignoreFrame(frameTypeAck)
   531  
   532  	tc.writeFrames(packetType1RTT,
   533  		debugFrameNewConnectionID{
   534  			seq:           2,
   535  			retirePriorTo: 1,
   536  			connID:        []byte{0xff},
   537  		})
   538  	tc.wantFrame("peer asked for conn id 0 to be retired",
   539  		packetType1RTT, debugFrameRetireConnectionID{
   540  			seq: 0,
   541  		})
   542  	if got, want := tc.lastPacket.dstConnID, cid; !bytes.Equal(got, want) {
   543  		t.Fatalf("used destination conn id {%x}, want {%x} from preferred address transport parameter", got, want)
   544  	}
   545  }
   546  
   547  func TestConnIDPeerProvidesPreferredAddrAndTooManyConnIDs(t *testing.T) {
   548  	// Peer gives us more conn ids than our advertised limit,
   549  	// including a conn id in the preferred address transport parameter.
   550  	cid := testPeerConnID(10)
   551  	tc := newTestConn(t, serverSide, func(p *transportParameters) {
   552  		p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
   553  		p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
   554  		p.preferredAddrConnID = cid
   555  		p.preferredAddrResetToken = make([]byte, 16)
   556  	})
   557  	tc.uncheckedHandshake()
   558  	tc.ignoreFrame(frameTypeAck)
   559  
   560  	tc.writeFrames(packetType1RTT,
   561  		debugFrameNewConnectionID{
   562  			seq:           2,
   563  			retirePriorTo: 0,
   564  			connID:        testPeerConnID(2),
   565  		})
   566  	tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
   567  		packetType1RTT, debugFrameConnectionCloseTransport{
   568  			code: errConnectionIDLimit,
   569  		})
   570  }
   571  
   572  func TestConnIDPeerWithZeroLengthIDProvidesPreferredAddr(t *testing.T) {
   573  	// Peer gives us more conn ids than our advertised limit,
   574  	// including a conn id in the preferred address transport parameter.
   575  	tc := newTestConn(t, serverSide, func(p *transportParameters) {
   576  		p.initialSrcConnID = []byte{}
   577  		p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
   578  		p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
   579  		p.preferredAddrConnID = testPeerConnID(1)
   580  		p.preferredAddrResetToken = make([]byte, 16)
   581  	}, func(cids *newServerConnIDs) {
   582  		cids.srcConnID = []byte{}
   583  	}, func(tc *testConn) {
   584  		tc.peerConnID = []byte{}
   585  	})
   586  
   587  	tc.writeFrames(packetTypeInitial,
   588  		debugFrameCrypto{
   589  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
   590  		})
   591  	tc.wantFrame("peer with zero-length connection ID tried to provide another in transport parameters",
   592  		packetTypeInitial, debugFrameConnectionCloseTransport{
   593  			code: errProtocolViolation,
   594  		})
   595  }
   596  
   597  func TestConnIDInitialSrcConnIDMismatch(t *testing.T) {
   598  	// "Endpoints MUST validate that received [initial_source_connection_id]
   599  	// parameters match received connection ID values."
   600  	// https://www.rfc-editor.org/rfc/rfc9000#section-7.3-3
   601  	testSides(t, "", func(t *testing.T, side connSide) {
   602  		tc := newTestConn(t, side, func(p *transportParameters) {
   603  			p.initialSrcConnID = []byte("invalid")
   604  		})
   605  		tc.ignoreFrame(frameTypeAck)
   606  		tc.ignoreFrame(frameTypeCrypto)
   607  		tc.writeFrames(packetTypeInitial,
   608  			debugFrameCrypto{
   609  				data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
   610  			})
   611  		if side == clientSide {
   612  			// Server transport parameters are carried in the Handshake packet.
   613  			tc.writeFrames(packetTypeHandshake,
   614  				debugFrameCrypto{
   615  					data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
   616  				})
   617  		}
   618  		tc.wantFrame("initial_source_connection_id transport parameter mismatch",
   619  			packetTypeInitial, debugFrameConnectionCloseTransport{
   620  				code: errTransportParameter,
   621  			})
   622  	})
   623  }
   624  
   625  func TestConnIDsCleanedUpAfterClose(t *testing.T) {
   626  	testSides(t, "", func(t *testing.T, side connSide) {
   627  		tc := newTestConn(t, side, func(p *transportParameters) {
   628  			if side == clientSide {
   629  				token := testPeerStatelessResetToken(0)
   630  				p.statelessResetToken = token[:]
   631  			}
   632  		})
   633  		tc.handshake()
   634  		tc.ignoreFrame(frameTypeAck)
   635  		tc.writeFrames(packetType1RTT,
   636  			debugFrameNewConnectionID{
   637  				seq:           2,
   638  				retirePriorTo: 1,
   639  				connID:        testPeerConnID(2),
   640  				token:         testPeerStatelessResetToken(0),
   641  			})
   642  		tc.wantFrame("peer asked for conn id 0 to be retired",
   643  			packetType1RTT, debugFrameRetireConnectionID{
   644  				seq: 0,
   645  			})
   646  		tc.writeFrames(packetType1RTT, debugFrameConnectionCloseTransport{})
   647  		tc.conn.Abort(nil)
   648  		tc.wantFrame("CONN_CLOSE sent after user closes connection",
   649  			packetType1RTT, debugFrameConnectionCloseTransport{})
   650  
   651  		// Wait for the conn to drain.
   652  		// Then wait for the conn loop to exit,
   653  		// and force an immediate sync of the connsMap updates
   654  		// (normally only done by the endpoint read loop).
   655  		tc.advanceToTimer()
   656  		<-tc.conn.donec
   657  		tc.endpoint.e.connsMap.applyUpdates()
   658  
   659  		if got := len(tc.endpoint.e.connsMap.byConnID); got != 0 {
   660  			t.Errorf("%v conn ids in endpoint map after closing, want 0", got)
   661  		}
   662  		if got := len(tc.endpoint.e.connsMap.byResetToken); got != 0 {
   663  			t.Errorf("%v reset tokens in endpoint map after closing, want 0", got)
   664  		}
   665  	})
   666  }