package pnyx import ( "encoding" "encoding/binary" "fmt" "github.com/google/uuid" ) type PacketType uint8 const ( PACKET_CHANNEL_COMMAND PacketType = iota PACKET_CHANNEL_DATA PACKET_CHANNEL_PEER CHANNEL_HEADER_LEN int = 5 CHANNEL_COMMAND_LEN = CHANNEL_HEADER_LEN + COMMAND_LENGTH + ID_LENGTH CHANNEL_PEER_LEN = CHANNEL_HEADER_LEN + ID_LENGTH ) type Payload interface { encoding.BinaryMarshaler } type Packet struct { Type PacketType Payload Payload } func(packet Packet) MarshalBinary() ([]byte, error) { payload, err := packet.Payload.MarshalBinary() if err != nil { return nil, err } return append([]byte{byte(packet.Type)}, payload...), nil } func ParsePacket(data []byte) (Payload, error) { if len(data) < 1 { return nil, fmt.Errorf("Packet too short to parse - %d/1", len(data)) } switch PacketType(data[0]) { case PACKET_CHANNEL_DATA: return ParseChannelDataPacket(data[1:]) case PACKET_CHANNEL_COMMAND: return ParseChannelCommandPacket(data[1:]) case PACKET_CHANNEL_PEER: return ParseChannelPeerPacket(data[1:]) default: return nil, fmt.Errorf("Don't know how to parse packet type 0x%02x", data[0]) } } type ChannelHeader struct { Channel ChannelID Mode ModeID } func(packet ChannelHeader) MarshalBinary() ([]byte, error) { p := binary.BigEndian.AppendUint32(nil, uint32(packet.Channel)) return append(p, byte(packet.Mode)), nil } func ParseChannelHeader(data []byte) (ChannelHeader, error) { if len(data) < 5 { return ChannelHeader{}, fmt.Errorf("Not enough bytes to parse ChannelPacket(%d/%d)", len(data), 6) } return ChannelHeader{ Channel: ChannelID(binary.BigEndian.Uint32(data)), Mode: ModeID(data[4]), }, nil } type ChannelCommandPacket struct { ChannelHeader Command byte ReqID uuid.UUID Data []byte } func NewChannelCommandPacket(channel ChannelID, mode ModeID, command byte, data []byte) (*Packet, uuid.UUID) { request_id := uuid.New() return &Packet{ Type: PACKET_CHANNEL_COMMAND, Payload: ChannelCommandPacket{ ChannelHeader: ChannelHeader{ Channel: channel, Mode: mode, }, Command: command, ReqID: request_id, Data: data, }, }, request_id } func(packet ChannelCommandPacket) MarshalBinary() ([]byte, error) { header, err := packet.ChannelHeader.MarshalBinary() if err != nil { return nil, err } data := append(header, packet.Command) data = append(data, packet.ReqID[:]...) return append(data, packet.Data...), nil } func ParseChannelCommandPacket(data []byte) (ChannelCommandPacket, error) { if len(data) < CHANNEL_COMMAND_LEN { return ChannelCommandPacket{}, fmt.Errorf("Not enough data to decode channel command packet %d/%d", len(data), CHANNEL_COMMAND_LEN) } header, err := ParseChannelHeader(data[:CHANNEL_HEADER_LEN]) if err != nil { return ChannelCommandPacket{}, err } command := data[CHANNEL_HEADER_LEN] request_id := uuid.UUID(data[CHANNEL_HEADER_LEN+COMMAND_LENGTH:]) return ChannelCommandPacket{ ChannelHeader: header, Command: command, ReqID: request_id, Data: data[CHANNEL_COMMAND_LEN:], }, nil } type ChannelPeerPacket struct { ChannelHeader Peer PeerID Data []byte } func NewChannelPeerPacket(peer PeerID, channel ChannelID, mode ModeID, data []byte) *Packet { return &Packet{ Type: PACKET_CHANNEL_PEER, Payload: ChannelPeerPacket{ ChannelHeader: ChannelHeader{ Channel: channel, Mode: mode, }, Peer: peer, Data: data, }, } } func(packet ChannelPeerPacket) MarshalBinary() ([]byte, error) { header, err := packet.ChannelHeader.MarshalBinary() if err != nil { return nil, err } data := append(header, packet.Peer[:]...) return append(data, packet.Data...), nil } func ParseChannelPeerPacket(data []byte) (ChannelPeerPacket, error) { if len(data) < CHANNEL_PEER_LEN { return ChannelPeerPacket{}, fmt.Errorf("Not enough bytes to parse ServerChannelPacket: %d/%d", len(data), ID_LENGTH) } header, err := ParseChannelHeader(data) if err != nil { return ChannelPeerPacket{}, err } return ChannelPeerPacket{ ChannelHeader: header, Peer: PeerID(data[CHANNEL_HEADER_LEN:]), Data: data[CHANNEL_PEER_LEN:], }, nil } type ChannelDataPacket struct { ChannelHeader Data []byte } func NewChannelDataPacket(channel ChannelID, mode ModeID, data []byte) *Packet { return &Packet{ Type: PACKET_CHANNEL_DATA, Payload: ChannelDataPacket{ ChannelHeader: ChannelHeader{ Channel: channel, Mode: mode, }, Data: data, }, } } func(packet ChannelDataPacket) MarshalBinary() ([]byte, error) { header, err := packet.ChannelHeader.MarshalBinary() if err != nil { return nil, err } return append(header, packet.Data...), nil } func ParseChannelDataPacket(data []byte) (ChannelDataPacket, error) { if len(data) < CHANNEL_HEADER_LEN { return ChannelDataPacket{}, fmt.Errorf("Not enough data to parse ChannelDataPacket") } header, err := ParseChannelHeader(data) if err != nil { return ChannelDataPacket{}, nil } return ChannelDataPacket{ ChannelHeader: header, Data: data[CHANNEL_HEADER_LEN:], }, nil }