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