Source file src/vendor/golang.org/x/net/quic/crypto_stream.go
1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package quic 6 7 // "Implementations MUST support buffering at least 4096 bytes of data 8 // received in out-of-order CRYPTO frames." 9 // https://www.rfc-editor.org/rfc/rfc9000.html#section-7.5-2 10 // 11 // 4096 is too small for real-world cases, however, so we allow more. 12 const cryptoBufferSize = 1 << 20 13 14 // A cryptoStream is the stream of data passed in CRYPTO frames. 15 // There is one cryptoStream per packet number space. 16 type cryptoStream struct { 17 // CRYPTO data received from the peer. 18 in pipe 19 inset rangeset[int64] // bytes received 20 21 // CRYPTO data queued for transmission to the peer. 22 out pipe 23 outunsent rangeset[int64] // bytes in need of sending 24 outacked rangeset[int64] // bytes acked by peer 25 } 26 27 // handleCrypto processes data received in a CRYPTO frame. 28 func (s *cryptoStream) handleCrypto(off int64, b []byte, f func([]byte) error) error { 29 end := off + int64(len(b)) 30 if end-s.inset.min() > cryptoBufferSize { 31 return localTransportError{ 32 code: errCryptoBufferExceeded, 33 reason: "crypto buffer exceeded", 34 } 35 } 36 s.inset.add(off, end) 37 if off == s.in.start { 38 // Fast path: This is the next chunk of data in the stream, 39 // so just handle it immediately. 40 if err := f(b); err != nil { 41 return err 42 } 43 s.in.discardBefore(end) 44 } else { 45 // This is either data we've already processed, 46 // data we can't process yet, or a mix of both. 47 s.in.writeAt(b, off) 48 } 49 // s.in.start is the next byte in sequence. 50 // If it's in s.inset, we have bytes to provide. 51 // If it isn't, we don't--we're either out of data, 52 // or only have data that comes after the next byte. 53 if !s.inset.contains(s.in.start) { 54 return nil 55 } 56 // size is the size of the first contiguous chunk of bytes 57 // that have not been processed yet. 58 size := int(s.inset[0].end - s.in.start) 59 if size <= 0 { 60 return nil 61 } 62 err := s.in.read(s.in.start, size, f) 63 s.in.discardBefore(s.inset[0].end) 64 return err 65 } 66 67 // write queues data for sending to the peer. 68 // It does not block or limit the amount of buffered data. 69 // QUIC connections don't communicate the amount of CRYPTO data they are willing to buffer, 70 // so we send what we have and the peer can close the connection if it is too much. 71 func (s *cryptoStream) write(b []byte) { 72 start := s.out.end 73 s.out.writeAt(b, start) 74 s.outunsent.add(start, s.out.end) 75 } 76 77 // ackOrLoss reports that an CRYPTO frame sent by us has been acknowledged by the peer, or lost. 78 func (s *cryptoStream) ackOrLoss(start, end int64, fate packetFate) { 79 switch fate { 80 case packetAcked: 81 s.outacked.add(start, end) 82 s.outunsent.sub(start, end) 83 // If this ack is for data at the start of the send buffer, we can now discard it. 84 if s.outacked.contains(s.out.start) { 85 s.out.discardBefore(s.outacked[0].end) 86 } 87 case packetLost: 88 // Mark everything lost, but not previously acked, as needing retransmission. 89 // We do this by adding all the lost bytes to outunsent, and then 90 // removing everything already acked. 91 s.outunsent.add(start, end) 92 for _, a := range s.outacked { 93 s.outunsent.sub(a.start, a.end) 94 } 95 } 96 } 97 98 // dataToSend reports what data should be sent in CRYPTO frames to the peer. 99 // It calls f with each range of data to send. 100 // f uses sendData to get the bytes to send, and returns the number of bytes sent. 101 // dataToSend calls f until no data is left, or f returns 0. 102 // 103 // This function is unusually indirect (why not just return a []byte, 104 // or implement io.Reader?). 105 // 106 // Returning a []byte to the caller either requires that we store the 107 // data to send contiguously (which we don't), allocate a temporary buffer 108 // and copy into it (inefficient), or return less data than we have available 109 // (requires complexity to avoid unnecessarily breaking data across frames). 110 // 111 // Accepting a []byte from the caller (io.Reader) makes packet construction 112 // difficult. Since CRYPTO data is encoded with a varint length prefix, the 113 // location of the data depends on the length of the data. (We could hardcode 114 // a 2-byte length, of course.) 115 // 116 // Instead, we tell the caller how much data is, the caller figures out where 117 // to put it (and possibly decides that it doesn't have space for this data 118 // in the packet after all), and the caller then makes a separate call to 119 // copy the data it wants into position. 120 func (s *cryptoStream) dataToSend(pto bool, f func(off, size int64) (sent int64)) { 121 for { 122 off, size := dataToSend(s.out.start, s.out.end, s.outunsent, s.outacked, pto) 123 if size == 0 { 124 return 125 } 126 n := f(off, size) 127 if n == 0 || pto { 128 return 129 } 130 } 131 } 132 133 // sendData fills b with data to send to the peer, starting at off, 134 // and marks the data as sent. The caller must have already ascertained 135 // that there is data to send in this region using dataToSend. 136 func (s *cryptoStream) sendData(off int64, b []byte) { 137 s.out.copy(off, b) 138 s.outunsent.sub(off, off+int64(len(b))) 139 } 140 141 // discardKeys is called when the packet protection keys for the stream are dropped. 142 func (s *cryptoStream) discardKeys() error { 143 if s.in.end-s.in.start != 0 { 144 // The peer sent some unprocessed CRYPTO data that we're about to discard. 145 // Close the connection with a TLS unexpected_message alert. 146 // https://www.rfc-editor.org/rfc/rfc5246#section-7.2.2 147 const unexpectedMessage = 10 148 return localTransportError{ 149 code: errTLSBase + unexpectedMessage, 150 reason: "excess crypto data", 151 } 152 } 153 // Discard any unacked (but presumably received) data in our output buffer. 154 s.out.discardBefore(s.out.end) 155 *s = cryptoStream{} 156 return nil 157 } 158