173 lines
5.4 KiB
Go
173 lines
5.4 KiB
Go
package gateway
|
|
|
|
import "encoding/binary"
|
|
|
|
type FeedbackKind uint8
|
|
|
|
const (
|
|
FeedbackIDR FeedbackKind = iota + 1
|
|
FeedbackFEC
|
|
FeedbackTerminalReceipt
|
|
)
|
|
|
|
const (
|
|
gatewayFeedbackHeaderSize = 8
|
|
gatewayFeedbackClient = 0
|
|
gatewayFeedbackGateway = 1
|
|
gatewayFeedbackIDR = 1
|
|
gatewayFeedbackFEC = 2
|
|
gatewayFeedbackTerminalAck = 3
|
|
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
|
|
}
|
|
case FeedbackTerminalReceipt:
|
|
kind = gatewayFeedbackTerminalAck
|
|
if len(feedback.Payload) != 0 {
|
|
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
|
|
case gatewayFeedbackTerminalAck:
|
|
if len(message.payload) != 0 {
|
|
return Feedback{}, ErrProviderMalformed
|
|
}
|
|
return Feedback{Kind: FeedbackTerminalReceipt}, 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
|
|
}
|