Files
cosmium/internal/rntbd/parser.go
T
2026-06-11 22:43:05 +03:00

213 lines
6.2 KiB
Go

package rntbd
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"os"
"github.com/pikami/cosmium/internal/logger"
)
type RntbdFrame struct {
ResourceType RntbdResourceType
OperationType RntbdOperationType
ActivityId []byte
RequestHeaders map[RntbdRequestHeader]any
ResponseHeaders map[RntbdResponseHeaderType]any
ContextHeaders map[RntbdContextHeader]any
Payload []byte
}
func ReadFrame(reader *bufio.Reader) (*RntbdFrame, error) {
sizeBytes := readBytes(reader, 4)
size := binary.LittleEndian.Uint32(sizeBytes)
payload := readBytes(reader, int(size)-4)
frame, err := parseFrame_Int(payload, false)
if err != nil {
return nil, err
}
if payloadPresent, ok := frame.RequestHeaders[RntbdRequestHeaderPayloadPresent]; ok && payloadPresent.([]byte)[0] == 1 {
payloadSize := binary.LittleEndian.Uint32(readBytes(reader, 4))
payload := readBytes(reader, int(payloadSize))
frame.Payload = payload
}
if payloadPresent, ok := frame.ResponseHeaders[RntbdResponseHeaderPayloadPresent]; ok && payloadPresent.([]byte)[0] == 1 {
payloadSize := binary.LittleEndian.Uint32(readBytes(reader, 4))
payload := readBytes(reader, int(payloadSize))
frame.Payload = payload
}
return frame, nil
}
func ParseFrame(data []byte, isResponse bool) (*RntbdFrame, error) {
if len(data) < 4 {
return nil, fmt.Errorf("data too short")
}
reader := bufio.NewReader(bytes.NewReader(data))
sizeBytes := readBytes(reader, 4)
size := binary.LittleEndian.Uint32(sizeBytes)
payload := readBytes(reader, int(size)-4)
frame, err := parseFrame_Int(payload, isResponse)
if err != nil {
return nil, err
}
if payloadPresent, ok := frame.RequestHeaders[RntbdRequestHeaderPayloadPresent]; ok && payloadPresent.([]byte)[0] == 1 {
payloadSize := binary.LittleEndian.Uint32(readBytes(reader, 4))
payload := readBytes(reader, int(payloadSize))
frame.Payload = payload
}
if payloadPresent, ok := frame.ResponseHeaders[RntbdResponseHeaderPayloadPresent]; ok && payloadPresent.([]byte)[0] == 1 {
payloadSize := binary.LittleEndian.Uint32(readBytes(reader, 4))
payload := readBytes(reader, int(payloadSize))
frame.Payload = payload
}
leftOverBytes, err := io.ReadAll(reader)
if err != nil {
logger.ErrorLn("Error reading leftOverBytes:", err)
}
if len(leftOverBytes) > 0 {
logger.ErrorLn("Left over bytes:", hex.EncodeToString(leftOverBytes))
}
return frame, nil
}
func parseFrame_Int(data []byte, isResponse bool) (*RntbdFrame, error) {
payloadReader := bufio.NewReader(bytes.NewReader(data))
resourceTypeBytes := readBytes(payloadReader, 2)
resourceType := binary.LittleEndian.Uint16(resourceTypeBytes)
operationTypeBytes := readBytes(payloadReader, 2)
operationType := RntbdOperationType(binary.LittleEndian.Uint16(operationTypeBytes))
activityIdBytes := readBytes(payloadReader, 16)
requestHeaders := make(map[RntbdRequestHeader]any)
responseHeaders := make(map[RntbdResponseHeaderType]any)
contextHeaders := make(map[RntbdContextHeader]any)
for {
if _, err := payloadReader.Peek(1); err != nil {
break
}
headerIdBytes := readBytes(payloadReader, 2)
headerId := binary.LittleEndian.Uint16(headerIdBytes)
token, err := parseRntbdToken(payloadReader)
if err != nil {
return nil, err
}
if resourceType == uint16(RntbdResourceTypeConnection) {
contextHeaders[RntbdContextHeader(headerId)] = token
} else if isResponse {
responseHeaders[RntbdResponseHeaderType(headerId)] = token
} else {
requestHeaders[RntbdRequestHeader(headerId)] = token
}
}
return &RntbdFrame{
ResourceType: RntbdResourceType(resourceType),
OperationType: RntbdOperationType(operationType),
ActivityId: activityIdBytes,
RequestHeaders: requestHeaders,
ResponseHeaders: responseHeaders,
ContextHeaders: contextHeaders,
}, nil
}
func parseRntbdToken(reader *bufio.Reader) (any, error) {
tokenTypeBytes := readBytes(reader, 1)
tokenType := RntbdTokenType(tokenTypeBytes[0])
switch tokenType {
case RntbdTokenTypeByte:
token := readBytes(reader, 1)
return token, nil
case RntbdTokenTypeUShort:
token := binary.LittleEndian.Uint16(readBytes(reader, 2))
return token, nil
case RntbdTokenTypeULong:
token := binary.LittleEndian.Uint32(readBytes(reader, 4))
return token, nil
case RntbdTokenTypeLong:
token := int32(binary.LittleEndian.Uint32(readBytes(reader, 4)))
return token, nil
case RntbdTokenTypeULongLong:
token := binary.LittleEndian.Uint64(readBytes(reader, 8))
return token, nil
case RntbdTokenTypeLongLong:
token := int64(binary.LittleEndian.Uint64(readBytes(reader, 8)))
return token, nil
case RntbdTokenTypeGuid:
token := readBytes(reader, 16)
return token, nil
case RntbdTokenTypeSmallString:
lengthBytes := readBytes(reader, 1)
length := uint8(lengthBytes[0])
token := readBytes(reader, int(length))
return string(token), nil
case RntbdTokenTypeString:
length := binary.LittleEndian.Uint16(readBytes(reader, 2))
token := readBytes(reader, int(length))
return string(token), nil
case RntbdTokenTypeULongString:
length := binary.LittleEndian.Uint32(readBytes(reader, 4))
token := readBytes(reader, int(length))
return string(token), nil
case RntbdTokenTypeSmallBytes:
lengthBytes := readBytes(reader, 1)
length := uint8(lengthBytes[0])
token := readBytes(reader, int(length))
return token, nil
case RntbdTokenTypeBytes:
length := binary.LittleEndian.Uint16(readBytes(reader, 2))
token := readBytes(reader, int(length))
return token, nil
case RntbdTokenTypeULongBytes:
length := binary.LittleEndian.Uint32(readBytes(reader, 4))
token := readBytes(reader, int(length))
return token, nil
case RntbdTokenTypeFloat:
// I can't be bothered to implement this, let's just return a byte array
token := readBytes(reader, 4)
return token, nil
case RntbdTokenTypeDouble:
// I can't be bothered to implement this, let's just return a byte array
token := readBytes(reader, 8)
return token, nil
case RntbdTokenTypeInvalid:
return nil, fmt.Errorf("invalid token type")
}
return nil, fmt.Errorf("invalid token type")
}
func readBytes(reader *bufio.Reader, n int) []byte {
bytes := make([]byte, n)
_, err := io.ReadFull(reader, bytes)
if err != nil {
logger.ErrorLn("Error reading bytes:", err)
os.Exit(0)
}
return bytes
}