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