package pnyx import ( "encoding" "fmt" "github.com/google/uuid" ) type PacketType uint8 const ( PACKET_SERVER_COMMAND PacketType = iota PACKET_CHANNEL_COMMAND PACKET_DATA PACKET_PEER PACKET_PING CHANNEL_HEADER_LEN int = 2 CHANNEL_COMMAND_LEN = CHANNEL_HEADER_LEN + COMMAND_LENGTH + REQ_ID_LENGTH CHANNEL_PEER_LEN = CHANNEL_HEADER_LEN + PEER_ID_LENGTH CHANNEL_COMMAND_JOIN byte = iota CHANNEL_COMMAND_LEAVE SERVER_COMMAND_ADD_CHANNEL byte = iota SERVER_COMMAND_DEL_CHANNEL ) 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_SERVER_COMMAND: return ParseCommandPacket(data[1:]) case PACKET_CHANNEL_COMMAND: return ParseChannelCommandPacket(data[1:]) case PACKET_DATA: return ParseDataPacket(data[1:]) case PACKET_PEER: return ParsePeerPacket(data[1:]) case PACKET_PING: return ParsePingPacket(data[1:]) default: return nil, fmt.Errorf("Don't know how to parse packet type 0x%02x", data[0]) } } type PingPacket struct { } func(packet PingPacket) MarshalBinary() ([]byte, error) { return []byte{}, nil } func NewPingPacket() *Packet { return &Packet{ Type: PACKET_PING, Payload: PingPacket{}, } } func ParsePingPacket(data []byte) (PingPacket, error) { if len(data) != 0 { return PingPacket{}, fmt.Errorf("Wrong number of bytes to parse PingPacket %d/0", len(data)) } return PingPacket{}, nil } type CommandPacket struct { ReqID uuid.UUID Command byte Data []byte } func (packet CommandPacket) MarshalBinary() ([]byte, error) { p := make([]byte, 17 + len(packet.Data)) copy(p, packet.ReqID[:]) p[16] = packet.Command copy(p[17:], packet.Data) return p, nil } func NewCommandPacket(request_id uuid.UUID, command byte, data []byte) *Packet { return &Packet{ Type: PACKET_SERVER_COMMAND, Payload: CommandPacket{ ReqID: request_id, Command: command, Data: data, }, } } func ParseCommandPacket(data []byte) (CommandPacket, error) { if len(data) < 17 { return CommandPacket{}, fmt.Errorf("Not enough data to decode CommandPacket: %d/%d", len(data), 17) } return CommandPacket{ ReqID: uuid.UUID(data[0:16]), Command: data[16], Data: data[17:], }, nil } type ChannelHeader struct { Channel ChannelID Mode ModeID } func(packet ChannelHeader) MarshalBinary() ([]byte, error) { return []byte{byte(packet.Channel), byte(packet.Mode)}, nil } func ParseChannelHeader(data []byte) (ChannelHeader, error) { if len(data) < 2 { return ChannelHeader{}, fmt.Errorf("Not enough bytes to parse ChannelPacket(%d/%d)", len(data), 2) } return ChannelHeader{ Channel: ChannelID(data[0]), Mode: ModeID(data[1]), }, nil } type ChannelCommandPacket struct { ChannelHeader Command byte ReqID uuid.UUID Data []byte } func NewChannelCommandPacket(request_id uuid.UUID, channel ChannelID, mode ModeID, command byte, data []byte) *Packet { return &Packet{ Type: PACKET_CHANNEL_COMMAND, Payload: ChannelCommandPacket{ ChannelHeader: ChannelHeader{ Channel: channel, Mode: mode, }, Command: command, ReqID: request_id, Data: data, }, } } func(packet ChannelCommandPacket) MarshalBinary() ([]byte, error) { header, err := packet.ChannelHeader.MarshalBinary() if err != nil { return nil, err } data := make([]byte, len(header) + len(packet.Data) + REQ_ID_LENGTH + COMMAND_LENGTH) copy(data, header) data[CHANNEL_HEADER_LEN] = packet.Command copy(data[CHANNEL_HEADER_LEN + COMMAND_LENGTH:], packet.ReqID[:]) copy(data[CHANNEL_HEADER_LEN + COMMAND_LENGTH + REQ_ID_LENGTH:], packet.Data) return 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 PeerPacket struct { ChannelHeader Peer PeerID Data []byte } func NewPeerPacket(peer PeerID, channel ChannelID, mode ModeID, data []byte) *Packet { return &Packet{ Type: PACKET_PEER, Payload: PeerPacket{ ChannelHeader: ChannelHeader{ Channel: channel, Mode: mode, }, Peer: peer, Data: data, }, } } func(packet PeerPacket) MarshalBinary() ([]byte, error) { header, err := packet.ChannelHeader.MarshalBinary() if err != nil { return nil, err } data := make([]byte, CHANNEL_PEER_LEN + len(packet.Data)) copy(data, header) copy(data[CHANNEL_HEADER_LEN:], packet.Peer[:]) copy(data[CHANNEL_PEER_LEN:], packet.Data) return data, nil } func ParsePeerPacket(data []byte) (PeerPacket, error) { if len(data) < CHANNEL_PEER_LEN { return PeerPacket{}, fmt.Errorf("Not enough bytes to parse ServerChannelPacket: %d/%d", len(data), PEER_ID_LENGTH) } header, err := ParseChannelHeader(data) if err != nil { return PeerPacket{}, err } return PeerPacket{ ChannelHeader: header, Peer: PeerID(data[CHANNEL_HEADER_LEN:]), Data: data[CHANNEL_HEADER_LEN + PEER_ID_LENGTH:], }, nil } type DataPacket struct { ChannelHeader Data []byte } func NewDataPacket(channel ChannelID, mode ModeID, data []byte) *Packet { return &Packet{ Type: PACKET_DATA, Payload: DataPacket{ ChannelHeader: ChannelHeader{ Channel: channel, Mode: mode, }, Data: data, }, } } func(packet DataPacket) MarshalBinary() ([]byte, error) { header, err := packet.ChannelHeader.MarshalBinary() if err != nil { return nil, err } return append(header, packet.Data...), nil } func ParseDataPacket(data []byte) (DataPacket, error) { if len(data) < CHANNEL_HEADER_LEN { return DataPacket{}, fmt.Errorf("Not enough data to parse DataPacket") } header, err := ParseChannelHeader(data) if err != nil { return DataPacket{}, nil } return DataPacket{ ChannelHeader: header, Data: data[CHANNEL_HEADER_LEN:], }, nil }