github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/crypto_stream.go (about) 1 package quic 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/apernet/quic-go/internal/protocol" 8 "github.com/apernet/quic-go/internal/qerr" 9 "github.com/apernet/quic-go/internal/wire" 10 ) 11 12 type cryptoStream interface { 13 // for receiving data 14 HandleCryptoFrame(*wire.CryptoFrame) error 15 GetCryptoData() []byte 16 Finish() error 17 // for sending data 18 io.Writer 19 HasData() bool 20 PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame 21 } 22 23 type cryptoStreamImpl struct { 24 queue *frameSorter 25 msgBuf []byte 26 27 highestOffset protocol.ByteCount 28 finished bool 29 30 writeOffset protocol.ByteCount 31 writeBuf []byte 32 } 33 34 func newCryptoStream() cryptoStream { 35 return &cryptoStreamImpl{queue: newFrameSorter()} 36 } 37 38 func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error { 39 highestOffset := f.Offset + protocol.ByteCount(len(f.Data)) 40 if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset { 41 return &qerr.TransportError{ 42 ErrorCode: qerr.CryptoBufferExceeded, 43 ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset), 44 } 45 } 46 if s.finished { 47 if highestOffset > s.highestOffset { 48 // reject crypto data received after this stream was already finished 49 return &qerr.TransportError{ 50 ErrorCode: qerr.ProtocolViolation, 51 ErrorMessage: "received crypto data after change of encryption level", 52 } 53 } 54 // ignore data with a smaller offset than the highest received 55 // could e.g. be a retransmission 56 return nil 57 } 58 s.highestOffset = max(s.highestOffset, highestOffset) 59 if err := s.queue.Push(f.Data, f.Offset, nil); err != nil { 60 return err 61 } 62 for { 63 _, data, _ := s.queue.Pop() 64 if data == nil { 65 return nil 66 } 67 s.msgBuf = append(s.msgBuf, data...) 68 } 69 } 70 71 // GetCryptoData retrieves data that was received in CRYPTO frames 72 func (s *cryptoStreamImpl) GetCryptoData() []byte { 73 b := s.msgBuf 74 s.msgBuf = nil 75 return b 76 } 77 78 func (s *cryptoStreamImpl) Finish() error { 79 if s.queue.HasMoreData() { 80 return &qerr.TransportError{ 81 ErrorCode: qerr.ProtocolViolation, 82 ErrorMessage: "encryption level changed, but crypto stream has more data to read", 83 } 84 } 85 s.finished = true 86 return nil 87 } 88 89 // Writes writes data that should be sent out in CRYPTO frames 90 func (s *cryptoStreamImpl) Write(p []byte) (int, error) { 91 s.writeBuf = append(s.writeBuf, p...) 92 return len(p), nil 93 } 94 95 func (s *cryptoStreamImpl) HasData() bool { 96 return len(s.writeBuf) > 0 97 } 98 99 func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { 100 f := &wire.CryptoFrame{Offset: s.writeOffset} 101 n := min(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf))) 102 f.Data = s.writeBuf[:n] 103 s.writeBuf = s.writeBuf[n:] 104 s.writeOffset += n 105 return f 106 }