Files
phishingclub/backend/vendor/github.com/gaukas/clienthellod/quic_frame.go
T
Ronni Skansing 07c8adaf76 update vendor deps
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2025-11-06 23:31:08 +01:00

179 lines
4.1 KiB
Go

package clienthellod
import (
"fmt"
"io"
"sort"
"github.com/gaukas/clienthellod/internal/utils"
)
const (
QUICFrame_PADDING uint64 = 0 // 0
QUICFrame_PING uint64 = 1 // 1
QUICFrame_CRYPTO uint64 = 6 // 6
)
type Frame interface {
// FrameType returns the type of the frame.
FrameType() uint64
// ReadReader takes a Reader and reads the rest of the frame from it,
// starting from the first byte after the frame type.
//
// The returned io.Reader contains the rest of the frame, it could be
// the input Reader itself (if no extra bytes are read) or a rewinded
// Reader (if extra bytes are read and rewinding is needed).
ReadReader(io.Reader) (io.Reader, error)
}
func ReadAllFrames(r io.Reader) ([]Frame, error) {
var frames []Frame = make([]Frame, 0)
for {
// Frame Type
frameType, _, err := ReadNextVLI(r)
if err != nil {
if err == io.EOF {
return frames, nil
}
return nil, err
}
// Frame
var frame Frame
switch frameType {
case QUICFrame_PADDING:
frame = &PADDING{}
case QUICFrame_PING:
frame = &PING{}
case QUICFrame_CRYPTO:
frame = &CRYPTO{}
default:
return nil, fmt.Errorf("unknown frame type: 0x%.2x", frameType)
}
// Read the rest of the frame
r, err = frame.ReadReader(r)
if err != nil {
return nil, err
}
// Append the frame
frames = append(frames, frame)
}
}
func ReassembleCRYPTOFrames(frames []Frame) ([]byte, error) {
var cryptoFrames []Frame = make([]Frame, 0)
// Collect all CRYPTO frames
for _, frame := range frames {
if frame.FrameType() == QUICFrame_CRYPTO {
cryptoFrames = append(cryptoFrames, frame)
}
}
if len(cryptoFrames) == 0 {
return nil, nil // no CRYPTO frames is not an error
}
// Sort CRYPTO frames by offset
sort.Slice(cryptoFrames, func(i, j int) bool {
return cryptoFrames[i].(*CRYPTO).Offset < cryptoFrames[j].(*CRYPTO).Offset
})
// Reassemble CRYPTO frames
var reassembled []byte = make([]byte, 0)
for _, frame := range cryptoFrames {
if uint64(len(reassembled)) == frame.(*CRYPTO).Offset {
reassembled = append(reassembled, frame.(*CRYPTO).Data...)
} else {
return nil, fmt.Errorf("failed to reassemble CRYPTO frames")
}
}
return reassembled, nil
}
// PADDING frame
type PADDING struct {
Length uint64 `json:"length,omitempty"` // count 0x00 bytes until not 0x00
}
// FrameType implements Frame interface.
func (*PADDING) FrameType() uint64 {
return QUICFrame_PADDING
}
// ReadFrom implements Frame interface. It keeps reading until it finds a
// non-zero byte, then the non-zero byte is rewinded back to the reader and
// the reader is returned.
func (f *PADDING) ReadReader(r io.Reader) (rr io.Reader, err error) {
f.Length = 1 // starting from 1, since type is already read
var b []byte = make([]byte, 1)
for {
_, err = r.Read(b)
if err != nil {
if err == io.EOF {
return r, nil // EOF is not an error, it just means all frames are read
}
return r, err
}
if b[0] != 0x00 {
// rewind the reader
rr = utils.RewindReader(r, b)
return
}
f.Length++
}
}
// PING frame
type PING struct{}
// FrameType implements Frame interface.
func (*PING) FrameType() uint64 {
return QUICFrame_PING
}
// ReadFrom implements Frame interface. It does nothing and returns the
// input reader.
func (*PING) ReadReader(r io.Reader) (rr io.Reader, err error) {
return r, nil
}
// CRYPTO frame
type CRYPTO struct {
Offset uint64 `json:"offset,omitempty"` // offset of crypto data, from VLI
Length uint64 `json:"length,omitempty"` // length of crypto data, from VLI
Data []byte `json:"data,omitempty"` // crypto data
}
// FrameType implements Frame interface.
func (*CRYPTO) FrameType() uint64 {
return QUICFrame_CRYPTO
}
// ReadFrom implements Frame interface. It reads the offset, length and
// crypto data from the input reader.
func (f *CRYPTO) ReadReader(r io.Reader) (rr io.Reader, err error) {
// Offset
f.Offset, _, err = ReadNextVLI(r)
if err != nil {
return r, err
}
// Length
f.Length, _, err = ReadNextVLI(r)
if err != nil {
return r, err
}
// Crypto Data
f.Data = make([]byte, f.Length)
_, err = r.Read(f.Data)
return r, err
}