mirror of
https://github.com/42wim/matterbridge.git
synced 2024-11-30 14:42:00 -08:00
201 lines
4.8 KiB
Go
201 lines
4.8 KiB
Go
|
package gumble
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"io"
|
||
|
"net"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/golang/protobuf/proto"
|
||
|
"layeh.com/gumble/gumble/MumbleProto"
|
||
|
"layeh.com/gumble/gumble/varint"
|
||
|
)
|
||
|
|
||
|
// DefaultPort is the default port on which Mumble servers listen.
|
||
|
const DefaultPort = 64738
|
||
|
|
||
|
// Conn represents a control protocol connection to a Mumble client/server.
|
||
|
type Conn struct {
|
||
|
sync.Mutex
|
||
|
net.Conn
|
||
|
|
||
|
MaximumPacketBytes int
|
||
|
Timeout time.Duration
|
||
|
|
||
|
buffer []byte
|
||
|
}
|
||
|
|
||
|
// NewConn creates a new Conn with the given net.Conn.
|
||
|
func NewConn(conn net.Conn) *Conn {
|
||
|
return &Conn{
|
||
|
Conn: conn,
|
||
|
Timeout: time.Second * 20,
|
||
|
MaximumPacketBytes: 1024 * 1024 * 10,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ReadPacket reads a packet from the server. Returns the packet type, the
|
||
|
// packet data, and nil on success.
|
||
|
//
|
||
|
// This function should only be called by a single go routine.
|
||
|
func (c *Conn) ReadPacket() (uint16, []byte, error) {
|
||
|
c.Conn.SetReadDeadline(time.Now().Add(c.Timeout))
|
||
|
var header [6]byte
|
||
|
if _, err := io.ReadFull(c.Conn, header[:]); err != nil {
|
||
|
return 0, nil, err
|
||
|
}
|
||
|
pType := binary.BigEndian.Uint16(header[:])
|
||
|
pLength := binary.BigEndian.Uint32(header[2:])
|
||
|
pLengthInt := int(pLength)
|
||
|
if pLengthInt > c.MaximumPacketBytes {
|
||
|
return 0, nil, errors.New("gumble: packet larger than maximum allowed size")
|
||
|
}
|
||
|
if pLengthInt > len(c.buffer) {
|
||
|
c.buffer = make([]byte, pLengthInt)
|
||
|
}
|
||
|
if _, err := io.ReadFull(c.Conn, c.buffer[:pLengthInt]); err != nil {
|
||
|
return 0, nil, err
|
||
|
}
|
||
|
return pType, c.buffer[:pLengthInt], nil
|
||
|
}
|
||
|
|
||
|
// WriteAudio writes an audio packet to the connection.
|
||
|
func (c *Conn) WriteAudio(format, target byte, sequence int64, final bool, data []byte, X, Y, Z *float32) error {
|
||
|
var buff [1 + varint.MaxVarintLen*2]byte
|
||
|
buff[0] = (format << 5) | target
|
||
|
n := varint.Encode(buff[1:], sequence)
|
||
|
if n == 0 {
|
||
|
return errors.New("gumble: varint out of range")
|
||
|
}
|
||
|
l := int64(len(data))
|
||
|
if final {
|
||
|
l |= 0x2000
|
||
|
}
|
||
|
m := varint.Encode(buff[1+n:], l)
|
||
|
if m == 0 {
|
||
|
return errors.New("gumble: varint out of range")
|
||
|
}
|
||
|
header := buff[:1+n+m]
|
||
|
|
||
|
var positionalLength int
|
||
|
if X != nil {
|
||
|
positionalLength = 3 * 4
|
||
|
}
|
||
|
|
||
|
c.Lock()
|
||
|
defer c.Unlock()
|
||
|
|
||
|
if err := c.writeHeader(1, uint32(len(header)+len(data)+positionalLength)); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if _, err := c.Conn.Write(header); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if _, err := c.Conn.Write(data); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if positionalLength > 0 {
|
||
|
if err := binary.Write(c.Conn, binary.LittleEndian, *X); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := binary.Write(c.Conn, binary.LittleEndian, *Y); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := binary.Write(c.Conn, binary.LittleEndian, *Z); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WritePacket writes a data packet of the given type to the connection.
|
||
|
func (c *Conn) WritePacket(ptype uint16, data []byte) error {
|
||
|
c.Lock()
|
||
|
defer c.Unlock()
|
||
|
if err := c.writeHeader(uint16(ptype), uint32(len(data))); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if _, err := c.Conn.Write(data); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *Conn) writeHeader(pType uint16, pLength uint32) error {
|
||
|
var header [6]byte
|
||
|
binary.BigEndian.PutUint16(header[:], pType)
|
||
|
binary.BigEndian.PutUint32(header[2:], pLength)
|
||
|
if _, err := c.Conn.Write(header[:]); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WriteProto writes a protocol buffer message to the connection.
|
||
|
func (c *Conn) WriteProto(message proto.Message) error {
|
||
|
var protoType uint16
|
||
|
switch message.(type) {
|
||
|
case *MumbleProto.Version:
|
||
|
protoType = 0
|
||
|
case *MumbleProto.Authenticate:
|
||
|
protoType = 2
|
||
|
case *MumbleProto.Ping:
|
||
|
protoType = 3
|
||
|
case *MumbleProto.Reject:
|
||
|
protoType = 4
|
||
|
case *MumbleProto.ServerSync:
|
||
|
protoType = 5
|
||
|
case *MumbleProto.ChannelRemove:
|
||
|
protoType = 6
|
||
|
case *MumbleProto.ChannelState:
|
||
|
protoType = 7
|
||
|
case *MumbleProto.UserRemove:
|
||
|
protoType = 8
|
||
|
case *MumbleProto.UserState:
|
||
|
protoType = 9
|
||
|
case *MumbleProto.BanList:
|
||
|
protoType = 10
|
||
|
case *MumbleProto.TextMessage:
|
||
|
protoType = 11
|
||
|
case *MumbleProto.PermissionDenied:
|
||
|
protoType = 12
|
||
|
case *MumbleProto.ACL:
|
||
|
protoType = 13
|
||
|
case *MumbleProto.QueryUsers:
|
||
|
protoType = 14
|
||
|
case *MumbleProto.CryptSetup:
|
||
|
protoType = 15
|
||
|
case *MumbleProto.ContextActionModify:
|
||
|
protoType = 16
|
||
|
case *MumbleProto.ContextAction:
|
||
|
protoType = 17
|
||
|
case *MumbleProto.UserList:
|
||
|
protoType = 18
|
||
|
case *MumbleProto.VoiceTarget:
|
||
|
protoType = 19
|
||
|
case *MumbleProto.PermissionQuery:
|
||
|
protoType = 20
|
||
|
case *MumbleProto.CodecVersion:
|
||
|
protoType = 21
|
||
|
case *MumbleProto.UserStats:
|
||
|
protoType = 22
|
||
|
case *MumbleProto.RequestBlob:
|
||
|
protoType = 23
|
||
|
case *MumbleProto.ServerConfig:
|
||
|
protoType = 24
|
||
|
case *MumbleProto.SuggestConfig:
|
||
|
protoType = 25
|
||
|
default:
|
||
|
return errors.New("gumble: unknown message type")
|
||
|
}
|
||
|
data, err := proto.Marshal(message)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return c.WritePacket(protoType, data)
|
||
|
}
|