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  

View as plain text