Files
VerseVDI-Data-Plane/gateway/feedback.go
T

161 lines
5.1 KiB
Go

package gateway
import "encoding/binary"
type FeedbackKind uint8
const (
FeedbackIDR FeedbackKind = iota + 1
FeedbackFEC
)
const (
gatewayFeedbackHeaderSize = 8
gatewayFeedbackClient = 0
gatewayFeedbackGateway = 1
gatewayFeedbackIDR = 1
gatewayFeedbackFEC = 2
gatewayFeedbackTerminated = 0x10
gatewayFeedbackRumble = 0x11
gatewayFeedbackHDR = 0x12
gatewayFeedbackDisconnected = 0x13
)
type gatewayFeedbackMessage struct {
direction byte
kind byte
payload []byte
}
func EncodeProviderEvent(event ProviderEvent) ([]byte, error) {
switch event.Kind {
case ProviderEventTerminated:
if len(event.Payload) != 4 {
return nil, ErrProviderMalformed
}
return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackTerminated, event.Payload)
case ProviderEventRumble:
if len(event.Payload) != 5 || event.Payload[0] > 15 {
return nil, ErrProviderMalformed
}
return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackRumble, event.Payload)
case ProviderEventHDR:
if len(event.Payload) != 1 || event.Payload[0] > 1 {
return nil, ErrProviderMalformed
}
return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackHDR, event.Payload)
case ProviderEventDisconnected:
if len(event.Payload) != 0 {
return nil, ErrProviderMalformed
}
return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackDisconnected, nil)
default:
return nil, ErrProviderMalformed
}
}
func EncodeClientFeedback(feedback Feedback) ([]byte, error) {
var kind byte
switch feedback.Kind {
case FeedbackIDR:
kind = gatewayFeedbackIDR
if len(feedback.Payload) != 0 {
return nil, ErrProviderMalformed
}
case FeedbackFEC:
kind = gatewayFeedbackFEC
if !validGatewayFECStatus(feedback.Payload) {
return nil, ErrProviderMalformed
}
default:
return nil, ErrProviderMalformed
}
return encodeGatewayFeedback(gatewayFeedbackClient, kind, feedback.Payload)
}
func DecodeClientFeedback(data []byte) (Feedback, error) {
message, err := decodeGatewayFeedback(data)
if err != nil || message.direction != gatewayFeedbackClient {
return Feedback{}, ErrProviderMalformed
}
switch message.kind {
case gatewayFeedbackIDR:
if len(message.payload) != 0 {
return Feedback{}, ErrProviderMalformed
}
return Feedback{Kind: FeedbackIDR}, nil
case gatewayFeedbackFEC:
if !validGatewayFECStatus(message.payload) {
return Feedback{}, ErrProviderMalformed
}
return Feedback{Kind: FeedbackFEC, Payload: message.payload}, nil
default:
return Feedback{}, ErrProviderMalformed
}
}
func validGatewayFECStatus(payload []byte) bool {
if len(payload) != 21 {
return false
}
totalData := binary.BigEndian.Uint16(payload[10:12])
totalParity := binary.BigEndian.Uint16(payload[12:14])
receivedData := binary.BigEndian.Uint16(payload[14:16])
receivedParity := binary.BigEndian.Uint16(payload[16:18])
return totalData > 0 && receivedData <= totalData && receivedParity <= totalParity && payload[18] <= 100 && payload[20] > 0 && payload[19] < payload[20]
}
func DecodeProviderEvent(data []byte) (ProviderEvent, error) {
message, err := decodeGatewayFeedback(data)
if err != nil || message.direction != gatewayFeedbackGateway {
return ProviderEvent{}, ErrProviderMalformed
}
switch message.kind {
case gatewayFeedbackTerminated:
if len(message.payload) != 4 {
return ProviderEvent{}, ErrProviderMalformed
}
return ProviderEvent{Kind: ProviderEventTerminated, Payload: message.payload}, nil
case gatewayFeedbackRumble:
if len(message.payload) != 5 || message.payload[0] > 15 {
return ProviderEvent{}, ErrProviderMalformed
}
return ProviderEvent{Kind: ProviderEventRumble, Payload: message.payload}, nil
case gatewayFeedbackHDR:
if len(message.payload) != 1 || message.payload[0] > 1 {
return ProviderEvent{}, ErrProviderMalformed
}
return ProviderEvent{Kind: ProviderEventHDR, Payload: message.payload}, nil
case gatewayFeedbackDisconnected:
if len(message.payload) != 0 {
return ProviderEvent{}, ErrProviderMalformed
}
return ProviderEvent{Kind: ProviderEventDisconnected}, nil
default:
return ProviderEvent{}, ErrProviderMalformed
}
}
func encodeGatewayFeedback(direction, kind byte, payload []byte) ([]byte, error) {
if len(payload) > 1016 {
return nil, ErrProviderMalformed
}
encoded := make([]byte, gatewayFeedbackHeaderSize+len(payload))
copy(encoded, "VGF1")
encoded[4], encoded[5] = direction, kind
binary.BigEndian.PutUint16(encoded[6:8], uint16(len(payload)))
copy(encoded[8:], payload)
return encoded, nil
}
func decodeGatewayFeedback(data []byte) (gatewayFeedbackMessage, error) {
if len(data) < gatewayFeedbackHeaderSize || len(data) > 1024 || string(data[:4]) != "VGF1" || len(data) != gatewayFeedbackHeaderSize+int(binary.BigEndian.Uint16(data[6:8])) {
return gatewayFeedbackMessage{}, ErrProviderMalformed
}
message := gatewayFeedbackMessage{direction: data[4], kind: data[5], payload: append([]byte(nil), data[8:]...)}
if message.direction != gatewayFeedbackClient && message.direction != gatewayFeedbackGateway {
return gatewayFeedbackMessage{}, ErrProviderMalformed
}
return message, nil
}