93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
|
package utils
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
// RicochetData is a structure containing the raw data and the channel it the
|
||
|
// message originated on.
|
||
|
type RicochetData struct {
|
||
|
Channel int32
|
||
|
Data []byte
|
||
|
}
|
||
|
|
||
|
// RicochetNetworkInterface abstract operations that interact with ricochet's
|
||
|
// packet layer.
|
||
|
type RicochetNetworkInterface interface {
|
||
|
Recv(conn net.Conn) ([]byte, error)
|
||
|
SendRicochetPacket(conn net.Conn, channel int32, data []byte)
|
||
|
RecvRicochetPackets(conn net.Conn) ([]RicochetData, error)
|
||
|
}
|
||
|
|
||
|
// RicochetNetwork is a concrete implementation of the RicochetNetworkInterface
|
||
|
type RicochetNetwork struct {
|
||
|
}
|
||
|
|
||
|
// Recv reads data from the client, and returns the raw byte array, else error.
|
||
|
func (rn *RicochetNetwork) Recv(conn net.Conn) ([]byte, error) {
|
||
|
buf := make([]byte, 4096)
|
||
|
n, err := conn.Read(buf)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
ret := make([]byte, n)
|
||
|
copy(ret[:], buf[:])
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
// SendRicochetPacket places the data into a structure needed for the client to
|
||
|
// decode the packet and writes the packet to the network.
|
||
|
func (rn *RicochetNetwork) SendRicochetPacket(conn net.Conn, channel int32, data []byte) {
|
||
|
header := make([]byte, 4+len(data))
|
||
|
header[0] = byte(len(header) >> 8)
|
||
|
header[1] = byte(len(header) & 0x00FF)
|
||
|
header[2] = 0x00
|
||
|
header[3] = byte(channel)
|
||
|
copy(header[4:], data[:])
|
||
|
conn.Write(header)
|
||
|
}
|
||
|
|
||
|
// RecvRicochetPackets returns an array of new messages received from the ricochet client
|
||
|
func (rn *RicochetNetwork) RecvRicochetPackets(conn net.Conn) ([]RicochetData, error) {
|
||
|
buf, err := rn.Recv(conn)
|
||
|
if err != nil && len(buf) < 4 {
|
||
|
return nil, errors.New("failed to retrieve new messages from the client")
|
||
|
}
|
||
|
|
||
|
pos := 0
|
||
|
finished := false
|
||
|
var datas []RicochetData
|
||
|
|
||
|
for !finished {
|
||
|
size := int(binary.BigEndian.Uint16(buf[pos+0 : pos+2]))
|
||
|
channel := int(binary.BigEndian.Uint16(buf[pos+2 : pos+4]))
|
||
|
|
||
|
if size < 4 {
|
||
|
return datas, errors.New("invalid ricochet packet received (size=" + strconv.Itoa(size) + ")")
|
||
|
}
|
||
|
|
||
|
if pos+size > len(buf) {
|
||
|
return datas, errors.New("partial data packet received")
|
||
|
}
|
||
|
|
||
|
data := RicochetData{}
|
||
|
data.Channel = int32(channel)
|
||
|
|
||
|
if pos+4 >= len(buf) {
|
||
|
data.Data = make([]byte, 0)
|
||
|
} else {
|
||
|
data.Data = buf[pos+4 : pos+size]
|
||
|
}
|
||
|
|
||
|
datas = append(datas, data)
|
||
|
pos += size
|
||
|
if pos >= len(buf) {
|
||
|
finished = true
|
||
|
}
|
||
|
}
|
||
|
return datas, nil
|
||
|
}
|