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