forked from External/grumble
Throw the code out there. :)
This commit is contained in:
commit
73ab596ae6
27 changed files with 3565 additions and 0 deletions
290
client.go
Normal file
290
client.go
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright (c) 2010 The Grumble Authors
|
||||
// The use of this source code is goverened by a BSD-style
|
||||
// license that can be found in the LICENSE-file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"encoding/binary"
|
||||
"goprotobuf.googlecode.com/hg/proto"
|
||||
"mumbleproto"
|
||||
"cryptstate"
|
||||
"packetdatastream"
|
||||
)
|
||||
|
||||
// A client connection
|
||||
type ClientConnection struct {
|
||||
// Connection-related
|
||||
tcpaddr *net.TCPAddr
|
||||
udpaddr *net.UDPAddr
|
||||
conn net.Conn
|
||||
reader *bufio.Reader
|
||||
writer *bufio.Writer
|
||||
state int
|
||||
server *Server
|
||||
|
||||
msgchan chan *Message
|
||||
udprecv chan []byte
|
||||
|
||||
disconnected bool
|
||||
|
||||
crypt *cryptstate.CryptState
|
||||
codecs []int32
|
||||
udp bool
|
||||
|
||||
// Personal
|
||||
Session uint32
|
||||
Username string
|
||||
Tokens []string
|
||||
}
|
||||
|
||||
// Something invalid happened on the wire.
|
||||
func (client *ClientConnection) Panic(reason string) {
|
||||
client.disconnected = true
|
||||
// fixme(mkrautz): we should inform the server "handler" method through a channel of this event,
|
||||
// so it can perform a proper disconnect.
|
||||
}
|
||||
|
||||
// Read a protobuf message from a client
|
||||
func (client *ClientConnection) readProtoMessage() (msg *Message, err os.Error) {
|
||||
var length uint32
|
||||
var kind uint16
|
||||
|
||||
// Read the message type (16-bit big-endian unsigned integer)
|
||||
err = binary.Read(client.reader, binary.BigEndian, &kind)
|
||||
if err != nil {
|
||||
client.Panic("Unable to read packet kind")
|
||||
return
|
||||
}
|
||||
|
||||
// Read the message length (32-bit big-endian unsigned integer)
|
||||
err = binary.Read(client.reader, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
client.Panic("Unable to read packet length")
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, length)
|
||||
_, err = client.reader.Read(buf)
|
||||
if err != nil {
|
||||
client.Panic("Unable to read packet content")
|
||||
return
|
||||
}
|
||||
|
||||
msg = &Message{
|
||||
buf: buf,
|
||||
kind: kind,
|
||||
client: client,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Send a protobuf-encoded message
|
||||
func (c *ClientConnection) sendProtoMessage(kind uint16, msg interface{}) (err os.Error) {
|
||||
d, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.msgchan <- &Message{
|
||||
buf: d,
|
||||
kind: kind,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UDP receiver.
|
||||
func (client *ClientConnection) udpreceiver() {
|
||||
for {
|
||||
buf := <-client.udprecv
|
||||
kind := (buf[0] >> 5) & 0x07;
|
||||
|
||||
switch kind {
|
||||
case UDPMessageVoiceSpeex: fallthrough;
|
||||
case UDPMessageVoiceCELTAlpha: fallthrough;
|
||||
case UDPMessageVoiceCELTBeta:
|
||||
kind := buf[0] & 0xe0
|
||||
target := buf[0] & 0x1f
|
||||
var counter uint8
|
||||
outbuf := make([]byte, 1024)
|
||||
|
||||
incoming := packetdatastream.New(buf[1:1+(len(buf)-1)])
|
||||
outgoing := packetdatastream.New(outbuf[1:1+(len(outbuf)-1)])
|
||||
_ = incoming.GetUint32()
|
||||
|
||||
for {
|
||||
counter = incoming.Next8()
|
||||
incoming.Skip(int(counter & 0x7f))
|
||||
if !((counter & 0x80) != 0 && incoming.IsValid()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
outgoing.PutUint32(client.Session)
|
||||
outgoing.PutBytes(buf[1:1+(len(buf)-1)])
|
||||
|
||||
// Sever loopback
|
||||
if target == 0x1f {
|
||||
outbuf[0] = kind
|
||||
client.sendUdp(&Message{
|
||||
buf: outbuf[0:1+outgoing.Size()],
|
||||
client: client,
|
||||
})
|
||||
}
|
||||
case UDPMessagePing:
|
||||
client.server.udpsend <- &Message{
|
||||
buf: buf,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (client *ClientConnection) sendUdp(msg *Message) {
|
||||
if client.udp {
|
||||
// Send as UDP
|
||||
log.Stdoutf("Sent UDP!")
|
||||
client.server.udpsend <- msg
|
||||
} else {
|
||||
// Tunnel through TCP
|
||||
log.Stdoutf("Sent TCP!")
|
||||
msg.kind = MessageUDPTunnel
|
||||
client.msgchan <- msg
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Sender Goroutine
|
||||
//
|
||||
func (client *ClientConnection) sender() {
|
||||
for {
|
||||
msg := <-client.msgchan
|
||||
|
||||
// First, we write out the message type as a big-endian uint16
|
||||
err := binary.Write(client.writer, binary.BigEndian, msg.kind)
|
||||
if err != nil {
|
||||
client.Panic("Unable to write message type to client")
|
||||
return
|
||||
}
|
||||
|
||||
// Then the length of the protobuf message
|
||||
err = binary.Write(client.writer, binary.BigEndian, uint32(len(msg.buf)))
|
||||
if err != nil {
|
||||
client.Panic("Unable to write message length to client")
|
||||
return
|
||||
}
|
||||
|
||||
// At last, write the buffer itself
|
||||
_, err = client.writer.Write(msg.buf)
|
||||
if err != nil {
|
||||
client.Panic("Unable to write message content to client")
|
||||
return
|
||||
}
|
||||
|
||||
// Flush the write buffer
|
||||
err = client.writer.Flush()
|
||||
if err != nil {
|
||||
client.Panic("Unable to flush client write buffer")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Receiver Goroutine
|
||||
func (client *ClientConnection) receiver() {
|
||||
for {
|
||||
|
||||
// The version handshake is done. Forward this message to the synchronous request handler.
|
||||
if client.state == StateClientAuthenticated || client.state == StateClientSentVersion {
|
||||
// Try to read the next message in the pool
|
||||
msg, err := client.readProtoMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Special case UDPTunnel messages. They're high priority and shouldn't
|
||||
// go through our synchronous path.
|
||||
if msg.kind == MessageUDPTunnel {
|
||||
client.udp = false
|
||||
client.udprecv <- msg.buf
|
||||
} else {
|
||||
client.server.incoming <- msg
|
||||
}
|
||||
}
|
||||
|
||||
// The client has just connected. Before it sends its authentication
|
||||
// information we must send it our version information so it knows
|
||||
// what version of the protocol it should speak.
|
||||
if client.state == StateClientConnected {
|
||||
client.sendProtoMessage(MessageVersion, &mumbleproto.Version{
|
||||
Version: proto.Uint32(0x10203),
|
||||
Release: proto.String("1.2.2"),
|
||||
})
|
||||
// fixme(mkrautz): Re-add OS information... Does it break anything? It seems like
|
||||
// the client discards the version message if there is no OS information in it.
|
||||
client.state = StateServerSentVersion
|
||||
continue
|
||||
} else if client.state == StateServerSentVersion {
|
||||
msg, err := client.readProtoMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
version := &mumbleproto.Version{}
|
||||
err = proto.Unmarshal(msg.buf, version)
|
||||
if err != nil {
|
||||
client.Panic("Unable to unmarshal client version packet.")
|
||||
return
|
||||
}
|
||||
|
||||
// Don't really do anything with it...
|
||||
|
||||
client.state = StateClientSentVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the channel list to a client.
|
||||
func (client *ClientConnection) sendChannelList() {
|
||||
server := client.server
|
||||
root := server.root
|
||||
|
||||
// Start at the root channel.
|
||||
err := client.sendProtoMessage(MessageChannelState, &mumbleproto.ChannelState{
|
||||
ChannelId: proto.Uint32(uint32(root.Id)),
|
||||
Name: proto.String(root.Name),
|
||||
Description: proto.String(root.Description),
|
||||
})
|
||||
if err != nil {
|
||||
// panic!
|
||||
log.Stdoutf("poanic!")
|
||||
}
|
||||
}
|
||||
|
||||
// Send the userlist to a client.
|
||||
func (client *ClientConnection) sendUserList() {
|
||||
server := client.server
|
||||
|
||||
server.cmutex.RLock()
|
||||
defer server.cmutex.RUnlock()
|
||||
|
||||
for x := range server.clients.Iter() {
|
||||
user := x.(*ClientConnection)
|
||||
err := user.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
|
||||
Session: proto.Uint32(client.Session),
|
||||
Name: proto.String(client.Username),
|
||||
ChannelId: proto.Uint32(0),
|
||||
})
|
||||
if err != nil {
|
||||
log.Stdoutf("unable to send!")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue