github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/integrationtests/self/zero_rtt_test.go (about) 1 //go:build go1.21 2 3 package self_test 4 5 import ( 6 "context" 7 "crypto/tls" 8 "fmt" 9 "io" 10 "net" 11 "sync" 12 "sync/atomic" 13 "time" 14 15 "github.com/metacubex/quic-go" 16 quicproxy "github.com/metacubex/quic-go/integrationtests/tools/proxy" 17 "github.com/metacubex/quic-go/internal/protocol" 18 "github.com/metacubex/quic-go/internal/wire" 19 "github.com/metacubex/quic-go/logging" 20 21 . "github.com/onsi/ginkgo/v2" 22 . "github.com/onsi/gomega" 23 ) 24 25 type metadataClientSessionCache struct { 26 toAdd []byte 27 restored func([]byte) 28 29 cache tls.ClientSessionCache 30 } 31 32 func (m metadataClientSessionCache) Get(key string) (*tls.ClientSessionState, bool) { 33 session, ok := m.cache.Get(key) 34 if !ok || session == nil { 35 return session, ok 36 } 37 ticket, state, err := session.ResumptionState() 38 Expect(err).ToNot(HaveOccurred()) 39 Expect(state.Extra).To(HaveLen(2)) // ours, and the quic-go's 40 m.restored(state.Extra[1]) 41 session, err = tls.NewResumptionState(ticket, state) 42 Expect(err).ToNot(HaveOccurred()) 43 return session, true 44 } 45 46 func (m metadataClientSessionCache) Put(key string, session *tls.ClientSessionState) { 47 ticket, state, err := session.ResumptionState() 48 Expect(err).ToNot(HaveOccurred()) 49 state.Extra = append(state.Extra, m.toAdd) 50 session, err = tls.NewResumptionState(ticket, state) 51 Expect(err).ToNot(HaveOccurred()) 52 m.cache.Put(key, session) 53 } 54 55 // contains0RTTPacket says if a packet contains a 0-RTT long header packet. 56 // It correctly handles coalesced packets. 57 func contains0RTTPacket(data []byte) bool { 58 for len(data) > 0 { 59 if !wire.IsLongHeaderPacket(data[0]) { 60 return false 61 } 62 hdr, _, rest, err := wire.ParsePacket(data) 63 Expect(err).ToNot(HaveOccurred()) 64 if hdr.Type == protocol.PacketType0RTT { 65 return true 66 } 67 data = rest 68 } 69 return false 70 } 71 72 var _ = Describe("0-RTT", func() { 73 rtt := scaleDuration(5 * time.Millisecond) 74 75 runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *atomic.Uint32) { 76 var num0RTTPackets atomic.Uint32 77 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ 78 RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), 79 DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { 80 if contains0RTTPacket(data) { 81 num0RTTPackets.Add(1) 82 } 83 return rtt / 2 84 }, 85 }) 86 Expect(err).ToNot(HaveOccurred()) 87 return proxy, &num0RTTPackets 88 } 89 90 dialAndReceiveSessionTicket := func(serverTLSConf *tls.Config, serverConf *quic.Config, clientTLSConf *tls.Config) { 91 if serverConf == nil { 92 serverConf = getQuicConfig(nil) 93 } 94 serverConf.Allow0RTT = true 95 ln, err := quic.ListenAddrEarly( 96 "localhost:0", 97 serverTLSConf, 98 serverConf, 99 ) 100 Expect(err).ToNot(HaveOccurred()) 101 defer ln.Close() 102 103 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ 104 RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), 105 DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 }, 106 }) 107 Expect(err).ToNot(HaveOccurred()) 108 defer proxy.Close() 109 110 // dial the first connection in order to receive a session ticket 111 done := make(chan struct{}) 112 go func() { 113 defer GinkgoRecover() 114 defer close(done) 115 conn, err := ln.Accept(context.Background()) 116 Expect(err).ToNot(HaveOccurred()) 117 <-conn.Context().Done() 118 }() 119 120 puts := make(chan string, 100) 121 cache := clientTLSConf.ClientSessionCache 122 if cache == nil { 123 cache = tls.NewLRUClientSessionCache(100) 124 } 125 clientTLSConf.ClientSessionCache = newClientSessionCache(cache, make(chan string, 100), puts) 126 conn, err := quic.DialAddr( 127 context.Background(), 128 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 129 clientTLSConf, 130 getQuicConfig(nil), 131 ) 132 Expect(err).ToNot(HaveOccurred()) 133 Eventually(puts).Should(Receive()) 134 // received the session ticket. We're done here. 135 Expect(conn.CloseWithError(0, "")).To(Succeed()) 136 Eventually(done).Should(BeClosed()) 137 } 138 139 transfer0RTTData := func( 140 ln *quic.EarlyListener, 141 proxyPort int, 142 connIDLen int, 143 clientTLSConf *tls.Config, 144 clientConf *quic.Config, 145 testdata []byte, // data to transfer 146 ) { 147 // accept the second connection, and receive the data sent in 0-RTT 148 done := make(chan struct{}) 149 go func() { 150 defer GinkgoRecover() 151 conn, err := ln.Accept(context.Background()) 152 Expect(err).ToNot(HaveOccurred()) 153 str, err := conn.AcceptStream(context.Background()) 154 Expect(err).ToNot(HaveOccurred()) 155 data, err := io.ReadAll(str) 156 Expect(err).ToNot(HaveOccurred()) 157 Expect(data).To(Equal(testdata)) 158 Expect(str.Close()).To(Succeed()) 159 Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) 160 <-conn.Context().Done() 161 close(done) 162 }() 163 164 if clientConf == nil { 165 clientConf = getQuicConfig(nil) 166 } 167 var conn quic.EarlyConnection 168 if connIDLen == 0 { 169 var err error 170 conn, err = quic.DialAddrEarly( 171 context.Background(), 172 fmt.Sprintf("localhost:%d", proxyPort), 173 clientTLSConf, 174 clientConf, 175 ) 176 Expect(err).ToNot(HaveOccurred()) 177 } else { 178 addr, err := net.ResolveUDPAddr("udp", "localhost:0") 179 Expect(err).ToNot(HaveOccurred()) 180 udpConn, err := net.ListenUDP("udp", addr) 181 Expect(err).ToNot(HaveOccurred()) 182 defer udpConn.Close() 183 tr := &quic.Transport{ 184 Conn: udpConn, 185 ConnectionIDLength: connIDLen, 186 } 187 addTracer(tr) 188 defer tr.Close() 189 conn, err = tr.DialEarly( 190 context.Background(), 191 &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxyPort}, 192 clientTLSConf, 193 clientConf, 194 ) 195 Expect(err).ToNot(HaveOccurred()) 196 } 197 defer conn.CloseWithError(0, "") 198 str, err := conn.OpenStream() 199 Expect(err).ToNot(HaveOccurred()) 200 _, err = str.Write(testdata) 201 Expect(err).ToNot(HaveOccurred()) 202 Expect(str.Close()).To(Succeed()) 203 <-conn.HandshakeComplete() 204 Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) 205 io.ReadAll(str) // wait for the EOF from the server to arrive before closing the conn 206 conn.CloseWithError(0, "") 207 Eventually(done).Should(BeClosed()) 208 Eventually(conn.Context().Done()).Should(BeClosed()) 209 } 210 211 check0RTTRejected := func( 212 ln *quic.EarlyListener, 213 proxyPort int, 214 clientConf *tls.Config, 215 ) { 216 conn, err := quic.DialAddrEarly( 217 context.Background(), 218 fmt.Sprintf("localhost:%d", proxyPort), 219 clientConf, 220 getQuicConfig(nil), 221 ) 222 Expect(err).ToNot(HaveOccurred()) 223 str, err := conn.OpenUniStream() 224 Expect(err).ToNot(HaveOccurred()) 225 _, err = str.Write(make([]byte, 3000)) 226 Expect(err).ToNot(HaveOccurred()) 227 Expect(str.Close()).To(Succeed()) 228 Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) 229 230 // make sure the server doesn't process the data 231 ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(50*time.Millisecond)) 232 defer cancel() 233 serverConn, err := ln.Accept(ctx) 234 Expect(err).ToNot(HaveOccurred()) 235 Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse()) 236 _, err = serverConn.AcceptUniStream(ctx) 237 Expect(err).To(Equal(context.DeadlineExceeded)) 238 Expect(serverConn.CloseWithError(0, "")).To(Succeed()) 239 Eventually(conn.Context().Done()).Should(BeClosed()) 240 } 241 242 // can be used to extract 0-RTT from a packetCounter 243 get0RTTPackets := func(packets []packet) []protocol.PacketNumber { 244 var zeroRTTPackets []protocol.PacketNumber 245 for _, p := range packets { 246 if p.hdr.Type == protocol.PacketType0RTT { 247 zeroRTTPackets = append(zeroRTTPackets, p.hdr.PacketNumber) 248 } 249 } 250 return zeroRTTPackets 251 } 252 253 for _, l := range []int{0, 15} { 254 connIDLen := l 255 256 It(fmt.Sprintf("transfers 0-RTT data, with %d byte connection IDs", connIDLen), func() { 257 tlsConf := getTLSConfig() 258 clientTLSConf := getTLSClientConfig() 259 dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf) 260 261 counter, tracer := newPacketTracer() 262 ln, err := quic.ListenAddrEarly( 263 "localhost:0", 264 tlsConf, 265 getQuicConfig(&quic.Config{ 266 Allow0RTT: true, 267 Tracer: newTracer(tracer), 268 }), 269 ) 270 Expect(err).ToNot(HaveOccurred()) 271 defer ln.Close() 272 273 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 274 defer proxy.Close() 275 276 transfer0RTTData( 277 ln, 278 proxy.LocalPort(), 279 connIDLen, 280 clientTLSConf, 281 getQuicConfig(nil), 282 PRData, 283 ) 284 285 var numNewConnIDs int 286 for _, p := range counter.getRcvdLongHeaderPackets() { 287 for _, f := range p.frames { 288 if _, ok := f.(*logging.NewConnectionIDFrame); ok { 289 numNewConnIDs++ 290 } 291 } 292 } 293 if connIDLen == 0 { 294 Expect(numNewConnIDs).To(BeZero()) 295 } else { 296 Expect(numNewConnIDs).ToNot(BeZero()) 297 } 298 299 num0RTT := num0RTTPackets.Load() 300 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 301 Expect(num0RTT).ToNot(BeZero()) 302 zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) 303 Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10)) 304 Expect(zeroRTTPackets).To(ContainElement(protocol.PacketNumber(0))) 305 }) 306 } 307 308 // Test that data intended to be sent with 1-RTT protection is not sent in 0-RTT packets. 309 It("waits for a connection until the handshake is done", func() { 310 tlsConf := getTLSConfig() 311 clientConf := getTLSClientConfig() 312 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 313 314 zeroRTTData := GeneratePRData(5 << 10) 315 oneRTTData := PRData 316 317 counter, tracer := newPacketTracer() 318 ln, err := quic.ListenAddrEarly( 319 "localhost:0", 320 tlsConf, 321 getQuicConfig(&quic.Config{ 322 Allow0RTT: true, 323 Tracer: newTracer(tracer), 324 }), 325 ) 326 Expect(err).ToNot(HaveOccurred()) 327 defer ln.Close() 328 329 // now accept the second connection, and receive the 0-RTT data 330 go func() { 331 defer GinkgoRecover() 332 conn, err := ln.Accept(context.Background()) 333 Expect(err).ToNot(HaveOccurred()) 334 str, err := conn.AcceptUniStream(context.Background()) 335 Expect(err).ToNot(HaveOccurred()) 336 data, err := io.ReadAll(str) 337 Expect(err).ToNot(HaveOccurred()) 338 Expect(data).To(Equal(zeroRTTData)) 339 str, err = conn.AcceptUniStream(context.Background()) 340 Expect(err).ToNot(HaveOccurred()) 341 data, err = io.ReadAll(str) 342 Expect(err).ToNot(HaveOccurred()) 343 Expect(data).To(Equal(oneRTTData)) 344 Expect(conn.CloseWithError(0, "")).To(Succeed()) 345 }() 346 347 proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 348 defer proxy.Close() 349 350 conn, err := quic.DialAddrEarly( 351 context.Background(), 352 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 353 clientConf, 354 getQuicConfig(nil), 355 ) 356 Expect(err).ToNot(HaveOccurred()) 357 firstStr, err := conn.OpenUniStream() 358 Expect(err).ToNot(HaveOccurred()) 359 _, err = firstStr.Write(zeroRTTData) 360 Expect(err).ToNot(HaveOccurred()) 361 Expect(firstStr.Close()).To(Succeed()) 362 363 // wait for the handshake to complete 364 Eventually(conn.HandshakeComplete()).Should(BeClosed()) 365 str, err := conn.OpenUniStream() 366 Expect(err).ToNot(HaveOccurred()) 367 _, err = str.Write(PRData) 368 Expect(err).ToNot(HaveOccurred()) 369 Expect(str.Close()).To(Succeed()) 370 <-conn.Context().Done() 371 372 // check that 0-RTT packets only contain STREAM frames for the first stream 373 var num0RTT int 374 for _, p := range counter.getRcvdLongHeaderPackets() { 375 if p.hdr.Header.Type != protocol.PacketType0RTT { 376 continue 377 } 378 for _, f := range p.frames { 379 sf, ok := f.(*logging.StreamFrame) 380 if !ok { 381 continue 382 } 383 num0RTT++ 384 Expect(sf.StreamID).To(Equal(firstStr.StreamID())) 385 } 386 } 387 fmt.Fprintf(GinkgoWriter, "received %d STREAM frames in 0-RTT packets\n", num0RTT) 388 Expect(num0RTT).ToNot(BeZero()) 389 }) 390 391 It("transfers 0-RTT data, when 0-RTT packets are lost", func() { 392 var num0RTTPackets, numDropped atomic.Uint32 393 394 tlsConf := getTLSConfig() 395 clientConf := getTLSClientConfig() 396 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 397 398 counter, tracer := newPacketTracer() 399 ln, err := quic.ListenAddrEarly( 400 "localhost:0", 401 tlsConf, 402 getQuicConfig(&quic.Config{ 403 Allow0RTT: true, 404 Tracer: newTracer(tracer), 405 }), 406 ) 407 Expect(err).ToNot(HaveOccurred()) 408 defer ln.Close() 409 410 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ 411 RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), 412 DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 }, 413 DropPacket: func(_ quicproxy.Direction, data []byte) bool { 414 if !wire.IsLongHeaderPacket(data[0]) { 415 return false 416 } 417 hdr, _, _, err := wire.ParsePacket(data) 418 Expect(err).ToNot(HaveOccurred()) 419 if hdr.Type == protocol.PacketType0RTT { 420 count := num0RTTPackets.Add(1) 421 // drop 25% of the 0-RTT packets 422 drop := count%4 == 0 423 if drop { 424 numDropped.Add(1) 425 } 426 return drop 427 } 428 return false 429 }, 430 }) 431 Expect(err).ToNot(HaveOccurred()) 432 defer proxy.Close() 433 434 transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData) 435 436 num0RTT := num0RTTPackets.Load() 437 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped.Load()) 438 Expect(numDropped.Load()).ToNot(BeZero()) 439 Expect(num0RTT).ToNot(BeZero()) 440 Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).ToNot(BeEmpty()) 441 }) 442 443 It("retransmits all 0-RTT data when the server performs a Retry", func() { 444 var mutex sync.Mutex 445 var firstConnID, secondConnID *protocol.ConnectionID 446 var firstCounter, secondCounter protocol.ByteCount 447 448 tlsConf := getTLSConfig() 449 clientConf := getTLSClientConfig() 450 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 451 452 countZeroRTTBytes := func(data []byte) (n protocol.ByteCount) { 453 for len(data) > 0 { 454 hdr, _, rest, err := wire.ParsePacket(data) 455 if err != nil { 456 return 457 } 458 data = rest 459 if hdr.Type == protocol.PacketType0RTT { 460 n += hdr.Length - 16 /* AEAD tag */ 461 } 462 } 463 return 464 } 465 466 counter, tracer := newPacketTracer() 467 laddr, err := net.ResolveUDPAddr("udp", "localhost:0") 468 Expect(err).ToNot(HaveOccurred()) 469 udpConn, err := net.ListenUDP("udp", laddr) 470 Expect(err).ToNot(HaveOccurred()) 471 defer udpConn.Close() 472 tr := &quic.Transport{ 473 Conn: udpConn, 474 VerifySourceAddress: func(net.Addr) bool { return true }, 475 } 476 addTracer(tr) 477 defer tr.Close() 478 ln, err := tr.ListenEarly( 479 tlsConf, 480 getQuicConfig(&quic.Config{Allow0RTT: true, Tracer: newTracer(tracer)}), 481 ) 482 Expect(err).ToNot(HaveOccurred()) 483 defer ln.Close() 484 485 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ 486 RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), 487 DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration { 488 connID, err := wire.ParseConnectionID(data, 0) 489 Expect(err).ToNot(HaveOccurred()) 490 491 mutex.Lock() 492 defer mutex.Unlock() 493 494 if zeroRTTBytes := countZeroRTTBytes(data); zeroRTTBytes > 0 { 495 if firstConnID == nil { 496 firstConnID = &connID 497 firstCounter += zeroRTTBytes 498 } else if firstConnID != nil && *firstConnID == connID { 499 Expect(secondConnID).To(BeNil()) 500 firstCounter += zeroRTTBytes 501 } else if secondConnID == nil { 502 secondConnID = &connID 503 secondCounter += zeroRTTBytes 504 } else if secondConnID != nil && *secondConnID == connID { 505 secondCounter += zeroRTTBytes 506 } else { 507 Fail("received 3 connection IDs on 0-RTT packets") 508 } 509 } 510 return rtt / 2 511 }, 512 }) 513 Expect(err).ToNot(HaveOccurred()) 514 defer proxy.Close() 515 516 transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, GeneratePRData(5000)) // ~5 packets 517 518 mutex.Lock() 519 defer mutex.Unlock() 520 Expect(firstCounter).To(BeNumerically("~", 5000+100 /* framing overhead */, 100)) // the FIN bit might be sent extra 521 Expect(secondCounter).To(BeNumerically("~", firstCounter, 20)) 522 zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) 523 Expect(len(zeroRTTPackets)).To(BeNumerically(">=", 5)) 524 Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5))) 525 }) 526 527 It("doesn't use 0-RTT when Dial is used for the resumed connection", func() { 528 tlsConf := getTLSConfig() 529 clientConf := getTLSClientConfig() 530 dialAndReceiveSessionTicket(tlsConf, getQuicConfig(nil), clientConf) 531 532 ln, err := quic.ListenAddrEarly( 533 "localhost:0", 534 tlsConf, 535 getQuicConfig(&quic.Config{Allow0RTT: true}), 536 ) 537 Expect(err).ToNot(HaveOccurred()) 538 defer ln.Close() 539 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 540 defer proxy.Close() 541 542 conn, err := quic.DialAddr( 543 context.Background(), 544 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 545 clientConf, 546 getQuicConfig(nil), 547 ) 548 Expect(err).ToNot(HaveOccurred()) 549 defer conn.CloseWithError(0, "") 550 Expect(conn.ConnectionState().TLS.DidResume).To(BeTrue()) 551 Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) 552 Expect(num0RTTPackets.Load()).To(BeZero()) 553 554 serverConn, err := ln.Accept(context.Background()) 555 Expect(err).ToNot(HaveOccurred()) 556 Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue()) 557 Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse()) 558 }) 559 560 It("doesn't reject 0-RTT when the server's transport stream limit increased", func() { 561 const maxStreams = 1 562 tlsConf := getTLSConfig() 563 clientConf := getTLSClientConfig() 564 dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ 565 MaxIncomingUniStreams: maxStreams, 566 }), clientConf) 567 568 ln, err := quic.ListenAddrEarly( 569 "localhost:0", 570 tlsConf, 571 getQuicConfig(&quic.Config{ 572 MaxIncomingUniStreams: maxStreams + 1, 573 Allow0RTT: true, 574 }), 575 ) 576 Expect(err).ToNot(HaveOccurred()) 577 defer ln.Close() 578 proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 579 defer proxy.Close() 580 581 conn, err := quic.DialAddrEarly( 582 context.Background(), 583 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 584 clientConf, 585 getQuicConfig(nil), 586 ) 587 Expect(err).ToNot(HaveOccurred()) 588 str, err := conn.OpenUniStream() 589 Expect(err).ToNot(HaveOccurred()) 590 _, err = str.Write([]byte("foobar")) 591 Expect(err).ToNot(HaveOccurred()) 592 Expect(str.Close()).To(Succeed()) 593 // The client remembers the old limit and refuses to open a new stream. 594 _, err = conn.OpenUniStream() 595 Expect(err).To(HaveOccurred()) 596 Expect(err.Error()).To(ContainSubstring("too many open streams")) 597 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 598 defer cancel() 599 _, err = conn.OpenUniStreamSync(ctx) 600 Expect(err).ToNot(HaveOccurred()) 601 Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) 602 Expect(conn.CloseWithError(0, "")).To(Succeed()) 603 }) 604 605 It("rejects 0-RTT when the server's stream limit decreased", func() { 606 const maxStreams = 42 607 tlsConf := getTLSConfig() 608 clientConf := getTLSClientConfig() 609 dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ 610 MaxIncomingStreams: maxStreams, 611 }), clientConf) 612 613 counter, tracer := newPacketTracer() 614 ln, err := quic.ListenAddrEarly( 615 "localhost:0", 616 tlsConf, 617 getQuicConfig(&quic.Config{ 618 MaxIncomingStreams: maxStreams - 1, 619 Allow0RTT: true, 620 Tracer: newTracer(tracer), 621 }), 622 ) 623 Expect(err).ToNot(HaveOccurred()) 624 defer ln.Close() 625 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 626 defer proxy.Close() 627 628 check0RTTRejected(ln, proxy.LocalPort(), clientConf) 629 630 // The client should send 0-RTT packets, but the server doesn't process them. 631 num0RTT := num0RTTPackets.Load() 632 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 633 Expect(num0RTT).ToNot(BeZero()) 634 Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) 635 }) 636 637 It("rejects 0-RTT when the ALPN changed", func() { 638 tlsConf := getTLSConfig() 639 clientConf := getTLSClientConfig() 640 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 641 642 // switch to different ALPN on the server side 643 tlsConf.NextProtos = []string{"new-alpn"} 644 // Append to the client's ALPN. 645 // crypto/tls will attempt to resume with the ALPN from the original connection 646 clientConf.NextProtos = append(clientConf.NextProtos, "new-alpn") 647 counter, tracer := newPacketTracer() 648 ln, err := quic.ListenAddrEarly( 649 "localhost:0", 650 tlsConf, 651 getQuicConfig(&quic.Config{ 652 Allow0RTT: true, 653 Tracer: newTracer(tracer), 654 }), 655 ) 656 Expect(err).ToNot(HaveOccurred()) 657 defer ln.Close() 658 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 659 defer proxy.Close() 660 661 check0RTTRejected(ln, proxy.LocalPort(), clientConf) 662 663 // The client should send 0-RTT packets, but the server doesn't process them. 664 num0RTT := num0RTTPackets.Load() 665 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 666 Expect(num0RTT).ToNot(BeZero()) 667 Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) 668 }) 669 670 It("rejects 0-RTT when the application doesn't allow it", func() { 671 tlsConf := getTLSConfig() 672 clientConf := getTLSClientConfig() 673 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 674 675 // now close the listener and dial new connection with a different ALPN 676 counter, tracer := newPacketTracer() 677 ln, err := quic.ListenAddrEarly( 678 "localhost:0", 679 tlsConf, 680 getQuicConfig(&quic.Config{ 681 Allow0RTT: false, // application rejects 0-RTT 682 Tracer: newTracer(tracer), 683 }), 684 ) 685 Expect(err).ToNot(HaveOccurred()) 686 defer ln.Close() 687 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 688 defer proxy.Close() 689 690 check0RTTRejected(ln, proxy.LocalPort(), clientConf) 691 692 // The client should send 0-RTT packets, but the server doesn't process them. 693 num0RTT := num0RTTPackets.Load() 694 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 695 Expect(num0RTT).ToNot(BeZero()) 696 Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) 697 }) 698 699 It("doesn't use 0-RTT, if the server didn't enable it", func() { 700 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) 701 Expect(err).ToNot(HaveOccurred()) 702 defer server.Close() 703 704 gets := make(chan string, 100) 705 puts := make(chan string, 100) 706 cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts) 707 tlsConf := getTLSClientConfig() 708 tlsConf.ClientSessionCache = cache 709 conn1, err := quic.DialAddr( 710 context.Background(), 711 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 712 tlsConf, 713 getQuicConfig(nil), 714 ) 715 Expect(err).ToNot(HaveOccurred()) 716 defer conn1.CloseWithError(0, "") 717 var sessionKey string 718 Eventually(puts).Should(Receive(&sessionKey)) 719 Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse()) 720 721 serverConn, err := server.Accept(context.Background()) 722 Expect(err).ToNot(HaveOccurred()) 723 Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) 724 725 conn2, err := quic.DialAddrEarly( 726 context.Background(), 727 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 728 tlsConf, 729 getQuicConfig(nil), 730 ) 731 Expect(err).ToNot(HaveOccurred()) 732 Expect(gets).To(Receive(Equal(sessionKey))) 733 Expect(conn2.ConnectionState().TLS.DidResume).To(BeTrue()) 734 735 serverConn, err = server.Accept(context.Background()) 736 Expect(err).ToNot(HaveOccurred()) 737 Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue()) 738 Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse()) 739 conn2.CloseWithError(0, "") 740 }) 741 742 DescribeTable("flow control limits", 743 func(addFlowControlLimit func(*quic.Config, uint64)) { 744 counter, tracer := newPacketTracer() 745 firstConf := getQuicConfig(&quic.Config{Allow0RTT: true}) 746 addFlowControlLimit(firstConf, 3) 747 tlsConf := getTLSConfig() 748 clientConf := getTLSClientConfig() 749 dialAndReceiveSessionTicket(tlsConf, firstConf, clientConf) 750 751 secondConf := getQuicConfig(&quic.Config{ 752 Allow0RTT: true, 753 Tracer: newTracer(tracer), 754 }) 755 addFlowControlLimit(secondConf, 100) 756 ln, err := quic.ListenAddrEarly( 757 "localhost:0", 758 tlsConf, 759 secondConf, 760 ) 761 Expect(err).ToNot(HaveOccurred()) 762 defer ln.Close() 763 proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 764 defer proxy.Close() 765 766 conn, err := quic.DialAddrEarly( 767 context.Background(), 768 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 769 clientConf, 770 getQuicConfig(nil), 771 ) 772 Expect(err).ToNot(HaveOccurred()) 773 str, err := conn.OpenUniStream() 774 Expect(err).ToNot(HaveOccurred()) 775 written := make(chan struct{}) 776 go func() { 777 defer GinkgoRecover() 778 defer close(written) 779 _, err := str.Write([]byte("foobar")) 780 Expect(err).ToNot(HaveOccurred()) 781 Expect(str.Close()).To(Succeed()) 782 }() 783 784 Eventually(written).Should(BeClosed()) 785 786 serverConn, err := ln.Accept(context.Background()) 787 Expect(err).ToNot(HaveOccurred()) 788 rstr, err := serverConn.AcceptUniStream(context.Background()) 789 Expect(err).ToNot(HaveOccurred()) 790 data, err := io.ReadAll(rstr) 791 Expect(err).ToNot(HaveOccurred()) 792 Expect(data).To(Equal([]byte("foobar"))) 793 Expect(serverConn.ConnectionState().Used0RTT).To(BeTrue()) 794 Expect(serverConn.CloseWithError(0, "")).To(Succeed()) 795 Eventually(conn.Context().Done()).Should(BeClosed()) 796 797 var processedFirst bool 798 for _, p := range counter.getRcvdLongHeaderPackets() { 799 for _, f := range p.frames { 800 if sf, ok := f.(*logging.StreamFrame); ok { 801 if !processedFirst { 802 // The first STREAM should have been sent in a 0-RTT packet. 803 // Due to the flow control limit, the STREAM frame was limit to the first 3 bytes. 804 Expect(p.hdr.Type).To(Equal(protocol.PacketType0RTT)) 805 Expect(sf.Length).To(BeEquivalentTo(3)) 806 processedFirst = true 807 } else { 808 Fail("STREAM was shouldn't have been sent in 0-RTT") 809 } 810 } 811 } 812 } 813 }, 814 Entry("doesn't reject 0-RTT when the server's transport stream flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialStreamReceiveWindow = limit }), 815 Entry("doesn't reject 0-RTT when the server's transport connection flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialConnectionReceiveWindow = limit }), 816 ) 817 818 for _, l := range []int{0, 15} { 819 connIDLen := l 820 821 It(fmt.Sprintf("correctly deals with 0-RTT rejections, for %d byte connection IDs", connIDLen), func() { 822 tlsConf := getTLSConfig() 823 clientConf := getTLSClientConfig() 824 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 825 // now dial new connection with different transport parameters 826 counter, tracer := newPacketTracer() 827 ln, err := quic.ListenAddrEarly( 828 "localhost:0", 829 tlsConf, 830 getQuicConfig(&quic.Config{ 831 MaxIncomingUniStreams: 1, 832 Tracer: newTracer(tracer), 833 }), 834 ) 835 Expect(err).ToNot(HaveOccurred()) 836 defer ln.Close() 837 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 838 defer proxy.Close() 839 840 conn, err := quic.DialAddrEarly( 841 context.Background(), 842 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 843 clientConf, 844 getQuicConfig(nil), 845 ) 846 Expect(err).ToNot(HaveOccurred()) 847 // The client remembers that it was allowed to open 2 uni-directional streams. 848 firstStr, err := conn.OpenUniStream() 849 Expect(err).ToNot(HaveOccurred()) 850 written := make(chan struct{}, 2) 851 go func() { 852 defer GinkgoRecover() 853 defer func() { written <- struct{}{} }() 854 _, err := firstStr.Write([]byte("first flight")) 855 Expect(err).ToNot(HaveOccurred()) 856 }() 857 secondStr, err := conn.OpenUniStream() 858 Expect(err).ToNot(HaveOccurred()) 859 go func() { 860 defer GinkgoRecover() 861 defer func() { written <- struct{}{} }() 862 _, err := secondStr.Write([]byte("first flight")) 863 Expect(err).ToNot(HaveOccurred()) 864 }() 865 866 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 867 defer cancel() 868 _, err = conn.AcceptStream(ctx) 869 Expect(err).To(MatchError(quic.Err0RTTRejected)) 870 Eventually(written).Should(Receive()) 871 Eventually(written).Should(Receive()) 872 _, err = firstStr.Write([]byte("foobar")) 873 Expect(err).To(MatchError(quic.Err0RTTRejected)) 874 _, err = conn.OpenUniStream() 875 Expect(err).To(MatchError(quic.Err0RTTRejected)) 876 877 _, err = conn.AcceptStream(ctx) 878 Expect(err).To(Equal(quic.Err0RTTRejected)) 879 880 newConn := conn.NextConnection() 881 str, err := newConn.OpenUniStream() 882 Expect(err).ToNot(HaveOccurred()) 883 _, err = newConn.OpenUniStream() 884 Expect(err).To(HaveOccurred()) 885 Expect(err.Error()).To(ContainSubstring("too many open streams")) 886 _, err = str.Write([]byte("second flight")) 887 Expect(err).ToNot(HaveOccurred()) 888 Expect(str.Close()).To(Succeed()) 889 Expect(conn.CloseWithError(0, "")).To(Succeed()) 890 891 // The client should send 0-RTT packets, but the server doesn't process them. 892 num0RTT := num0RTTPackets.Load() 893 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 894 Expect(num0RTT).ToNot(BeZero()) 895 Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) 896 }) 897 } 898 899 It("queues 0-RTT packets, if the Initial is delayed", func() { 900 tlsConf := getTLSConfig() 901 clientConf := getTLSClientConfig() 902 dialAndReceiveSessionTicket(tlsConf, nil, clientConf) 903 904 counter, tracer := newPacketTracer() 905 ln, err := quic.ListenAddrEarly( 906 "localhost:0", 907 tlsConf, 908 getQuicConfig(&quic.Config{ 909 Allow0RTT: true, 910 Tracer: newTracer(tracer), 911 }), 912 ) 913 Expect(err).ToNot(HaveOccurred()) 914 defer ln.Close() 915 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ 916 RemoteAddr: ln.Addr().String(), 917 DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration { 918 if dir == quicproxy.DirectionIncoming && wire.IsLongHeaderPacket(data[0]) && data[0]&0x30>>4 == 0 { // Initial packet from client 919 return rtt/2 + rtt 920 } 921 return rtt / 2 922 }, 923 }) 924 Expect(err).ToNot(HaveOccurred()) 925 defer proxy.Close() 926 927 transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData) 928 929 Expect(counter.getRcvdLongHeaderPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial)) 930 zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) 931 Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10)) 932 Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0))) 933 }) 934 935 It("allows the application to attach data to the session ticket, for the server", func() { 936 tlsConf := getTLSConfig() 937 tlsConf.WrapSession = func(cs tls.ConnectionState, ss *tls.SessionState) ([]byte, error) { 938 ss.Extra = append(ss.Extra, []byte("foobar")) 939 return tlsConf.EncryptTicket(cs, ss) 940 } 941 var unwrapped bool 942 tlsConf.UnwrapSession = func(identity []byte, cs tls.ConnectionState) (*tls.SessionState, error) { 943 defer GinkgoRecover() 944 state, err := tlsConf.DecryptTicket(identity, cs) 945 if err != nil { 946 return nil, err 947 } 948 Expect(state.Extra).To(HaveLen(2)) 949 Expect(state.Extra[1]).To(Equal([]byte("foobar"))) 950 unwrapped = true 951 return state, nil 952 } 953 clientTLSConf := getTLSClientConfig() 954 dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf) 955 956 ln, err := quic.ListenAddrEarly( 957 "localhost:0", 958 tlsConf, 959 getQuicConfig(&quic.Config{Allow0RTT: true}), 960 ) 961 Expect(err).ToNot(HaveOccurred()) 962 defer ln.Close() 963 964 transfer0RTTData( 965 ln, 966 ln.Addr().(*net.UDPAddr).Port, 967 10, 968 clientTLSConf, 969 getQuicConfig(nil), 970 PRData, 971 ) 972 Expect(unwrapped).To(BeTrue()) 973 }) 974 975 It("allows the application to attach data to the session ticket, for the client", func() { 976 tlsConf := getTLSConfig() 977 clientTLSConf := getTLSClientConfig() 978 var restored bool 979 clientTLSConf.ClientSessionCache = &metadataClientSessionCache{ 980 toAdd: []byte("foobar"), 981 restored: func(b []byte) { 982 defer GinkgoRecover() 983 Expect(b).To(Equal([]byte("foobar"))) 984 restored = true 985 }, 986 cache: tls.NewLRUClientSessionCache(100), 987 } 988 dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf) 989 990 ln, err := quic.ListenAddrEarly( 991 "localhost:0", 992 tlsConf, 993 getQuicConfig(&quic.Config{Allow0RTT: true}), 994 ) 995 Expect(err).ToNot(HaveOccurred()) 996 defer ln.Close() 997 998 transfer0RTTData( 999 ln, 1000 ln.Addr().(*net.UDPAddr).Port, 1001 10, 1002 clientTLSConf, 1003 getQuicConfig(nil), 1004 PRData, 1005 ) 1006 Expect(restored).To(BeTrue()) 1007 }) 1008 1009 It("sends 0-RTT datagrams", func() { 1010 tlsConf := getTLSConfig() 1011 clientTLSConf := getTLSClientConfig() 1012 dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ 1013 EnableDatagrams: true, 1014 }), clientTLSConf) 1015 1016 counter, tracer := newPacketTracer() 1017 ln, err := quic.ListenAddrEarly( 1018 "localhost:0", 1019 tlsConf, 1020 getQuicConfig(&quic.Config{ 1021 Allow0RTT: true, 1022 EnableDatagrams: true, 1023 Tracer: newTracer(tracer), 1024 }), 1025 ) 1026 Expect(err).ToNot(HaveOccurred()) 1027 defer ln.Close() 1028 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 1029 defer proxy.Close() 1030 1031 // second connection 1032 sentMessage := GeneratePRData(100) 1033 var receivedMessage []byte 1034 received := make(chan struct{}) 1035 go func() { 1036 defer GinkgoRecover() 1037 defer close(received) 1038 conn, err := ln.Accept(context.Background()) 1039 Expect(err).ToNot(HaveOccurred()) 1040 receivedMessage, err = conn.ReceiveDatagram(context.Background()) 1041 Expect(err).ToNot(HaveOccurred()) 1042 Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) 1043 }() 1044 conn, err := quic.DialAddrEarly( 1045 context.Background(), 1046 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 1047 clientTLSConf, 1048 getQuicConfig(&quic.Config{ 1049 EnableDatagrams: true, 1050 }), 1051 ) 1052 Expect(err).ToNot(HaveOccurred()) 1053 Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue()) 1054 Expect(conn.SendDatagram(sentMessage)).To(Succeed()) 1055 <-conn.HandshakeComplete() 1056 <-received 1057 1058 Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) 1059 Expect(conn.CloseWithError(0, "")).To(Succeed()) 1060 Expect(receivedMessage).To(Equal(sentMessage)) 1061 num0RTT := num0RTTPackets.Load() 1062 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 1063 Expect(num0RTT).ToNot(BeZero()) 1064 zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) 1065 Expect(zeroRTTPackets).To(HaveLen(1)) 1066 }) 1067 1068 It("rejects 0-RTT datagrams when the server doesn't support datagrams anymore", func() { 1069 tlsConf := getTLSConfig() 1070 clientTLSConf := getTLSClientConfig() 1071 dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ 1072 EnableDatagrams: true, 1073 }), clientTLSConf) 1074 1075 counter, tracer := newPacketTracer() 1076 ln, err := quic.ListenAddrEarly( 1077 "localhost:0", 1078 tlsConf, 1079 getQuicConfig(&quic.Config{ 1080 Allow0RTT: true, 1081 EnableDatagrams: false, 1082 Tracer: newTracer(tracer), 1083 }), 1084 ) 1085 Expect(err).ToNot(HaveOccurred()) 1086 defer ln.Close() 1087 1088 proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) 1089 defer proxy.Close() 1090 1091 // second connection 1092 go func() { 1093 defer GinkgoRecover() 1094 conn, err := ln.Accept(context.Background()) 1095 Expect(err).ToNot(HaveOccurred()) 1096 _, err = conn.ReceiveDatagram(context.Background()) 1097 Expect(err.Error()).To(Equal("datagram support disabled")) 1098 <-conn.HandshakeComplete() 1099 Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) 1100 }() 1101 conn, err := quic.DialAddrEarly( 1102 context.Background(), 1103 fmt.Sprintf("localhost:%d", proxy.LocalPort()), 1104 clientTLSConf, 1105 getQuicConfig(&quic.Config{ 1106 EnableDatagrams: true, 1107 }), 1108 ) 1109 Expect(err).ToNot(HaveOccurred()) 1110 // the client can temporarily send datagrams but the server doesn't process them. 1111 Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue()) 1112 Expect(conn.SendDatagram(make([]byte, 100))).To(Succeed()) 1113 <-conn.HandshakeComplete() 1114 1115 Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse()) 1116 Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) 1117 Expect(conn.CloseWithError(0, "")).To(Succeed()) 1118 num0RTT := num0RTTPackets.Load() 1119 fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) 1120 Expect(num0RTT).ToNot(BeZero()) 1121 Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) 1122 }) 1123 })