mirror of
https://github.com/daeuniverse/dae.git
synced 2025-01-07 05:51:20 +07:00
265 lines
6.0 KiB
Go
265 lines
6.0 KiB
Go
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
* Copyright (c) 2022-2024, daeuniverse Organization <dae@v2raya.org>
|
|
*/
|
|
|
|
package quicutils
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"sort"
|
|
)
|
|
|
|
var (
|
|
ErrUnknownFrameType = fmt.Errorf("unknown frame type")
|
|
ErrOutOfRange = fmt.Errorf("index out of range")
|
|
)
|
|
|
|
const (
|
|
Quic_FrameType_Padding = 0
|
|
Quic_FrameType_Ping = 1
|
|
Quic_FrameType_Crypto = 6
|
|
Quic_FrameType_ConnectionClose = 0x1c
|
|
Quic_FrameType_ConnectionClose2 = 0x1d
|
|
)
|
|
|
|
type CryptoFrameOffset struct {
|
|
UpperAppOffset int
|
|
// Offset of data in quic payload.
|
|
Data []byte
|
|
}
|
|
|
|
func ReassembleCryptos(offsets []*CryptoFrameOffset, newPayload []byte) (newOffsets []*CryptoFrameOffset, err error) {
|
|
oldLen := len(offsets)
|
|
var frameSize int
|
|
var offset *CryptoFrameOffset
|
|
var boundary int
|
|
// Extract crypto frames.
|
|
for iNextFrame := 0; iNextFrame < len(newPayload); iNextFrame += frameSize {
|
|
offset, frameSize, err = ExtractCryptoFrameOffset(newPayload[iNextFrame:], iNextFrame)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if offset == nil {
|
|
continue
|
|
}
|
|
offsets = append(offsets, offset)
|
|
if offset.UpperAppOffset+len(offset.Data) > boundary {
|
|
boundary = offset.UpperAppOffset + len(offset.Data)
|
|
}
|
|
}
|
|
// Sort the new part.
|
|
newPart := offsets[oldLen:]
|
|
sort.Slice(newPart, func(i, j int) bool {
|
|
return newPart[i].UpperAppOffset < newPart[j].UpperAppOffset
|
|
})
|
|
|
|
// Insertion sort.
|
|
for i := oldLen; i < len(offsets); i++ {
|
|
item := offsets[i]
|
|
j := i - 1
|
|
for ; j >= 0; j-- {
|
|
if item.UpperAppOffset < offsets[j].UpperAppOffset {
|
|
offsets[j+1] = offsets[j]
|
|
} else {
|
|
if offsets[j+1] != item {
|
|
offsets[j+1] = item
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if j < 0 {
|
|
offsets[0] = item
|
|
}
|
|
}
|
|
return offsets, nil
|
|
}
|
|
|
|
func ExtractCryptoFrameOffset(remainder []byte, transportOffset int) (offset *CryptoFrameOffset, frameSize int, err error) {
|
|
if len(remainder) == 0 {
|
|
return nil, 0, fmt.Errorf("frame has no length: %w", ErrOutOfRange)
|
|
}
|
|
frameType, nextField, err := BigEndianUvarint(remainder)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
switch frameType {
|
|
case Quic_FrameType_Ping:
|
|
return nil, nextField, nil
|
|
case Quic_FrameType_Padding:
|
|
for ; nextField < len(remainder) && remainder[nextField] == 0; nextField++ {
|
|
}
|
|
return nil, nextField, nil
|
|
case Quic_FrameType_Crypto:
|
|
offset, n, err := BigEndianUvarint(remainder[nextField:])
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
nextField += n
|
|
|
|
length, n, err := BigEndianUvarint(remainder[nextField:])
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
nextField += n
|
|
|
|
return &CryptoFrameOffset{
|
|
UpperAppOffset: int(offset),
|
|
Data: remainder[nextField : nextField+int(length)],
|
|
}, nextField + int(length), nil
|
|
case Quic_FrameType_ConnectionClose, Quic_FrameType_ConnectionClose2:
|
|
return nil, 0, fmt.Errorf("connection closed: %w", fs.ErrClosed)
|
|
default:
|
|
return nil, 0, fmt.Errorf("%w: %v", ErrUnknownFrameType, frameType)
|
|
}
|
|
}
|
|
|
|
var (
|
|
ErrMissingCrypto = fmt.Errorf("missing crypto frame")
|
|
)
|
|
|
|
type Locator interface {
|
|
Range(i, j int) ([]byte, error)
|
|
Slice(i, j int) (Locator, error)
|
|
At(i int) (byte, error)
|
|
Len() int
|
|
Bytes() ([]byte, error)
|
|
}
|
|
|
|
// LinearLocator only searches forward and have no boundary check.
|
|
type LinearLocator struct {
|
|
left int
|
|
length int
|
|
iOuter int
|
|
baseEnd int
|
|
baseStart int
|
|
baseData []byte
|
|
o []*CryptoFrameOffset
|
|
}
|
|
|
|
func NewLinearLocator(o []*CryptoFrameOffset) *LinearLocator {
|
|
if len(o) == 0 {
|
|
return &LinearLocator{}
|
|
}
|
|
return &LinearLocator{
|
|
left: 0,
|
|
length: o[len(o)-1].UpperAppOffset + len(o[len(o)-1].Data),
|
|
iOuter: 0,
|
|
baseData: o[0].Data,
|
|
baseStart: o[0].UpperAppOffset,
|
|
baseEnd: o[0].UpperAppOffset + len(o[0].Data),
|
|
o: o,
|
|
}
|
|
}
|
|
|
|
func (l *LinearLocator) relocate(i int) error {
|
|
// Relocate ll.iOuter.
|
|
for i >= l.baseEnd {
|
|
if l.iOuter+1 >= len(l.o) {
|
|
return ErrMissingCrypto
|
|
}
|
|
l.iOuter++
|
|
l.baseData = l.o[l.iOuter].Data
|
|
l.baseStart = l.o[l.iOuter].UpperAppOffset
|
|
l.baseEnd = l.baseStart + len(l.baseData)
|
|
}
|
|
if i < l.baseStart {
|
|
return ErrMissingCrypto
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *LinearLocator) Range(i, j int) ([]byte, error) {
|
|
if i == j {
|
|
return []byte{}, nil
|
|
}
|
|
if len(l.o) == 0 {
|
|
return nil, ErrMissingCrypto
|
|
}
|
|
size := j - i
|
|
|
|
// We find bytes including i and j, so we should sub j with 1.
|
|
i += l.left
|
|
j += l.left - 1
|
|
if err := l.relocate(i); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Linearly copy.
|
|
|
|
if j < l.baseEnd {
|
|
// In the same block, no copy needed.
|
|
return l.baseData[i-l.baseStart : j-l.baseStart+1], nil
|
|
}
|
|
|
|
b := make([]byte, size)
|
|
k := 0
|
|
for j >= l.baseEnd {
|
|
n := copy(b[k:], l.baseData[i-l.baseStart:])
|
|
k += n
|
|
i += n
|
|
if l.iOuter+1 >= len(l.o) || l.o[l.iOuter].UpperAppOffset+len(l.o[l.iOuter+1].Data) != l.o[l.iOuter].UpperAppOffset {
|
|
// Some crypto is missing.
|
|
return nil, ErrMissingCrypto
|
|
}
|
|
l.iOuter++
|
|
l.baseData = l.o[l.iOuter].Data
|
|
l.baseStart = l.o[l.iOuter].UpperAppOffset
|
|
l.baseEnd = l.baseStart + len(l.baseData)
|
|
}
|
|
copy(b[k:], l.baseData[i-l.baseStart:j-l.baseStart+1])
|
|
return b, nil
|
|
}
|
|
|
|
func (l *LinearLocator) At(i int) (byte, error) {
|
|
if len(l.o) == 0 {
|
|
return 0, ErrMissingCrypto
|
|
}
|
|
i += l.left
|
|
|
|
if err := l.relocate(i); err != nil {
|
|
return 0, err
|
|
}
|
|
b := l.baseData[i-l.baseStart]
|
|
return b, nil
|
|
}
|
|
|
|
func (l *LinearLocator) Slice(i, j int) (Locator, error) {
|
|
// We do not care about right.
|
|
newLL := *l
|
|
newLL.left += i
|
|
newLL.length = j - i + 1
|
|
return &newLL, nil
|
|
}
|
|
|
|
func (l *LinearLocator) Bytes() ([]byte, error) {
|
|
return l.Range(0, l.length)
|
|
}
|
|
|
|
var _ Locator = &LinearLocator{}
|
|
|
|
func (l *LinearLocator) Len() int {
|
|
return l.length
|
|
}
|
|
|
|
type BuiltinBytesLocator []byte
|
|
|
|
func (l BuiltinBytesLocator) Range(i, j int) ([]byte, error) {
|
|
return l[i:j], nil
|
|
}
|
|
func (l BuiltinBytesLocator) At(i int) (byte, error) {
|
|
return l[i], nil
|
|
}
|
|
func (l BuiltinBytesLocator) Slice(i, j int) (Locator, error) {
|
|
return l[i:j], nil
|
|
}
|
|
func (l BuiltinBytesLocator) Len() int {
|
|
return len(l)
|
|
}
|
|
func (l BuiltinBytesLocator) Bytes() ([]byte, error) {
|
|
return l, nil
|
|
}
|
|
|
|
var _ Locator = BuiltinBytesLocator{}
|