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