mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-19 21:59:59 -08:00
Throw the code out there. :)
This commit is contained in:
commit
73ab596ae6
27 changed files with 3565 additions and 0 deletions
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
|
@ -0,0 +1 @@
|
|||
Mikkel Krautz <mikkel@krautz.dk>
|
||||
32
LICENSE
Normal file
32
LICENSE
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Grumble - an implementation of Murmur in Go
|
||||
//
|
||||
// Copyright (c) 2010 The Grumble Authors
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// - Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// - Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// - Neither the name of the Mumble Developers nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
40
Makefile
Normal file
40
Makefile
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# 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.
|
||||
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG = grumble
|
||||
|
||||
PACKAGES = \
|
||||
pkg/packetdatastream \
|
||||
pkg/cryptstate \
|
||||
pkg/mumbleproto
|
||||
|
||||
GCFLAGS = -Ipkg/cryptstate/_obj -Ipkg/packetdatastream/_obj -Ipkg/mumbleproto/_obj
|
||||
LDFLAGS = -Lpkg/cryptstate/_obj -Lpkg/packetdatastream/_obj -Lpkg/mumbleproto/_obj
|
||||
|
||||
GOFILES = \
|
||||
grumble.go \
|
||||
message.go \
|
||||
tlsserver.go \
|
||||
server.go \
|
||||
client.go
|
||||
|
||||
.PHONY: grumble
|
||||
grumble: pkg
|
||||
$(GC) $(GCFLAGS) -o $(TARG).$(O) $(GOFILES)
|
||||
$(LD) $(LDFLAGS) -o $(TARG) $(TARG).$(O)
|
||||
|
||||
.PHONY: pkg
|
||||
pkg:
|
||||
for dir in $(PACKAGES); do $(MAKE) -C $$dir; done
|
||||
|
||||
.PHONY: pkgclean
|
||||
pkgclean:
|
||||
for dir in $(PACKAGES); do $(MAKE) -C $$dir clean; done
|
||||
|
||||
.PHONY: clean
|
||||
clean: pkgclean
|
||||
rm -f grumble
|
||||
rm -f *.$(O)
|
||||
5
README
Normal file
5
README
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
What is Grumble?
|
||||
================
|
||||
|
||||
Grumble is an implementation of Murmur (the server component of Mumble, an
|
||||
open-source VoIP application) in the Go language.
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
grumble.crt
Normal file
9
grumble.crt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBNTCB4KADAgECAgQmyMwOMAsGCSqGSIb3DQEBBTASMRAwDgYDVQQDDAdncnVt
|
||||
YmxlMB4XDTEwMDMyNTE1MzE0N1oXDTEwMDQyNDE1MzE0N1owEjEQMA4GA1UEAwwH
|
||||
Z3J1bWJsZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDH0Hy33EgJbHBupPXfoukc
|
||||
b3CRwP891PPYdsDyCXoxWcYYSTC3BNC8/b2u+qtaE2EAcnfMwuNJBTMvn9UCXwwV
|
||||
AgMBAAGjIDAeMAsGA1UdDwQEAwIBtjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
|
||||
DQEBBQUAA0EAFjT5pPcVmXmDghqkZHZQcHsqSnIIH1MbULzNFErPHNabvdOp6gw1
|
||||
ycGs6CI1gdO3EuF+cHvS1ocm9ylWULf2cA==
|
||||
-----END CERTIFICATE-----
|
||||
43
grumble.go
Normal file
43
grumble.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// 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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var help *bool = flag.Bool("help", false, "Show this help")
|
||||
var port *int = flag.Int("port", 64738, "Default port to listen on")
|
||||
var host *string = flag.String("host", "0.0.0.0", "Default host to listen on")
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *help == true {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
|
||||
// Create our default server
|
||||
m, err := NewServer(*host, *port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// And launch it.
|
||||
go m.ListenAndMurmur()
|
||||
|
||||
// Listen forever
|
||||
sleeper := make(chan int)
|
||||
zzz := <-sleeper
|
||||
if zzz > 0 {
|
||||
}
|
||||
}
|
||||
10
grumble.key
Normal file
10
grumble.key
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOgIBAAJBAMfQfLfcSAlscG6k9d+i6RxvcJHA/z3U89h2wPIJejFZxhhJMLcE
|
||||
0Lz9va76q1oTYQByd8zC40kFMy+f1QJfDBUCAwEAAQJBALuKJDTRTM+DdvdyXs96
|
||||
8T5eHhK/SRF4uTHXK/tAB+8eQdSx7D2jsEASX+XSvO5Oc78jFTdG/9EYLW85nv2f
|
||||
yQECIQDnRvppEVbvSVHbOZXTGCH85Raq+triXrQjT8qrQ2RAVQIhAN0sgv6rM8n0
|
||||
enIu7lhgQq4islgskCeagTZmI8V1/FzBAiBQBOPRBHnSssiKlCL9dYUU7eJo6ABh
|
||||
gCjNaucRWHDQPQIgfVwvW116WyuhA3sqSRk2cjDkWSnZAzmFp2m5OMCpK4ECIG93
|
||||
VI6MTx5PJvOg2Nd6+6qbKF4uFNe/n0CHZ3QtDqhK
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
180
message.go
Normal file
180
message.go
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// Grumble - an implementation of Murmur in Go
|
||||
// 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 (
|
||||
"log"
|
||||
"mumbleproto"
|
||||
"goprotobuf.googlecode.com/hg/proto"
|
||||
"net"
|
||||
"container/list"
|
||||
"cryptstate"
|
||||
)
|
||||
|
||||
// These are the different kinds of messages
|
||||
// that are defined for the Mumble protocol
|
||||
const (
|
||||
MessageVersion = iota
|
||||
MessageUDPTunnel
|
||||
MessageAuthenticate
|
||||
MessagePing
|
||||
MessageReject
|
||||
MessageServerSync
|
||||
MessageChannelRemove
|
||||
MessageChannelState
|
||||
MessageUserRemove
|
||||
MessageUserState
|
||||
MessageBanList
|
||||
MessageTextMessage
|
||||
MessagePermissionDenied
|
||||
MessageACL
|
||||
MessageQueryUsers
|
||||
MessageCryptSetup
|
||||
MessageContextActionAdd
|
||||
MessageContextAction
|
||||
MessageUserList
|
||||
MessageVoiceTarget
|
||||
MessagePermissionQuery
|
||||
MessageCodecVersion
|
||||
MessageUserStats
|
||||
MessageRequestBlob
|
||||
MessageServerConfig
|
||||
)
|
||||
|
||||
const (
|
||||
UDPMessageVoiceCELTAlpha = iota
|
||||
UDPMessagePing
|
||||
UDPMessageVoiceSpeex
|
||||
UDPMessageVoiceCELTBeta
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
buf []byte
|
||||
|
||||
// Kind denotes a message kind for TCP packets. This field
|
||||
// is ignored for UDP packets.
|
||||
kind uint16
|
||||
|
||||
// For UDP datagrams one of these fiels have to be filled out.
|
||||
// If there is no connection established, address must be used.
|
||||
// If the datagram comes from an already-connected client, the
|
||||
// client field should point to that client.
|
||||
client *ClientConnection
|
||||
address net.Addr
|
||||
}
|
||||
|
||||
func (server *Server) handleCryptSetup(client *ClientConnection, msg *Message) {
|
||||
cs := &mumbleproto.CryptSetup{}
|
||||
err := proto.Unmarshal(msg.buf, cs)
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
||||
// No client nonce. This means the client
|
||||
// is requesting that we re-sync our nonces.
|
||||
if len(cs.ClientNonce) == 0 {
|
||||
log.Stdoutf("Requested crypt-nonce resync")
|
||||
cs.ClientNonce = make([]byte, cryptstate.AESBlockSize)
|
||||
if copy(cs.ClientNonce, client.crypt.EncryptIV[0:]) != cryptstate.AESBlockSize {
|
||||
return
|
||||
}
|
||||
client.sendProtoMessage(MessageCryptSetup, cs)
|
||||
} else {
|
||||
log.Stdoutf("Received client nonce")
|
||||
if len(cs.ClientNonce) != cryptstate.AESBlockSize {
|
||||
return
|
||||
}
|
||||
|
||||
client.crypt.Resync += 1
|
||||
if copy(client.crypt.DecryptIV[0:], cs.ClientNonce) != cryptstate.AESBlockSize {
|
||||
return
|
||||
}
|
||||
log.Stdoutf("Crypt re-sync successful")
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) handlePingMessage(client *ClientConnection, msg *Message) {
|
||||
ping := &mumbleproto.Ping{}
|
||||
err := proto.Unmarshal(msg.buf, ping)
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Phony response for ping messages. We don't keep stats
|
||||
// for this yet.
|
||||
client.sendProtoMessage(MessagePing, &mumbleproto.Ping{
|
||||
Timestamp: ping.Timestamp,
|
||||
Good: proto.Uint32(uint32(client.crypt.Good)),
|
||||
Late: proto.Uint32(uint32(client.crypt.Late)),
|
||||
Lost: proto.Uint32(uint32(client.crypt.Lost)),
|
||||
Resync: proto.Uint32(uint32(client.crypt.Resync)),
|
||||
})
|
||||
}
|
||||
|
||||
func (server *Server) handleChannelAddMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
func (server *Server) handleChannelRemoveMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
func (server *Server) handleChannelStateMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
func (server *Server) handleUserRemoveMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
func (server *Server) handleUserStateMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
func (server *Server) handleBanListMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
func (server *Server) handleTextMessage(client *ClientConnection, msg *Message) {
|
||||
txtmsg := &mumbleproto.TextMessage{}
|
||||
err := proto.Unmarshal(msg.buf, txtmsg)
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Receiver list...
|
||||
users := list.New()
|
||||
|
||||
for i := 0; i < len(txtmsg.Session); i++ {
|
||||
// Lookup user by ID
|
||||
user := server.getClientConnection(txtmsg.Session[i])
|
||||
if user != nil {
|
||||
users.PushBack(user)
|
||||
}
|
||||
}
|
||||
|
||||
for x := range users.Iter() {
|
||||
user := x.(*ClientConnection)
|
||||
user.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{
|
||||
Actor: proto.Uint32(client.Session),
|
||||
Message: txtmsg.Message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) handleAclMessage(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
// User query
|
||||
func (server *Server) handleQueryUsers(client *ClientConnection, msg *Message) {
|
||||
}
|
||||
|
||||
// User stats message. Shown in the Mumble client when a
|
||||
// user right clicks a user and selects 'User Information'.
|
||||
func (server *Server) handleUserStatsMessage(client *ClientConnection, msg *Message) {
|
||||
stats := &mumbleproto.UserStats{}
|
||||
err := proto.Unmarshal(msg.buf, stats)
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
}
|
||||
6
pkg/cryptstate/Makefile
Normal file
6
pkg/cryptstate/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG = cryptstate
|
||||
GOFILES = cryptstate.go
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
360
pkg/cryptstate/cryptstate.go
Normal file
360
pkg/cryptstate/cryptstate.go
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
// Grumble - an implementation of Murmur in Go
|
||||
// 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 cryptstate
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/rand"
|
||||
"os"
|
||||
)
|
||||
|
||||
const AESBlockSize = 16
|
||||
const DecryptHistorySize = 0x100
|
||||
|
||||
type CryptState struct {
|
||||
RawKey [AESBlockSize]byte
|
||||
EncryptIV [AESBlockSize]byte
|
||||
DecryptIV [AESBlockSize]byte
|
||||
decryptHistory [DecryptHistorySize]byte
|
||||
|
||||
Good int
|
||||
Late int
|
||||
Lost int
|
||||
Resync int
|
||||
|
||||
RemoteGood int
|
||||
RemoteLate int
|
||||
RemoteLost int
|
||||
RemoteResync int
|
||||
|
||||
cipher *aes.Cipher
|
||||
}
|
||||
|
||||
func New() (cs *CryptState, err os.Error) {
|
||||
cs = new(CryptState)
|
||||
|
||||
for i := 0; i < DecryptHistorySize; i++ {
|
||||
cs.decryptHistory[i] = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *CryptState) GenerateKey() (err os.Error) {
|
||||
rand.Read(cs.RawKey[0:])
|
||||
rand.Read(cs.EncryptIV[0:])
|
||||
rand.Read(cs.DecryptIV[0:])
|
||||
|
||||
cs.cipher, err = aes.NewCipher(cs.RawKey[0:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *CryptState) SetKey(key []byte, eiv []byte, div[]byte) (err os.Error) {
|
||||
if copy(cs.RawKey[0:], key[0:]) != AESBlockSize {
|
||||
err = os.NewError("Unable to copy key")
|
||||
return
|
||||
}
|
||||
|
||||
if copy(cs.EncryptIV[0:], eiv[0:]) != AESBlockSize {
|
||||
err = os.NewError("Unable to copy EIV")
|
||||
return
|
||||
}
|
||||
|
||||
if copy(cs.DecryptIV[0:], div[0:]) != AESBlockSize {
|
||||
err = os.NewError("Unable to copy DIV")
|
||||
return
|
||||
}
|
||||
|
||||
cs.cipher, err = aes.NewCipher(cs.RawKey[0:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *CryptState) Decrypt(src, dst []byte) (err os.Error) {
|
||||
if len(src) < 4 {
|
||||
err = os.NewError("Crypted length too short to decrypt")
|
||||
return
|
||||
}
|
||||
|
||||
plain_len := len(src) - 4
|
||||
if len(dst) != plain_len {
|
||||
err = os.NewError("plain_len and src len mismatch")
|
||||
return
|
||||
}
|
||||
|
||||
var saveiv [AESBlockSize]byte
|
||||
var tag [AESBlockSize]byte
|
||||
var ivbyte byte
|
||||
var restore bool
|
||||
lost := 0
|
||||
late := 0
|
||||
|
||||
ivbyte = src[0]
|
||||
restore = false
|
||||
|
||||
if copy(saveiv[0:], cs.DecryptIV[0:]) != AESBlockSize {
|
||||
err = os.NewError("Copy failed")
|
||||
return
|
||||
}
|
||||
|
||||
if byte(cs.DecryptIV[0] + 1) == ivbyte {
|
||||
// In order as expected
|
||||
if ivbyte > cs.DecryptIV[0] {
|
||||
cs.DecryptIV[0] = ivbyte
|
||||
} else if ivbyte < cs.DecryptIV[0] {
|
||||
cs.DecryptIV[0] = ivbyte
|
||||
for i := 1; i < AESBlockSize; i++ {
|
||||
cs.DecryptIV[i] += 1
|
||||
if cs.DecryptIV[i] > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = os.NewError("invalid ivbyte")
|
||||
}
|
||||
} else {
|
||||
// Out of order or repeat
|
||||
var diff int
|
||||
diff = int(ivbyte - cs.DecryptIV[0])
|
||||
if diff > 128 {
|
||||
diff = diff - 256
|
||||
} else if diff < -128 {
|
||||
diff = diff + 256
|
||||
}
|
||||
|
||||
if ivbyte < cs.DecryptIV[0] && diff > -30 && diff < 0 {
|
||||
// Late packet, but no wraparound
|
||||
late = 1
|
||||
lost = -1
|
||||
cs.DecryptIV[0] = ivbyte
|
||||
restore = true
|
||||
} else if ivbyte > cs.DecryptIV[0] && diff > -30 && diff < 0 {
|
||||
// Last was 0x02, here comes 0xff from last round
|
||||
late = 1
|
||||
lost = -1
|
||||
cs.DecryptIV[0] = ivbyte
|
||||
for i := 1; i < AESBlockSize; i++ {
|
||||
cs.DecryptIV[0] -= 1
|
||||
if cs.DecryptIV[0] > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
restore = true
|
||||
} else if ivbyte > cs.DecryptIV[0] && diff > 0 {
|
||||
// Lost a few packets, but beyond that we're good.
|
||||
lost = int(ivbyte - cs.DecryptIV[0] - 1)
|
||||
cs.DecryptIV[0] = ivbyte
|
||||
} else if ivbyte < cs.DecryptIV[0] && diff > 0 {
|
||||
// Lost a few packets, and wrapped around
|
||||
lost = int(256 - int(cs.DecryptIV[0]) + int(ivbyte) - 1)
|
||||
cs.DecryptIV[0] = ivbyte
|
||||
for i := 1; i < AESBlockSize; i++ {
|
||||
cs.DecryptIV[0] += 1
|
||||
if cs.DecryptIV[0] > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = os.NewError("No matching ivbyte")
|
||||
return
|
||||
}
|
||||
|
||||
if cs.decryptHistory[cs.DecryptIV[0]] == cs.DecryptIV[0] {
|
||||
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
|
||||
err = os.NewError("Failed to copy AESBlockSize bytes")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cs.OCBDecrypt(src[4:], dst[0:], cs.DecryptIV[0:], tag[0:])
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
if tag[i] != src[i+1] {
|
||||
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
|
||||
err = os.NewError("Error while trying to recover from error")
|
||||
return
|
||||
}
|
||||
err = os.NewError("tag mismatch")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cs.decryptHistory[cs.DecryptIV[0]] = cs.DecryptIV[0]
|
||||
|
||||
if restore {
|
||||
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
|
||||
err = os.NewError("Error while trying to recover IV")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cs.Good += 1
|
||||
cs.Late += late
|
||||
cs.Lost += lost
|
||||
|
||||
// restart timer
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *CryptState) Encrypt(src, dst []byte) {
|
||||
var tag [AESBlockSize]byte
|
||||
|
||||
// First, increase our IV
|
||||
for i := 0; i < AESBlockSize; i++ {
|
||||
cs.EncryptIV[i] += 1;
|
||||
if cs.EncryptIV[i] > 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cs.OCBEncrypt(src, dst[4:], cs.EncryptIV[0:], tag[0:])
|
||||
|
||||
dst[0] = cs.EncryptIV[0]
|
||||
dst[1] = tag[0];
|
||||
dst[2] = tag[1];
|
||||
dst[3] = tag[2];
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func zeros(block []byte) {
|
||||
for i := 0; i < AESBlockSize; i++ {
|
||||
block[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func xor(dst []byte, a []byte, b []byte) {
|
||||
for i := 0; i < AESBlockSize; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
}
|
||||
|
||||
func times2(block []byte) {
|
||||
carry := (block[0] >> 7) & 0x1
|
||||
for i := 0; i < AESBlockSize-1; i++ {
|
||||
block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
|
||||
}
|
||||
block[AESBlockSize-1] = (block[AESBlockSize-1] << 1) ^ (carry * 135)
|
||||
}
|
||||
|
||||
func times3(block []byte) {
|
||||
carry := (block[0] >> 7) & 0x1;
|
||||
for i := 0; i < AESBlockSize-1; i++ {
|
||||
block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
|
||||
}
|
||||
block[AESBlockSize-1] ^= ((block[AESBlockSize-1] << 1) ^ (carry * 135))
|
||||
}
|
||||
|
||||
func (cs *CryptState) OCBEncrypt(src []byte, dst []byte, nonce []byte, tag []byte) (err os.Error) {
|
||||
var delta [AESBlockSize]byte
|
||||
var checksum [AESBlockSize]byte
|
||||
var tmp [AESBlockSize]byte
|
||||
var pad [AESBlockSize]byte
|
||||
off := 0
|
||||
|
||||
cs.cipher.Encrypt(cs.EncryptIV[0:], delta[0:])
|
||||
zeros(checksum[0:])
|
||||
|
||||
remain := len(src)
|
||||
for remain > AESBlockSize {
|
||||
times2(delta[0:])
|
||||
xor(tmp[0:], delta[0:], src[off:off+AESBlockSize])
|
||||
cs.cipher.Encrypt(tmp[0:], tmp[0:])
|
||||
xor(dst[off:off+AESBlockSize], delta[0:], tmp[0:])
|
||||
xor(checksum[0:], checksum[0:], src[off:off+AESBlockSize])
|
||||
remain -= AESBlockSize
|
||||
off += AESBlockSize
|
||||
}
|
||||
|
||||
times2(delta[0:])
|
||||
zeros(tmp[0:])
|
||||
num := remain * 8
|
||||
tmp[AESBlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
|
||||
tmp[AESBlockSize-1] = uint8(num & 0xff)
|
||||
xor(tmp[0:], tmp[0:], delta[0:])
|
||||
cs.cipher.Encrypt(tmp[0:], pad[0:])
|
||||
copied := copy(tmp[0:], src[off:])
|
||||
if copied != remain {
|
||||
err = os.NewError("Copy failed")
|
||||
return
|
||||
}
|
||||
if copy(tmp[copied:], pad[copied:]) != (AESBlockSize-remain) {
|
||||
err = os.NewError("Copy failed")
|
||||
return
|
||||
}
|
||||
xor(checksum[0:], checksum[0:], tmp[0:])
|
||||
xor(tmp[0:], pad[0:], tmp[0:])
|
||||
if copy(dst[off:], tmp[0:]) != remain {
|
||||
err = os.NewError("Copy failed")
|
||||
return
|
||||
}
|
||||
|
||||
times3(delta[0:])
|
||||
xor(tmp[0:], delta[0:], checksum[0:])
|
||||
cs.cipher.Encrypt(tmp[0:], tag[0:])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *CryptState) OCBDecrypt(encrypted []byte, plain []byte, nonce []byte, tag []byte) (err os.Error) {
|
||||
var checksum [AESBlockSize]byte
|
||||
var delta [AESBlockSize]byte
|
||||
var tmp [AESBlockSize]byte
|
||||
var pad [AESBlockSize]byte
|
||||
off := 0
|
||||
|
||||
cs.cipher.Encrypt(nonce[0:], delta[0:])
|
||||
zeros(checksum[0:])
|
||||
|
||||
remain := len(encrypted)
|
||||
for remain > AESBlockSize {
|
||||
times2(delta[0:])
|
||||
xor(tmp[0:], delta[0:], encrypted[off:off+AESBlockSize])
|
||||
cs.cipher.Decrypt(tmp[0:], tmp[0:])
|
||||
xor(plain[off:off+AESBlockSize], delta[0:], tmp[0:])
|
||||
xor(checksum[0:], checksum[0:], plain[off:off+AESBlockSize])
|
||||
off += AESBlockSize
|
||||
remain -= AESBlockSize
|
||||
}
|
||||
|
||||
times2(delta[0:])
|
||||
zeros(tmp[0:])
|
||||
num := remain * 8
|
||||
tmp[AESBlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
|
||||
tmp[AESBlockSize-1] = uint8(num & 0xff)
|
||||
xor(tmp[0:], tmp[0:], delta[0:])
|
||||
cs.cipher.Encrypt(tmp[0:], pad[0:])
|
||||
for i := 0; i < AESBlockSize; i++ {
|
||||
tmp[i] = 0
|
||||
}
|
||||
copied := copy(tmp[0:remain], encrypted[off:off+remain])
|
||||
if copied != remain {
|
||||
err = os.NewError("Copy failed")
|
||||
return
|
||||
}
|
||||
xor(tmp[0:], tmp[0:], pad[0:])
|
||||
xor(checksum[0:], checksum[0:], tmp[0:])
|
||||
copied = copy(plain[off:off+remain], tmp[0:remain])
|
||||
if copied != remain {
|
||||
err = os.NewError("Copy failed")
|
||||
return
|
||||
}
|
||||
|
||||
times3(delta[0:])
|
||||
xor(tmp[0:], delta[0:], checksum[0:])
|
||||
cs.cipher.Encrypt(tmp[0:], tag[0:])
|
||||
|
||||
return
|
||||
}
|
||||
147
pkg/cryptstate/cryptstate_test.go
Normal file
147
pkg/cryptstate/cryptstate_test.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
package cryptstate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BlockCompare(a []byte, b []byte) (match bool) {
|
||||
if len(a) != len(b) {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
match = true
|
||||
return
|
||||
}
|
||||
|
||||
func TestTimes2(t *testing.T) {
|
||||
msg := [AESBlockSize]byte{
|
||||
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
}
|
||||
expected := [AESBlockSize]byte{
|
||||
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b,
|
||||
}
|
||||
|
||||
times2(msg[0:])
|
||||
if BlockCompare(msg[0:], expected[0:]) == false {
|
||||
t.Errorf("times2 produces invalid output: %v, expected: %v", msg, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimes3(t *testing.T) {
|
||||
msg := [AESBlockSize]byte{
|
||||
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
}
|
||||
expected := [AESBlockSize]byte {
|
||||
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
|
||||
}
|
||||
|
||||
times3(msg[0:])
|
||||
if BlockCompare(msg[0:], expected[0:]) == false {
|
||||
t.Errorf("times3 produces invalid output: %v, expected: %v", msg, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeros(t *testing.T) {
|
||||
var msg [AESBlockSize]byte
|
||||
zeros(msg[0:])
|
||||
for i := 0; i < len(msg); i++ {
|
||||
if msg[i] != 0 {
|
||||
t.Errorf("zeros does not zero slice.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestXor(t *testing.T) {
|
||||
msg := [AESBlockSize]byte{
|
||||
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
}
|
||||
var out [AESBlockSize]byte
|
||||
xor(out[0:], msg[0:], msg[0:])
|
||||
for i := 0; i < len(out); i++ {
|
||||
if out[i] != 0 {
|
||||
t.Errorf("XOR broken")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
msg := [15]byte {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
}
|
||||
key := [AESBlockSize]byte {
|
||||
0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32,
|
||||
}
|
||||
eiv := [AESBlockSize]byte {
|
||||
0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
|
||||
}
|
||||
div := [AESBlockSize]byte {
|
||||
0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07,
|
||||
}
|
||||
expected := [19]byte {
|
||||
0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4,
|
||||
}
|
||||
expected_eiv := [AESBlockSize]byte {
|
||||
0x1f, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
|
||||
}
|
||||
|
||||
cs, err := New()
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
out := make([]byte, 19)
|
||||
cs.SetKey(key[0:], eiv[0:], div[0:])
|
||||
cs.Encrypt(msg[0:], out[0:])
|
||||
|
||||
if BlockCompare(out[0:], expected[0:]) == false {
|
||||
t.Errorf("Mismatch in output")
|
||||
}
|
||||
|
||||
if BlockCompare(cs.EncryptIV[0:], expected_eiv[0:]) == false {
|
||||
t.Errorf("EIV mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
key := [AESBlockSize]byte {
|
||||
0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32,
|
||||
}
|
||||
eiv := [AESBlockSize]byte {
|
||||
0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
|
||||
}
|
||||
div := [AESBlockSize]byte {
|
||||
0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07,
|
||||
}
|
||||
crypted := [19]byte {
|
||||
0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4,
|
||||
}
|
||||
expected := [15]byte {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
}
|
||||
post_div := [AESBlockSize]byte {
|
||||
0x1f, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
|
||||
}
|
||||
|
||||
cs, err := New()
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
out := make([]byte, 15)
|
||||
cs.SetKey(key[0:], div[0:], eiv[0:])
|
||||
cs.Decrypt(crypted[0:], out[0:])
|
||||
|
||||
if BlockCompare(out[0:], expected[0:]) == false {
|
||||
t.Errorf("Mismatch in output")
|
||||
}
|
||||
|
||||
if BlockCompare(cs.DecryptIV[0:], post_div[0:]) == false {
|
||||
t.Errorf("Mismatch in DIV")
|
||||
}
|
||||
}
|
||||
322
pkg/cryptstate/testgen/CryptState.cpp
Normal file
322
pkg/cryptstate/testgen/CryptState.cpp
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
- Neither the name of the Mumble Developers nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements OCB-AES128.
|
||||
* In the US, OCB is covered by patents. The inventor has given a license
|
||||
* to all programs distributed under the GPL.
|
||||
* Mumble is BSD (revised) licensed, meaning you can use the code in a
|
||||
* closed-source program. If you do, you'll have to either replace
|
||||
* OCB with something else or get yourself a license.
|
||||
*/
|
||||
|
||||
#include "CryptState.h"
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace MumbleClient {
|
||||
|
||||
CryptState::CryptState() {
|
||||
for (int i = 0; i < 0x100; i++)
|
||||
decrypt_history[i] = 0;
|
||||
|
||||
bInit = false;
|
||||
uiGood = uiLate = uiLost = uiResync = 0;
|
||||
uiRemoteGood = uiRemoteLate = uiRemoteLost = uiRemoteResync = 0;
|
||||
}
|
||||
|
||||
bool CryptState::isValid() const {
|
||||
return bInit;
|
||||
}
|
||||
|
||||
void CryptState::genKey() {
|
||||
RAND_bytes(raw_key, AES_BLOCK_SIZE);
|
||||
RAND_bytes(encrypt_iv, AES_BLOCK_SIZE);
|
||||
RAND_bytes(decrypt_iv, AES_BLOCK_SIZE);
|
||||
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
|
||||
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
|
||||
bInit = true;
|
||||
}
|
||||
|
||||
void CryptState::setKey(const unsigned char* rkey, const unsigned char* eiv, const unsigned char* div) {
|
||||
memcpy(raw_key, rkey, AES_BLOCK_SIZE);
|
||||
memcpy(encrypt_iv, eiv, AES_BLOCK_SIZE);
|
||||
memcpy(decrypt_iv, div, AES_BLOCK_SIZE);
|
||||
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
|
||||
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
|
||||
bInit = true;
|
||||
}
|
||||
|
||||
void CryptState::setDecryptIV(const unsigned char* iv) {
|
||||
memcpy(decrypt_iv, iv, AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
const unsigned char* CryptState::getEncryptIV() const {
|
||||
return encrypt_iv;
|
||||
}
|
||||
|
||||
void CryptState::encrypt(const unsigned char* source, unsigned char* dst, unsigned int plain_length) {
|
||||
unsigned char tag[AES_BLOCK_SIZE];
|
||||
|
||||
// First, increase our IV.
|
||||
for (int i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
if (++encrypt_iv[i])
|
||||
break;
|
||||
|
||||
ocb_encrypt(source, dst+4, plain_length, encrypt_iv, tag);
|
||||
|
||||
dst[0] = encrypt_iv[0];
|
||||
dst[1] = tag[0];
|
||||
dst[2] = tag[1];
|
||||
dst[3] = tag[2];
|
||||
}
|
||||
|
||||
bool CryptState::decrypt(const unsigned char* source, unsigned char* dst, unsigned int crypted_length) {
|
||||
if (crypted_length < 4)
|
||||
return false;
|
||||
|
||||
unsigned int plain_length = crypted_length - 4;
|
||||
|
||||
unsigned char saveiv[AES_BLOCK_SIZE];
|
||||
unsigned char ivbyte = source[0];
|
||||
bool restore = false;
|
||||
unsigned char tag[AES_BLOCK_SIZE];
|
||||
|
||||
int lost = 0;
|
||||
int late = 0;
|
||||
|
||||
memcpy(saveiv, decrypt_iv, AES_BLOCK_SIZE);
|
||||
|
||||
if (((decrypt_iv[0] + 1) & 0xFF) == ivbyte) {
|
||||
// In order as expected.
|
||||
if (ivbyte > decrypt_iv[0]) {
|
||||
decrypt_iv[0] = ivbyte;
|
||||
} else if (ivbyte < decrypt_iv[0]) {
|
||||
decrypt_iv[0] = ivbyte;
|
||||
for (int i = 1;i < AES_BLOCK_SIZE; i++)
|
||||
if (++decrypt_iv[i])
|
||||
break;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// This is either out of order or a repeat.
|
||||
|
||||
int diff = ivbyte - decrypt_iv[0];
|
||||
if (diff > 128)
|
||||
diff = diff-256;
|
||||
else if (diff < -128)
|
||||
diff = diff+256;
|
||||
|
||||
if ((ivbyte < decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
|
||||
// Late packet, but no wraparound.
|
||||
late = 1;
|
||||
lost = -1;
|
||||
decrypt_iv[0] = ivbyte;
|
||||
restore = true;
|
||||
} else if ((ivbyte > decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
|
||||
// Last was 0x02, here comes 0xff from last round
|
||||
late = 1;
|
||||
lost = -1;
|
||||
decrypt_iv[0] = ivbyte;
|
||||
for (int i = 1; i < AES_BLOCK_SIZE; i++)
|
||||
if (decrypt_iv[i]--)
|
||||
break;
|
||||
restore = true;
|
||||
} else if ((ivbyte > decrypt_iv[0]) && (diff > 0)) {
|
||||
// Lost a few packets, but beyond that we're good.
|
||||
lost = ivbyte - decrypt_iv[0] - 1;
|
||||
decrypt_iv[0] = ivbyte;
|
||||
} else if ((ivbyte < decrypt_iv[0]) && (diff > 0)) {
|
||||
// Lost a few packets, and wrapped around
|
||||
lost = 256 - decrypt_iv[0] + ivbyte - 1;
|
||||
decrypt_iv[0] = ivbyte;
|
||||
for (int i = 1; i < AES_BLOCK_SIZE; i++)
|
||||
if (++decrypt_iv[i])
|
||||
break;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decrypt_history[decrypt_iv[0]] == decrypt_iv[1]) {
|
||||
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ocb_decrypt(source + 4, dst, plain_length, decrypt_iv, tag);
|
||||
|
||||
if (memcmp(tag, source + 1, 3) != 0) {
|
||||
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
|
||||
return false;
|
||||
}
|
||||
decrypt_history[decrypt_iv[0]] = decrypt_iv[1];
|
||||
|
||||
if (restore)
|
||||
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
|
||||
|
||||
uiGood++;
|
||||
uiLate += late;
|
||||
uiLost += lost;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__LP64__)
|
||||
|
||||
#define BLOCKSIZE 2
|
||||
#define SHIFTBITS 63
|
||||
typedef uint64_t subblock;
|
||||
|
||||
#ifdef __x86_64__
|
||||
static inline uint64_t SWAP64(register uint64_t __in) { register uint64_t __out; __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); return __out; }
|
||||
#else
|
||||
#define SWAP64(x) ((static_cast<uint64_t>(x) << 56) | \
|
||||
((static_cast<uint64_t>(x) << 40) & 0xff000000000000ULL) | \
|
||||
((static_cast<uint64_t>(x) << 24) & 0xff0000000000ULL) | \
|
||||
((static_cast<uint64_t>(x) << 8) & 0xff00000000ULL) | \
|
||||
((static_cast<uint64_t>(x) >> 8) & 0xff000000ULL) | \
|
||||
((static_cast<uint64_t>(x) >> 24) & 0xff0000ULL) | \
|
||||
((static_cast<uint64_t>(x) >> 40) & 0xff00ULL) | \
|
||||
((static_cast<uint64_t>(x) >> 56)))
|
||||
#endif
|
||||
|
||||
#define SWAPPED(x) SWAP64(x)
|
||||
|
||||
#else
|
||||
#define BLOCKSIZE 4
|
||||
#define SHIFTBITS 31
|
||||
typedef uint32_t subblock;
|
||||
#define SWAPPED(x) htonl(x)
|
||||
#endif
|
||||
|
||||
typedef subblock keyblock[BLOCKSIZE];
|
||||
|
||||
#define HIGHBIT (1<<SHIFTBITS);
|
||||
|
||||
|
||||
static void inline XOR(subblock* dst, const subblock* a, const subblock* b) {
|
||||
for (int i = 0; i < BLOCKSIZE; i++)
|
||||
dst[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
static void inline S2(subblock* block) {
|
||||
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
|
||||
for (int i = 0; i < BLOCKSIZE - 1; i++)
|
||||
block[i] = SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
|
||||
block[BLOCKSIZE - 1] = SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^(carry * 0x87));
|
||||
}
|
||||
|
||||
static void inline S3(subblock* block) {
|
||||
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
|
||||
for (int i = 0; i < BLOCKSIZE - 1; i++)
|
||||
block[i] ^= SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
|
||||
block[BLOCKSIZE - 1] ^= SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^(carry * 0x87));
|
||||
}
|
||||
|
||||
static void inline ZERO(keyblock &block) {
|
||||
for (int i = 0; i < BLOCKSIZE; i++)
|
||||
block[i] = 0;
|
||||
}
|
||||
|
||||
#define AESencrypt(src,dst,key) AES_encrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
|
||||
#define AESdecrypt(src,dst,key) AES_decrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
|
||||
|
||||
void CryptState::ocb_encrypt(const unsigned char* plain, unsigned char* encrypted, unsigned int len, const unsigned char* nonce, unsigned char* tag) {
|
||||
keyblock checksum, delta, tmp, pad;
|
||||
|
||||
// Initialize
|
||||
AESencrypt(nonce, delta, &encrypt_key);
|
||||
ZERO(checksum);
|
||||
|
||||
while (len > AES_BLOCK_SIZE) {
|
||||
S2(delta);
|
||||
XOR(tmp, delta, reinterpret_cast<const subblock *>(plain));
|
||||
AESencrypt(tmp, tmp, &encrypt_key);
|
||||
XOR(reinterpret_cast<subblock *>(encrypted), delta, tmp);
|
||||
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
|
||||
len -= AES_BLOCK_SIZE;
|
||||
plain += AES_BLOCK_SIZE;
|
||||
encrypted += AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
S2(delta);
|
||||
ZERO(tmp);
|
||||
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
|
||||
XOR(tmp, tmp, delta);
|
||||
AESencrypt(tmp, pad, &encrypt_key);
|
||||
memcpy(tmp, plain, len);
|
||||
memcpy(reinterpret_cast<unsigned char *>(tmp) + len, reinterpret_cast<const unsigned char *>(pad) + len, AES_BLOCK_SIZE - len);
|
||||
XOR(checksum, checksum, tmp);
|
||||
XOR(tmp, pad, tmp);
|
||||
memcpy(encrypted, tmp, len);
|
||||
|
||||
S3(delta);
|
||||
XOR(tmp, delta, checksum);
|
||||
AESencrypt(tmp, tag, &encrypt_key);
|
||||
}
|
||||
|
||||
void CryptState::ocb_decrypt(const unsigned char* encrypted, unsigned char* plain, unsigned int len, const unsigned char* nonce, unsigned char* tag) {
|
||||
keyblock checksum, delta, tmp, pad;
|
||||
|
||||
// Initialize
|
||||
AESencrypt(nonce, delta, &encrypt_key);
|
||||
ZERO(checksum);
|
||||
|
||||
while (len > AES_BLOCK_SIZE) {
|
||||
S2(delta);
|
||||
XOR(tmp, delta, reinterpret_cast<const subblock *>(encrypted));
|
||||
AESdecrypt(tmp, tmp, &decrypt_key);
|
||||
XOR(reinterpret_cast<subblock *>(plain), delta, tmp);
|
||||
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
|
||||
len -= AES_BLOCK_SIZE;
|
||||
plain += AES_BLOCK_SIZE;
|
||||
encrypted += AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
S2(delta);
|
||||
ZERO(tmp);
|
||||
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
|
||||
XOR(tmp, tmp, delta);
|
||||
AESencrypt(tmp, pad, &encrypt_key);
|
||||
memset(tmp, 0, AES_BLOCK_SIZE);
|
||||
memcpy(tmp, encrypted, len);
|
||||
XOR(tmp, tmp, pad);
|
||||
XOR(checksum, checksum, tmp);
|
||||
memcpy(plain, tmp, len);
|
||||
|
||||
S3(delta);
|
||||
XOR(tmp, delta, checksum);
|
||||
AESencrypt(tmp, tag, &encrypt_key);
|
||||
}
|
||||
|
||||
} // end namespace MumbleClient
|
||||
77
pkg/cryptstate/testgen/CryptState.h
Normal file
77
pkg/cryptstate/testgen/CryptState.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
- Neither the name of the Mumble Developers nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTSTATE_H
|
||||
#define _CRYPTSTATE_H
|
||||
|
||||
#include <openssl/aes.h>
|
||||
|
||||
namespace MumbleClient {
|
||||
|
||||
class CryptState {
|
||||
public:
|
||||
unsigned char raw_key[AES_BLOCK_SIZE];
|
||||
unsigned char encrypt_iv[AES_BLOCK_SIZE];
|
||||
unsigned char decrypt_iv[AES_BLOCK_SIZE];
|
||||
unsigned char decrypt_history[0x100];
|
||||
|
||||
unsigned int uiGood;
|
||||
unsigned int uiLate;
|
||||
unsigned int uiLost;
|
||||
unsigned int uiResync;
|
||||
|
||||
unsigned int uiRemoteGood;
|
||||
unsigned int uiRemoteLate;
|
||||
unsigned int uiRemoteLost;
|
||||
unsigned int uiRemoteResync;
|
||||
|
||||
AES_KEY encrypt_key;
|
||||
AES_KEY decrypt_key;
|
||||
bool bInit;
|
||||
|
||||
public:
|
||||
CryptState();
|
||||
|
||||
bool isValid() const;
|
||||
void genKey();
|
||||
void setKey(const unsigned char* rkey, const unsigned char* eiv, const unsigned char* div);
|
||||
void setDecryptIV(const unsigned char* iv);
|
||||
const unsigned char* getEncryptIV() const;
|
||||
|
||||
void ocb_encrypt(const unsigned char* plain, unsigned char* encrypted, unsigned int len, const unsigned char* nonce, unsigned char* tag);
|
||||
void ocb_decrypt(const unsigned char* encrypted, unsigned char* plain, unsigned int len, const unsigned char* nonce, unsigned char* tag);
|
||||
|
||||
bool decrypt(const unsigned char* source, unsigned char* dst, unsigned int crypted_length);
|
||||
void encrypt(const unsigned char* source, unsigned char* dst, unsigned int plain_length);
|
||||
};
|
||||
|
||||
} // end namespace MumbleClient
|
||||
|
||||
#endif
|
||||
9
pkg/cryptstate/testgen/Makefile
Normal file
9
pkg/cryptstate/testgen/Makefile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.PHONY: default
|
||||
default:
|
||||
g++ test.cpp CryptState.cpp -o test -lcrypto
|
||||
g++ test2.cpp CryptState.cpp -o test2 -lcrypto
|
||||
g++ test3.cpp CryptState.cpp -o test3 -lcrypto
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f test test2 test3
|
||||
1
pkg/cryptstate/testgen/README
Normal file
1
pkg/cryptstate/testgen/README
Normal file
|
|
@ -0,0 +1 @@
|
|||
C++ code for generating some of the test vectors used in cryptstate_test.go
|
||||
28
pkg/cryptstate/testgen/test.cpp
Normal file
28
pkg/cryptstate/testgen/test.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "CryptState.h"
|
||||
#include <stdio.h>
|
||||
|
||||
unsigned char msg[] = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
};
|
||||
|
||||
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
|
||||
printf("unsigned char %s[] = { ", name);
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf("0x%.2x, ", bytes[i]);
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
MumbleClient::CryptState cs;
|
||||
cs.genKey();
|
||||
|
||||
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
|
||||
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
|
||||
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
|
||||
|
||||
unsigned char buf[19];
|
||||
cs.encrypt(msg, &buf[0], 15);
|
||||
|
||||
DumpBytes(buf, 19, "crypted");
|
||||
}
|
||||
36
pkg/cryptstate/testgen/test2.cpp
Normal file
36
pkg/cryptstate/testgen/test2.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#include "CryptState.h"
|
||||
#include <stdio.h>
|
||||
|
||||
unsigned char msg[] = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
};
|
||||
|
||||
unsigned char rawkey[] = { 0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32, };
|
||||
unsigned char encrypt_iv[] = { 0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a, };
|
||||
unsigned char decrypt_iv[] = { 0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07, };
|
||||
unsigned char crypted[] = { 0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4, };
|
||||
|
||||
|
||||
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
|
||||
printf("unsigned char %s[] = { ", name);
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf("0x%.2x, ", bytes[i]);
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
MumbleClient::CryptState cs;
|
||||
// cs.genKey();
|
||||
cs.setKey(rawkey, encrypt_iv, decrypt_iv);
|
||||
|
||||
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
|
||||
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
|
||||
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
|
||||
|
||||
unsigned char buf[19];
|
||||
cs.encrypt(msg, &buf[0], 15);
|
||||
|
||||
DumpBytes(buf, 19, "crypted");
|
||||
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "post_eiv");
|
||||
}
|
||||
34
pkg/cryptstate/testgen/test3.cpp
Normal file
34
pkg/cryptstate/testgen/test3.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#include "CryptState.h"
|
||||
#include <stdio.h>
|
||||
|
||||
unsigned char msg[] = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
};
|
||||
|
||||
unsigned char rawkey[] = { 0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32, };
|
||||
unsigned char encrypt_iv[] = { 0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a, };
|
||||
unsigned char decrypt_iv[] = { 0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07, };
|
||||
unsigned char crypted[] = { 0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4, };
|
||||
|
||||
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
|
||||
printf("unsigned char %s[] = { ", name);
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf("0x%.2x, ", bytes[i]);
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
MumbleClient::CryptState cs;
|
||||
cs.setKey(rawkey, decrypt_iv, encrypt_iv);
|
||||
|
||||
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
|
||||
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
|
||||
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
|
||||
|
||||
unsigned char buf[15];
|
||||
cs.decrypt(crypted, &buf[0], 19);
|
||||
|
||||
DumpBytes(buf, 15, "plain");
|
||||
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "post_div");
|
||||
}
|
||||
7
pkg/mumbleproto/Makefile
Normal file
7
pkg/mumbleproto/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG = mumbleproto
|
||||
GOFILES = Mumble.pb.go
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
include $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/Make.protobuf
|
||||
502
pkg/mumbleproto/Mumble.pb.go
Normal file
502
pkg/mumbleproto/Mumble.pb.go
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
// Code generated by protoc-gen-go from "Mumble.proto"
|
||||
// DO NOT EDIT!
|
||||
|
||||
package mumbleproto
|
||||
|
||||
import proto "goprotobuf.googlecode.com/hg/proto"
|
||||
|
||||
// Reference proto import to suppress error if it's not otherwise used.
|
||||
var _ = proto.GetString
|
||||
|
||||
type Reject_RejectType int32
|
||||
const (
|
||||
Reject_None = 0
|
||||
Reject_WrongVersion = 1
|
||||
Reject_InvalidUsername = 2
|
||||
Reject_WrongUserPW = 3
|
||||
Reject_WrongServerPW = 4
|
||||
Reject_UsernameInUse = 5
|
||||
Reject_ServerFull = 6
|
||||
Reject_NoCertificate = 7
|
||||
)
|
||||
var Reject_RejectType_name = map[int32] string {
|
||||
0: "None",
|
||||
1: "WrongVersion",
|
||||
2: "InvalidUsername",
|
||||
3: "WrongUserPW",
|
||||
4: "WrongServerPW",
|
||||
5: "UsernameInUse",
|
||||
6: "ServerFull",
|
||||
7: "NoCertificate",
|
||||
}
|
||||
var Reject_RejectType_value = map[string] int32 {
|
||||
"None": 0,
|
||||
"WrongVersion": 1,
|
||||
"InvalidUsername": 2,
|
||||
"WrongUserPW": 3,
|
||||
"WrongServerPW": 4,
|
||||
"UsernameInUse": 5,
|
||||
"ServerFull": 6,
|
||||
"NoCertificate": 7,
|
||||
}
|
||||
func NewReject_RejectType(x int32) *Reject_RejectType {
|
||||
e := Reject_RejectType(x)
|
||||
return &e
|
||||
}
|
||||
|
||||
type PermissionDenied_DenyType int32
|
||||
const (
|
||||
PermissionDenied_Text = 0
|
||||
PermissionDenied_Permission = 1
|
||||
PermissionDenied_SuperUser = 2
|
||||
PermissionDenied_ChannelName = 3
|
||||
PermissionDenied_TextTooLong = 4
|
||||
PermissionDenied_H9K = 5
|
||||
PermissionDenied_TemporaryChannel = 6
|
||||
PermissionDenied_MissingCertificate = 7
|
||||
PermissionDenied_UserName = 8
|
||||
PermissionDenied_ChannelFull = 9
|
||||
)
|
||||
var PermissionDenied_DenyType_name = map[int32] string {
|
||||
0: "Text",
|
||||
1: "Permission",
|
||||
2: "SuperUser",
|
||||
3: "ChannelName",
|
||||
4: "TextTooLong",
|
||||
5: "H9K",
|
||||
6: "TemporaryChannel",
|
||||
7: "MissingCertificate",
|
||||
8: "UserName",
|
||||
9: "ChannelFull",
|
||||
}
|
||||
var PermissionDenied_DenyType_value = map[string] int32 {
|
||||
"Text": 0,
|
||||
"Permission": 1,
|
||||
"SuperUser": 2,
|
||||
"ChannelName": 3,
|
||||
"TextTooLong": 4,
|
||||
"H9K": 5,
|
||||
"TemporaryChannel": 6,
|
||||
"MissingCertificate": 7,
|
||||
"UserName": 8,
|
||||
"ChannelFull": 9,
|
||||
}
|
||||
func NewPermissionDenied_DenyType(x int32) *PermissionDenied_DenyType {
|
||||
e := PermissionDenied_DenyType(x)
|
||||
return &e
|
||||
}
|
||||
|
||||
type ContextActionAdd_Context int32
|
||||
const (
|
||||
ContextActionAdd_Server = 1
|
||||
ContextActionAdd_Channel = 2
|
||||
ContextActionAdd_User = 4
|
||||
)
|
||||
var ContextActionAdd_Context_name = map[int32] string {
|
||||
1: "Server",
|
||||
2: "Channel",
|
||||
4: "User",
|
||||
}
|
||||
var ContextActionAdd_Context_value = map[string] int32 {
|
||||
"Server": 1,
|
||||
"Channel": 2,
|
||||
"User": 4,
|
||||
}
|
||||
func NewContextActionAdd_Context(x int32) *ContextActionAdd_Context {
|
||||
e := ContextActionAdd_Context(x)
|
||||
return &e
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
Version *uint32 "PB(varint,1,opt,name=version)"
|
||||
Release *string "PB(bytes,2,opt,name=release)"
|
||||
Os *string "PB(bytes,3,opt,name=os)"
|
||||
OsVersion *string "PB(bytes,4,opt,name=os_version)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *Version) Reset() {
|
||||
*this = Version{}
|
||||
}
|
||||
|
||||
type UDPTunnel struct {
|
||||
Packet []byte "PB(bytes,1,req,name=packet)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UDPTunnel) Reset() {
|
||||
*this = UDPTunnel{}
|
||||
}
|
||||
|
||||
type Authenticate struct {
|
||||
Username *string "PB(bytes,1,opt,name=username)"
|
||||
Password *string "PB(bytes,2,opt,name=password)"
|
||||
Tokens []string "PB(bytes,3,rep,name=tokens)"
|
||||
CeltVersions []int32 "PB(varint,4,rep,name=celt_versions)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *Authenticate) Reset() {
|
||||
*this = Authenticate{}
|
||||
}
|
||||
|
||||
type Ping struct {
|
||||
Timestamp *uint64 "PB(varint,1,opt,name=timestamp)"
|
||||
Good *uint32 "PB(varint,2,opt,name=good)"
|
||||
Late *uint32 "PB(varint,3,opt,name=late)"
|
||||
Lost *uint32 "PB(varint,4,opt,name=lost)"
|
||||
Resync *uint32 "PB(varint,5,opt,name=resync)"
|
||||
UdpPackets *uint32 "PB(varint,6,opt,name=udp_packets)"
|
||||
TcpPackets *uint32 "PB(varint,7,opt,name=tcp_packets)"
|
||||
UdpPingAvg *float32 "PB(fixed32,8,opt,name=udp_ping_avg)"
|
||||
UdpPingVar *float32 "PB(fixed32,9,opt,name=udp_ping_var)"
|
||||
TcpPingAvg *float32 "PB(fixed32,10,opt,name=tcp_ping_avg)"
|
||||
TcpPingVar *float32 "PB(fixed32,11,opt,name=tcp_ping_var)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *Ping) Reset() {
|
||||
*this = Ping{}
|
||||
}
|
||||
|
||||
type Reject struct {
|
||||
Type *Reject_RejectType "PB(varint,1,opt,name=type,enum=mumbleproto.Reject_RejectType)"
|
||||
Reason *string "PB(bytes,2,opt,name=reason)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *Reject) Reset() {
|
||||
*this = Reject{}
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
MaxBandwidth *uint32 "PB(varint,1,opt,name=max_bandwidth)"
|
||||
WelcomeText *string "PB(bytes,2,opt,name=welcome_text)"
|
||||
AllowHtml *bool "PB(varint,3,opt,name=allow_html)"
|
||||
MessageLength *uint32 "PB(varint,4,opt,name=message_length)"
|
||||
ImageMessageLength *uint32 "PB(varint,5,opt,name=image_message_length)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ServerConfig) Reset() {
|
||||
*this = ServerConfig{}
|
||||
}
|
||||
|
||||
type ServerSync struct {
|
||||
Session *uint32 "PB(varint,1,opt,name=session)"
|
||||
MaxBandwidth *uint32 "PB(varint,2,opt,name=max_bandwidth)"
|
||||
WelcomeText *string "PB(bytes,3,opt,name=welcome_text)"
|
||||
Permissions *uint64 "PB(varint,4,opt,name=permissions)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ServerSync) Reset() {
|
||||
*this = ServerSync{}
|
||||
}
|
||||
|
||||
type ChannelRemove struct {
|
||||
ChannelId *uint32 "PB(varint,1,req,name=channel_id)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ChannelRemove) Reset() {
|
||||
*this = ChannelRemove{}
|
||||
}
|
||||
|
||||
type ChannelState struct {
|
||||
ChannelId *uint32 "PB(varint,1,opt,name=channel_id)"
|
||||
Parent *uint32 "PB(varint,2,opt,name=parent)"
|
||||
Name *string "PB(bytes,3,opt,name=name)"
|
||||
Links []uint32 "PB(varint,4,rep,name=links)"
|
||||
Description *string "PB(bytes,5,opt,name=description)"
|
||||
LinksAdd []uint32 "PB(varint,6,rep,name=links_add)"
|
||||
LinksRemove []uint32 "PB(varint,7,rep,name=links_remove)"
|
||||
Temporary *bool "PB(varint,8,opt,name=temporary,def=0)"
|
||||
Position *int32 "PB(varint,9,opt,name=position,def=0)"
|
||||
DescriptionHash []byte "PB(bytes,10,opt,name=description_hash)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ChannelState) Reset() {
|
||||
*this = ChannelState{}
|
||||
}
|
||||
const Default_ChannelState_Temporary bool = false
|
||||
const Default_ChannelState_Position int32 = 0
|
||||
|
||||
type UserRemove struct {
|
||||
Session *uint32 "PB(varint,1,req,name=session)"
|
||||
Actor *uint32 "PB(varint,2,opt,name=actor)"
|
||||
Reason *string "PB(bytes,3,opt,name=reason)"
|
||||
Ban *bool "PB(varint,4,opt,name=ban)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UserRemove) Reset() {
|
||||
*this = UserRemove{}
|
||||
}
|
||||
|
||||
type UserState struct {
|
||||
Session *uint32 "PB(varint,1,opt,name=session)"
|
||||
Actor *uint32 "PB(varint,2,opt,name=actor)"
|
||||
Name *string "PB(bytes,3,opt,name=name)"
|
||||
UserId *uint32 "PB(varint,4,opt,name=user_id)"
|
||||
ChannelId *uint32 "PB(varint,5,opt,name=channel_id)"
|
||||
Mute *bool "PB(varint,6,opt,name=mute)"
|
||||
Deaf *bool "PB(varint,7,opt,name=deaf)"
|
||||
Suppress *bool "PB(varint,8,opt,name=suppress)"
|
||||
SelfMute *bool "PB(varint,9,opt,name=self_mute)"
|
||||
SelfDeaf *bool "PB(varint,10,opt,name=self_deaf)"
|
||||
Texture []byte "PB(bytes,11,opt,name=texture)"
|
||||
PluginContext []byte "PB(bytes,12,opt,name=plugin_context)"
|
||||
PluginIdentity *string "PB(bytes,13,opt,name=plugin_identity)"
|
||||
Comment *string "PB(bytes,14,opt,name=comment)"
|
||||
Hash *string "PB(bytes,15,opt,name=hash)"
|
||||
CommentHash []byte "PB(bytes,16,opt,name=comment_hash)"
|
||||
TextureHash []byte "PB(bytes,17,opt,name=texture_hash)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UserState) Reset() {
|
||||
*this = UserState{}
|
||||
}
|
||||
|
||||
type BanList struct {
|
||||
Bans []*BanList_BanEntry "PB(bytes,1,rep,name=bans)"
|
||||
Query *bool "PB(varint,2,opt,name=query,def=0)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *BanList) Reset() {
|
||||
*this = BanList{}
|
||||
}
|
||||
const Default_BanList_Query bool = false
|
||||
|
||||
type BanList_BanEntry struct {
|
||||
Address []byte "PB(bytes,1,req,name=address)"
|
||||
Mask *uint32 "PB(varint,2,req,name=mask)"
|
||||
Name *string "PB(bytes,3,opt,name=name)"
|
||||
Hash *string "PB(bytes,4,opt,name=hash)"
|
||||
Reason *string "PB(bytes,5,opt,name=reason)"
|
||||
Start *string "PB(bytes,6,opt,name=start)"
|
||||
Duration *uint32 "PB(varint,7,opt,name=duration)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *BanList_BanEntry) Reset() {
|
||||
*this = BanList_BanEntry{}
|
||||
}
|
||||
|
||||
type TextMessage struct {
|
||||
Actor *uint32 "PB(varint,1,opt,name=actor)"
|
||||
Session []uint32 "PB(varint,2,rep,name=session)"
|
||||
ChannelId []uint32 "PB(varint,3,rep,name=channel_id)"
|
||||
TreeId []uint32 "PB(varint,4,rep,name=tree_id)"
|
||||
Message *string "PB(bytes,5,req,name=message)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *TextMessage) Reset() {
|
||||
*this = TextMessage{}
|
||||
}
|
||||
|
||||
type PermissionDenied struct {
|
||||
Permission *uint32 "PB(varint,1,opt,name=permission)"
|
||||
ChannelId *uint32 "PB(varint,2,opt,name=channel_id)"
|
||||
Session *uint32 "PB(varint,3,opt,name=session)"
|
||||
Reason *string "PB(bytes,4,opt,name=reason)"
|
||||
Type *PermissionDenied_DenyType "PB(varint,5,opt,name=type,enum=mumbleproto.PermissionDenied_DenyType)"
|
||||
Name *string "PB(bytes,6,opt,name=name)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *PermissionDenied) Reset() {
|
||||
*this = PermissionDenied{}
|
||||
}
|
||||
|
||||
type ACL struct {
|
||||
ChannelId *uint32 "PB(varint,1,req,name=channel_id)"
|
||||
InheritAcls *bool "PB(varint,2,opt,name=inherit_acls,def=1)"
|
||||
Groups []*ACL_ChanGroup "PB(bytes,3,rep,name=groups)"
|
||||
Acls []*ACL_ChanACL "PB(bytes,4,rep,name=acls)"
|
||||
Query *bool "PB(varint,5,opt,name=query,def=0)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ACL) Reset() {
|
||||
*this = ACL{}
|
||||
}
|
||||
const Default_ACL_InheritAcls bool = true
|
||||
const Default_ACL_Query bool = false
|
||||
|
||||
type ACL_ChanGroup struct {
|
||||
Name *string "PB(bytes,1,req,name=name)"
|
||||
Inherited *bool "PB(varint,2,opt,name=inherited,def=1)"
|
||||
Inherit *bool "PB(varint,3,opt,name=inherit,def=1)"
|
||||
Inheritable *bool "PB(varint,4,opt,name=inheritable,def=1)"
|
||||
Add []uint32 "PB(varint,5,rep,name=add)"
|
||||
Remove []uint32 "PB(varint,6,rep,name=remove)"
|
||||
InheritedMembers []uint32 "PB(varint,7,rep,name=inherited_members)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ACL_ChanGroup) Reset() {
|
||||
*this = ACL_ChanGroup{}
|
||||
}
|
||||
const Default_ACL_ChanGroup_Inherited bool = true
|
||||
const Default_ACL_ChanGroup_Inherit bool = true
|
||||
const Default_ACL_ChanGroup_Inheritable bool = true
|
||||
|
||||
type ACL_ChanACL struct {
|
||||
ApplyHere *bool "PB(varint,1,opt,name=apply_here,def=1)"
|
||||
ApplySubs *bool "PB(varint,2,opt,name=apply_subs,def=1)"
|
||||
Inherited *bool "PB(varint,3,opt,name=inherited,def=1)"
|
||||
UserId *uint32 "PB(varint,4,opt,name=user_id)"
|
||||
Group *string "PB(bytes,5,opt,name=group)"
|
||||
Grant *uint32 "PB(varint,6,opt,name=grant)"
|
||||
Deny *uint32 "PB(varint,7,opt,name=deny)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ACL_ChanACL) Reset() {
|
||||
*this = ACL_ChanACL{}
|
||||
}
|
||||
const Default_ACL_ChanACL_ApplyHere bool = true
|
||||
const Default_ACL_ChanACL_ApplySubs bool = true
|
||||
const Default_ACL_ChanACL_Inherited bool = true
|
||||
|
||||
type QueryUsers struct {
|
||||
Ids []uint32 "PB(varint,1,rep,name=ids)"
|
||||
Names []string "PB(bytes,2,rep,name=names)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *QueryUsers) Reset() {
|
||||
*this = QueryUsers{}
|
||||
}
|
||||
|
||||
type CryptSetup struct {
|
||||
Key []byte "PB(bytes,1,opt,name=key)"
|
||||
ClientNonce []byte "PB(bytes,2,opt,name=client_nonce)"
|
||||
ServerNonce []byte "PB(bytes,3,opt,name=server_nonce)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *CryptSetup) Reset() {
|
||||
*this = CryptSetup{}
|
||||
}
|
||||
|
||||
type ContextActionAdd struct {
|
||||
Action *string "PB(bytes,1,req,name=action)"
|
||||
Text *string "PB(bytes,2,req,name=text)"
|
||||
Context *uint32 "PB(varint,3,opt,name=context)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ContextActionAdd) Reset() {
|
||||
*this = ContextActionAdd{}
|
||||
}
|
||||
|
||||
type ContextAction struct {
|
||||
Session *uint32 "PB(varint,1,opt,name=session)"
|
||||
ChannelId *uint32 "PB(varint,2,opt,name=channel_id)"
|
||||
Action *string "PB(bytes,3,req,name=action)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *ContextAction) Reset() {
|
||||
*this = ContextAction{}
|
||||
}
|
||||
|
||||
type UserList struct {
|
||||
Users []*UserList_User "PB(bytes,1,rep,name=users)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UserList) Reset() {
|
||||
*this = UserList{}
|
||||
}
|
||||
|
||||
type UserList_User struct {
|
||||
UserId *uint32 "PB(varint,1,req,name=user_id)"
|
||||
Name *string "PB(bytes,2,opt,name=name)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UserList_User) Reset() {
|
||||
*this = UserList_User{}
|
||||
}
|
||||
|
||||
type VoiceTarget struct {
|
||||
Id *uint32 "PB(varint,1,opt,name=id)"
|
||||
Targets []*VoiceTarget_Target "PB(bytes,2,rep,name=targets)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *VoiceTarget) Reset() {
|
||||
*this = VoiceTarget{}
|
||||
}
|
||||
|
||||
type VoiceTarget_Target struct {
|
||||
Session []uint32 "PB(varint,1,rep,name=session)"
|
||||
ChannelId *uint32 "PB(varint,2,opt,name=channel_id)"
|
||||
Group *string "PB(bytes,3,opt,name=group)"
|
||||
Links *bool "PB(varint,4,opt,name=links,def=0)"
|
||||
Children *bool "PB(varint,5,opt,name=children,def=0)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *VoiceTarget_Target) Reset() {
|
||||
*this = VoiceTarget_Target{}
|
||||
}
|
||||
const Default_VoiceTarget_Target_Links bool = false
|
||||
const Default_VoiceTarget_Target_Children bool = false
|
||||
|
||||
type PermissionQuery struct {
|
||||
ChannelId *uint32 "PB(varint,1,opt,name=channel_id)"
|
||||
Permissions *uint32 "PB(varint,2,opt,name=permissions)"
|
||||
Flush *bool "PB(varint,3,opt,name=flush,def=0)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *PermissionQuery) Reset() {
|
||||
*this = PermissionQuery{}
|
||||
}
|
||||
const Default_PermissionQuery_Flush bool = false
|
||||
|
||||
type CodecVersion struct {
|
||||
Alpha *int32 "PB(varint,1,req,name=alpha)"
|
||||
Beta *int32 "PB(varint,2,req,name=beta)"
|
||||
PreferAlpha *bool "PB(varint,3,req,name=prefer_alpha,def=1)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *CodecVersion) Reset() {
|
||||
*this = CodecVersion{}
|
||||
}
|
||||
const Default_CodecVersion_PreferAlpha bool = true
|
||||
|
||||
type UserStats struct {
|
||||
Session *uint32 "PB(varint,1,opt,name=session)"
|
||||
StatsOnly *bool "PB(varint,2,opt,name=stats_only,def=0)"
|
||||
Certificates [][]byte "PB(bytes,3,rep,name=certificates)"
|
||||
FromClient *UserStats_Stats "PB(bytes,4,opt,name=from_client)"
|
||||
FromServer *UserStats_Stats "PB(bytes,5,opt,name=from_server)"
|
||||
UdpPackets *uint32 "PB(varint,6,opt,name=udp_packets)"
|
||||
TcpPackets *uint32 "PB(varint,7,opt,name=tcp_packets)"
|
||||
UdpPingAvg *float32 "PB(fixed32,8,opt,name=udp_ping_avg)"
|
||||
UdpPingVar *float32 "PB(fixed32,9,opt,name=udp_ping_var)"
|
||||
TcpPingAvg *float32 "PB(fixed32,10,opt,name=tcp_ping_avg)"
|
||||
TcpPingVar *float32 "PB(fixed32,11,opt,name=tcp_ping_var)"
|
||||
Version *Version "PB(bytes,12,opt,name=version)"
|
||||
CeltVersions []int32 "PB(varint,13,rep,name=celt_versions)"
|
||||
Address []byte "PB(bytes,14,opt,name=address)"
|
||||
Bandwidth *uint32 "PB(varint,15,opt,name=bandwidth)"
|
||||
Onlinesecs *uint32 "PB(varint,16,opt,name=onlinesecs)"
|
||||
Idlesecs *uint32 "PB(varint,17,opt,name=idlesecs)"
|
||||
StrongCertificate *bool "PB(varint,18,opt,name=strong_certificate,def=0)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UserStats) Reset() {
|
||||
*this = UserStats{}
|
||||
}
|
||||
const Default_UserStats_StatsOnly bool = false
|
||||
const Default_UserStats_StrongCertificate bool = false
|
||||
|
||||
type UserStats_Stats struct {
|
||||
Good *uint32 "PB(varint,1,opt,name=good)"
|
||||
Late *uint32 "PB(varint,2,opt,name=late)"
|
||||
Lost *uint32 "PB(varint,3,opt,name=lost)"
|
||||
Resync *uint32 "PB(varint,4,opt,name=resync)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *UserStats_Stats) Reset() {
|
||||
*this = UserStats_Stats{}
|
||||
}
|
||||
|
||||
type RequestBlob struct {
|
||||
SessionTexture []uint32 "PB(varint,1,rep,name=session_texture)"
|
||||
SessionComment []uint32 "PB(varint,2,rep,name=session_comment)"
|
||||
ChannelDescription []uint32 "PB(varint,3,rep,name=channel_description)"
|
||||
XXX_unrecognized []byte
|
||||
}
|
||||
func (this *RequestBlob) Reset() {
|
||||
*this = RequestBlob{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("mumbleproto.Reject_RejectType", Reject_RejectType_name, Reject_RejectType_value)
|
||||
proto.RegisterEnum("mumbleproto.PermissionDenied_DenyType", PermissionDenied_DenyType_name, PermissionDenied_DenyType_value)
|
||||
proto.RegisterEnum("mumbleproto.ContextActionAdd_Context", ContextActionAdd_Context_name, ContextActionAdd_Context_value)
|
||||
}
|
||||
274
pkg/mumbleproto/Mumble.proto
Normal file
274
pkg/mumbleproto/Mumble.proto
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
package mumbleproto;
|
||||
|
||||
option optimize_for = SPEED;
|
||||
|
||||
message Version {
|
||||
optional uint32 version = 1;
|
||||
optional string release = 2;
|
||||
optional string os = 3;
|
||||
optional string os_version = 4;
|
||||
}
|
||||
|
||||
message UDPTunnel {
|
||||
required bytes packet = 1;
|
||||
}
|
||||
|
||||
message Authenticate {
|
||||
optional string username = 1;
|
||||
optional string password = 2;
|
||||
repeated string tokens = 3;
|
||||
repeated int32 celt_versions = 4;
|
||||
}
|
||||
|
||||
message Ping {
|
||||
optional uint64 timestamp = 1;
|
||||
optional uint32 good = 2;
|
||||
optional uint32 late = 3;
|
||||
optional uint32 lost = 4;
|
||||
optional uint32 resync = 5;
|
||||
optional uint32 udp_packets = 6;
|
||||
optional uint32 tcp_packets = 7;
|
||||
optional float udp_ping_avg = 8;
|
||||
optional float udp_ping_var = 9;
|
||||
optional float tcp_ping_avg = 10;
|
||||
optional float tcp_ping_var = 11;
|
||||
}
|
||||
|
||||
message Reject {
|
||||
enum RejectType {
|
||||
None = 0;
|
||||
WrongVersion = 1;
|
||||
InvalidUsername = 2;
|
||||
WrongUserPW = 3;
|
||||
WrongServerPW = 4;
|
||||
UsernameInUse = 5;
|
||||
ServerFull = 6;
|
||||
NoCertificate = 7;
|
||||
}
|
||||
optional RejectType type = 1;
|
||||
optional string reason = 2;
|
||||
}
|
||||
|
||||
message ServerConfig {
|
||||
optional uint32 max_bandwidth = 1;
|
||||
optional string welcome_text = 2;
|
||||
optional bool allow_html = 3;
|
||||
optional uint32 message_length = 4;
|
||||
optional uint32 image_message_length = 5;
|
||||
}
|
||||
|
||||
message ServerSync {
|
||||
optional uint32 session = 1;
|
||||
optional uint32 max_bandwidth = 2;
|
||||
optional string welcome_text = 3;
|
||||
optional uint64 permissions = 4;
|
||||
}
|
||||
|
||||
message ChannelRemove {
|
||||
required uint32 channel_id = 1;
|
||||
}
|
||||
|
||||
message ChannelState {
|
||||
optional uint32 channel_id = 1;
|
||||
optional uint32 parent = 2;
|
||||
optional string name = 3;
|
||||
repeated uint32 links = 4;
|
||||
optional string description = 5;
|
||||
repeated uint32 links_add = 6;
|
||||
repeated uint32 links_remove = 7;
|
||||
optional bool temporary = 8 [default = false];
|
||||
optional int32 position = 9 [default = 0];
|
||||
optional bytes description_hash = 10;
|
||||
}
|
||||
|
||||
message UserRemove {
|
||||
required uint32 session = 1;
|
||||
optional uint32 actor = 2;
|
||||
optional string reason = 3;
|
||||
optional bool ban = 4;
|
||||
}
|
||||
|
||||
message UserState {
|
||||
optional uint32 session = 1;
|
||||
optional uint32 actor = 2;
|
||||
optional string name = 3;
|
||||
optional uint32 user_id = 4;
|
||||
optional uint32 channel_id = 5;
|
||||
optional bool mute = 6;
|
||||
optional bool deaf = 7;
|
||||
optional bool suppress = 8;
|
||||
optional bool self_mute = 9;
|
||||
optional bool self_deaf = 10;
|
||||
optional bytes texture = 11;
|
||||
optional bytes plugin_context = 12;
|
||||
optional string plugin_identity = 13;
|
||||
optional string comment = 14;
|
||||
optional string hash = 15;
|
||||
optional bytes comment_hash = 16;
|
||||
optional bytes texture_hash = 17;
|
||||
}
|
||||
|
||||
message BanList {
|
||||
message BanEntry {
|
||||
required bytes address = 1;
|
||||
required uint32 mask = 2;
|
||||
optional string name = 3;
|
||||
optional string hash = 4;
|
||||
optional string reason = 5;
|
||||
optional string start = 6;
|
||||
optional uint32 duration = 7;
|
||||
}
|
||||
repeated BanEntry bans = 1;
|
||||
optional bool query = 2 [default = false];
|
||||
}
|
||||
|
||||
message TextMessage {
|
||||
optional uint32 actor = 1;
|
||||
repeated uint32 session = 2;
|
||||
repeated uint32 channel_id = 3;
|
||||
repeated uint32 tree_id = 4;
|
||||
required string message = 5;
|
||||
}
|
||||
|
||||
message PermissionDenied {
|
||||
enum DenyType {
|
||||
Text = 0;
|
||||
Permission = 1;
|
||||
SuperUser = 2;
|
||||
ChannelName = 3;
|
||||
TextTooLong = 4;
|
||||
H9K = 5;
|
||||
TemporaryChannel = 6;
|
||||
MissingCertificate = 7;
|
||||
UserName = 8;
|
||||
ChannelFull = 9;
|
||||
}
|
||||
optional uint32 permission = 1;
|
||||
optional uint32 channel_id = 2;
|
||||
optional uint32 session = 3;
|
||||
optional string reason = 4;
|
||||
optional DenyType type = 5;
|
||||
optional string name = 6;
|
||||
}
|
||||
|
||||
message ACL {
|
||||
message ChanGroup {
|
||||
required string name = 1;
|
||||
optional bool inherited = 2 [default = true];
|
||||
optional bool inherit = 3 [default = true];
|
||||
optional bool inheritable = 4 [default = true];
|
||||
repeated uint32 add = 5;
|
||||
repeated uint32 remove = 6;
|
||||
repeated uint32 inherited_members = 7;
|
||||
}
|
||||
message ChanACL {
|
||||
optional bool apply_here = 1 [default = true];
|
||||
optional bool apply_subs = 2 [default = true];
|
||||
optional bool inherited = 3 [default = true];
|
||||
optional uint32 user_id = 4;
|
||||
optional string group = 5;
|
||||
optional uint32 grant = 6;
|
||||
optional uint32 deny = 7;
|
||||
}
|
||||
required uint32 channel_id = 1;
|
||||
optional bool inherit_acls = 2 [default = true];
|
||||
repeated ChanGroup groups = 3;
|
||||
repeated ChanACL acls = 4;
|
||||
optional bool query = 5 [default = false];
|
||||
}
|
||||
|
||||
message QueryUsers {
|
||||
repeated uint32 ids = 1;
|
||||
repeated string names = 2;
|
||||
}
|
||||
|
||||
message CryptSetup {
|
||||
optional bytes key = 1;
|
||||
optional bytes client_nonce = 2;
|
||||
optional bytes server_nonce = 3;
|
||||
}
|
||||
|
||||
message ContextActionAdd {
|
||||
enum Context {
|
||||
Server = 0x01;
|
||||
Channel = 0x02;
|
||||
User = 0x04;
|
||||
}
|
||||
required string action = 1;
|
||||
required string text = 2;
|
||||
optional uint32 context = 3;
|
||||
}
|
||||
|
||||
message ContextAction {
|
||||
optional uint32 session = 1;
|
||||
optional uint32 channel_id = 2;
|
||||
required string action = 3;
|
||||
}
|
||||
|
||||
message UserList {
|
||||
message User {
|
||||
required uint32 user_id = 1;
|
||||
optional string name = 2;
|
||||
}
|
||||
repeated User users = 1;
|
||||
}
|
||||
|
||||
message VoiceTarget {
|
||||
message Target {
|
||||
repeated uint32 session = 1;
|
||||
optional uint32 channel_id = 2;
|
||||
optional string group = 3;
|
||||
optional bool links = 4 [default = false];
|
||||
optional bool children = 5 [default = false];
|
||||
}
|
||||
optional uint32 id = 1;
|
||||
repeated Target targets = 2;
|
||||
}
|
||||
|
||||
message PermissionQuery {
|
||||
optional uint32 channel_id = 1;
|
||||
optional uint32 permissions = 2;
|
||||
optional bool flush = 3 [default = false];
|
||||
}
|
||||
|
||||
message CodecVersion {
|
||||
required int32 alpha = 1;
|
||||
required int32 beta = 2;
|
||||
required bool prefer_alpha = 3 [default = true];
|
||||
}
|
||||
|
||||
message UserStats {
|
||||
message Stats {
|
||||
optional uint32 good = 1;
|
||||
optional uint32 late = 2;
|
||||
optional uint32 lost = 3;
|
||||
optional uint32 resync = 4;
|
||||
}
|
||||
|
||||
optional uint32 session = 1;
|
||||
optional bool stats_only = 2 [default = false];
|
||||
repeated bytes certificates = 3;
|
||||
optional Stats from_client = 4;
|
||||
optional Stats from_server = 5;
|
||||
|
||||
optional uint32 udp_packets = 6;
|
||||
optional uint32 tcp_packets = 7;
|
||||
optional float udp_ping_avg = 8;
|
||||
optional float udp_ping_var = 9;
|
||||
optional float tcp_ping_avg = 10;
|
||||
optional float tcp_ping_var = 11;
|
||||
|
||||
optional Version version = 12;
|
||||
repeated int32 celt_versions = 13;
|
||||
optional bytes address = 14;
|
||||
optional uint32 bandwidth = 15;
|
||||
optional uint32 onlinesecs = 16;
|
||||
optional uint32 idlesecs = 17;
|
||||
optional bool strong_certificate = 18 [default = false];
|
||||
}
|
||||
|
||||
message RequestBlob {
|
||||
repeated uint32 session_texture = 1;
|
||||
repeated uint32 session_comment = 2;
|
||||
repeated uint32 channel_description = 3;
|
||||
}
|
||||
6
pkg/packetdatastream/Makefile
Normal file
6
pkg/packetdatastream/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG = packetdatastream
|
||||
GOFILES = packetdatastream.go
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
326
pkg/packetdatastream/packetdatastream.go
Normal file
326
pkg/packetdatastream/packetdatastream.go
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
// Grumble - an implementation of Murmur in Go
|
||||
// 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 packetdatastream
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
type PacketDataStream struct {
|
||||
Buf []byte
|
||||
offset int
|
||||
maxsize int
|
||||
overshoot int
|
||||
ok bool
|
||||
}
|
||||
|
||||
func New(buf []byte) (pds *PacketDataStream) {
|
||||
pds = new(PacketDataStream)
|
||||
pds.Buf = buf
|
||||
pds.maxsize = len(buf)
|
||||
pds.ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (pds *PacketDataStream) IsValid() bool {
|
||||
return pds.ok
|
||||
}
|
||||
|
||||
func (pds *PacketDataStream) Skip(skip int) {
|
||||
if pds.Left() >= skip {
|
||||
pds.offset += skip
|
||||
} else {
|
||||
pds.ok = false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns number of bytes remaining in
|
||||
// the buffer.
|
||||
func (pds *PacketDataStream) Left() int {
|
||||
return int(pds.maxsize - pds.offset)
|
||||
}
|
||||
|
||||
// Returns the size of the currently-assembled data
|
||||
// stream
|
||||
func (pds *PacketDataStream) Size() int {
|
||||
return pds.offset
|
||||
}
|
||||
|
||||
// Get the next byte from the PacketDataStream as a uint64
|
||||
func (pds *PacketDataStream) next() (ret uint64) {
|
||||
if pds.offset < pds.maxsize {
|
||||
ret = uint64(pds.Buf[pds.offset])
|
||||
pds.offset += 1
|
||||
return
|
||||
} else {
|
||||
pds.ok = false
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Get the next byte from the PacketDataStream as a byte (uint8)
|
||||
func (pds *PacketDataStream) Next8() (ret uint8) {
|
||||
if pds.offset < pds.maxsize {
|
||||
ret = uint8(pds.Buf[pds.offset])
|
||||
pds.offset += 1
|
||||
return
|
||||
} else {
|
||||
pds.ok = false
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Put a byte (represented in an uint64) into the
|
||||
// PacketDataStream.
|
||||
func (pds *PacketDataStream) append(val uint64) {
|
||||
if (val > 0xff) {
|
||||
pds.ok = false
|
||||
return
|
||||
}
|
||||
|
||||
if pds.offset < pds.maxsize {
|
||||
pds.Buf[pds.offset] = byte(val)
|
||||
pds.offset += 1
|
||||
} else {
|
||||
pds.ok = false
|
||||
pds.overshoot++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add a variably-sized integer to the PacketDataStream.
|
||||
// The PacketDataStream will figure out the most efficient
|
||||
// encoding based on the binary representation of the value.
|
||||
func (pds *PacketDataStream) addVarint(val uint64) {
|
||||
i := val
|
||||
|
||||
if (i & 0x8000000000000000) != 0 && ^i < 0x100000000 {
|
||||
// Signed number
|
||||
i = ^i
|
||||
if i <= 0x3 {
|
||||
// Short for -1 to -4
|
||||
pds.append(0xfc | i)
|
||||
} else {
|
||||
pds.append(0xf8)
|
||||
}
|
||||
}
|
||||
if i < 0x80 {
|
||||
// Needs top bit clear
|
||||
pds.append(i)
|
||||
} else if i < 0x4000 {
|
||||
// Needs two top bits clear
|
||||
pds.append((i >> 8) | 0x80)
|
||||
pds.append(i & 0xff)
|
||||
} else if i < 0x10000000 {
|
||||
// Needs three top bits clear
|
||||
pds.append((i >> 16) | 0xc0)
|
||||
pds.append((i >> 8) & 0xff)
|
||||
pds.append(i & 0xff)
|
||||
} else if i < 0x100000000 {
|
||||
// Full 32 bit integer
|
||||
pds.append(0xf0)
|
||||
pds.append((i >> 24) & 0xff)
|
||||
pds.append((i >> 16) & 0xff)
|
||||
pds.append((i >> 8) & 0xff)
|
||||
pds.append(i & 0xff)
|
||||
} else {
|
||||
// 64 bit val
|
||||
pds.append(0xf4)
|
||||
pds.append((i >> 56) & 0xff)
|
||||
pds.append((i >> 48) & 0xff)
|
||||
pds.append((i >> 40) & 0xff)
|
||||
pds.append((i >> 32) & 0xff)
|
||||
pds.append((i >> 24) & 0xff)
|
||||
pds.append((i >> 16) & 0xff)
|
||||
pds.append((i >> 8) & 0xff)
|
||||
pds.append(i & 0xff)
|
||||
}
|
||||
}
|
||||
|
||||
func (pds *PacketDataStream) getVarint() (i uint64) {
|
||||
v := pds.next()
|
||||
|
||||
if (v & 0x80) == 0x00 {
|
||||
i = (v & 0x7f)
|
||||
} else if (v & 0xc0) == 0x80 {
|
||||
i = (v & 0x3f) << 8 | pds.next()
|
||||
} else if (v & 0xf0) == 0xf0 {
|
||||
switch v & 0xfc {
|
||||
case 0xf0:
|
||||
i = pds.next() << 24 | pds.next() << 16 | pds.next() << 8 | pds.next()
|
||||
case 0xf4:
|
||||
i = pds.next() << 56 | pds.next() << 48 | pds.next() << 40 | pds.next() << 32 | pds.next() << 24 | pds.next() << 16 | pds.next() << 8 | pds.next()
|
||||
case 0xf8:
|
||||
i = ^pds.getVarint()
|
||||
case 0xfc:
|
||||
i = ^(v & 0x03)
|
||||
default:
|
||||
pds.ok = false
|
||||
i = 0
|
||||
}
|
||||
} else if (v & 0xf0) == 0xe0 {
|
||||
i = (v & 0x0f) << 24 | pds.next() << 16 | pds.next() << 8 | pds.next()
|
||||
} else if (v & 0xe0) == 0xc0 {
|
||||
i = (v & 0x1f) << 16 | pds.next() << 8 | pds.next()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Read a uint64 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetUint64() uint64 {
|
||||
return pds.getVarint()
|
||||
}
|
||||
|
||||
// Write a uint64 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutUint64(val uint64) {
|
||||
pds.addVarint(val)
|
||||
}
|
||||
|
||||
// Read a uint32 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetUint32() uint32 {
|
||||
return uint32(pds.getVarint())
|
||||
}
|
||||
|
||||
// Write a uint32 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutUint32(val uint32) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a uint16 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetUint16() uint16 {
|
||||
return uint16(pds.getVarint())
|
||||
}
|
||||
|
||||
// Write a uint16 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutUint16(val uint16) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a uint8 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetUint8() uint8 {
|
||||
varint := pds.getVarint()
|
||||
return uint8(varint)
|
||||
}
|
||||
|
||||
// Write a uint8 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutUint8(val uint8) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a int64 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetInt64() int64 {
|
||||
return int64(pds.getVarint())
|
||||
}
|
||||
|
||||
// Write a int64 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutInt64(val int64) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a int32 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetInt32() int32 {
|
||||
return int32(pds.getVarint())
|
||||
}
|
||||
|
||||
// Write a int32 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutInt32(val int32) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a int16 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetInt16() int16 {
|
||||
return int16(pds.getVarint())
|
||||
}
|
||||
|
||||
// Write a int16 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutInt16(val int16) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a int8 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetInt8() int8 {
|
||||
return int8(pds.getVarint())
|
||||
}
|
||||
|
||||
// Write a int8 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutInt8(val int8) {
|
||||
pds.addVarint(uint64(val))
|
||||
}
|
||||
|
||||
// Read a float32 from the PacketDataStream
|
||||
func (pds *PacketDataStream) GetFloat32() float32 {
|
||||
if pds.Left() < 4 {
|
||||
pds.ok = false
|
||||
return 0
|
||||
}
|
||||
|
||||
var val uint32
|
||||
|
||||
val = uint32(pds.Next8()) << 24 | uint32(pds.Next8()) << 16 | uint32(pds.Next8()) << 8 | uint32(pds.Next8())
|
||||
return math.Float32frombits(val)
|
||||
}
|
||||
|
||||
// Write a float32 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutFloat32(val float32) {
|
||||
bits := math.Float32bits(val)
|
||||
pds.append(uint64((bits >> 24) & 0xff))
|
||||
pds.append(uint64((bits >> 16) & 0xff))
|
||||
pds.append(uint64((bits >> 8) & 0xff))
|
||||
pds.append(uint64(bits & 0xff))
|
||||
}
|
||||
|
||||
// Read a float64 from the PacketDataStream.
|
||||
func (pds *PacketDataStream) GetFloat64() float64 {
|
||||
if pds.Left() < 8 {
|
||||
pds.ok = false
|
||||
return 0
|
||||
}
|
||||
|
||||
var val uint64
|
||||
val = uint64(pds.Next8()) << 56 | uint64(pds.Next8()) << 48 | uint64(pds.Next8()) << 40 | uint64(pds.Next8()) << 32 | uint64(pds.Next8()) << 24 | uint64(pds.Next8()) << 16 | uint64(pds.Next8()) << 8 | uint64(pds.Next8())
|
||||
|
||||
return math.Float64frombits(val)
|
||||
}
|
||||
|
||||
// Write a float64 to the PacketDataStream
|
||||
func (pds *PacketDataStream) PutFloat64(val float64) {
|
||||
bits := math.Float64bits(val)
|
||||
pds.append((bits >> 56) & 0xff)
|
||||
pds.append((bits >> 48) & 0xff)
|
||||
pds.append((bits >> 40) & 0xff)
|
||||
pds.append((bits >> 32) & 0xff)
|
||||
pds.append((bits >> 24) & 0xff)
|
||||
pds.append((bits >> 16) & 0xff)
|
||||
pds.append((bits >> 8) & 0xff)
|
||||
pds.append(bits & 0xff)
|
||||
}
|
||||
|
||||
// Copy a buffer out of the PacketDataStream into dst.
|
||||
func (pds *PacketDataStream) CopyBytes(dst []byte) {
|
||||
if pds.Left() >= len(dst) {
|
||||
if copy(dst, pds.Buf[pds.offset:pds.offset+len(dst)]) != len(dst) {
|
||||
pds.ok = false
|
||||
}
|
||||
} else {
|
||||
pds.ok = false
|
||||
}
|
||||
}
|
||||
|
||||
// Put a buffer src into the PacketDataStream at the
|
||||
// current offset.
|
||||
func (pds *PacketDataStream) PutBytes(src []byte) {
|
||||
if pds.Left() >= len(src) {
|
||||
if copy(pds.Buf[pds.offset:pds.offset+len(src)], src) != len(src) {
|
||||
pds.ok = false
|
||||
} else {
|
||||
pds.offset += len(src)
|
||||
}
|
||||
} else {
|
||||
pds.overshoot += len(src) - pds.Left()
|
||||
pds.ok = false
|
||||
}
|
||||
}
|
||||
144
pkg/packetdatastream/packetdatastream_test.go
Normal file
144
pkg/packetdatastream/packetdatastream_test.go
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
package packetdatastream
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"math"
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
func TestSelfUint8(t *testing.T) {
|
||||
buf := make([]byte, 500)
|
||||
pds := New(buf)
|
||||
|
||||
for i := uint8(0); i < 0xff; i++ {
|
||||
pds.PutUint8(i)
|
||||
if !pds.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pds2 := New(pds.Buf)
|
||||
for i := uint8(0); i < 0xff; i++ {
|
||||
val := pds2.GetUint8()
|
||||
if val != i {
|
||||
t.Errorf("Mismatch (read: %v, expected: %v)", val, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfUint64(t *testing.T) {
|
||||
buf := make([]byte, 500)
|
||||
pds := New(buf)
|
||||
|
||||
for i := uint64(1 << 54); i < (uint64(1 << 54)+10); i++ {
|
||||
pds.PutUint64(i)
|
||||
if !pds.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pds2 := New(buf)
|
||||
for i := uint64(1 << 54); i < (uint64(1 << 54)+10); i++ {
|
||||
val := pds2.GetUint64()
|
||||
if !pds.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
}
|
||||
if val != i {
|
||||
t.Errorf("Mismatch (read: %v, expected: %v)", val, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfMumbleVoicePacket(t *testing.T) {
|
||||
buf := make([]byte, 500)
|
||||
pds := New(buf)
|
||||
data := make([]byte, 54)
|
||||
|
||||
rand.Read(data)
|
||||
|
||||
pds.PutUint32(1)
|
||||
pds.PutBytes(data)
|
||||
|
||||
pds2 := New(buf)
|
||||
if pds2.GetUint32() != 1 {
|
||||
t.Errorf("Session mismatch")
|
||||
}
|
||||
|
||||
outbuf := make([]byte, 54)
|
||||
pds2.CopyBytes(outbuf)
|
||||
|
||||
if !pds.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 54; i++ {
|
||||
if outbuf[i] != data[i] {
|
||||
t.Errorf("Voice data mismatch (got %v, expected %v)", outbuf[i], data[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfFloat64(t *testing.T) {
|
||||
buf := make([]byte, 500)
|
||||
pds := New(buf)
|
||||
pds2 := New(buf)
|
||||
|
||||
pds.PutFloat64(math.Pi)
|
||||
pi := pds2.GetFloat64()
|
||||
|
||||
if !pds.IsValid() || !pds2.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
return
|
||||
}
|
||||
|
||||
if pi != float64(math.Pi) {
|
||||
t.Errorf("Unexpected result. Got %v, expected %v", pi, float64(math.Pi))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfFloat32(t *testing.T) {
|
||||
buf := make([]byte, 500)
|
||||
pds := New(buf)
|
||||
pds2 := New(buf)
|
||||
|
||||
pds.PutFloat32(math.E)
|
||||
e := pds2.GetFloat32()
|
||||
|
||||
if !pds.IsValid() || !pds2.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
}
|
||||
|
||||
if e != float32(math.E) {
|
||||
t.Errorf("Unexpected result. Got %v, expected %v", e, float32(math.E))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfBytes(t *testing.T) {
|
||||
msg := [15]byte{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, }
|
||||
buf := make([]byte, 500)
|
||||
pds := New(buf)
|
||||
pds2 := New(buf)
|
||||
|
||||
pds.PutBytes(msg[0:])
|
||||
out := make([]byte, 15)
|
||||
pds2.CopyBytes(out)
|
||||
|
||||
if !pds.IsValid() || !pds.IsValid() {
|
||||
t.Errorf("Invalid PDS")
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 15; i++ {
|
||||
if msg[i] != out[i] {
|
||||
t.Errorf("Mismatch at index %v. Got %v, expected %v", i, out[i], msg[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
588
server.go
Normal file
588
server.go
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
// 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 (
|
||||
"log"
|
||||
"crypto/tls"
|
||||
"os"
|
||||
"net"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"container/list"
|
||||
"sync"
|
||||
"goprotobuf.googlecode.com/hg/proto"
|
||||
"mumbleproto"
|
||||
"cryptstate"
|
||||
)
|
||||
|
||||
// The default port a Murmur server listens on
|
||||
const DefaultPort = 64738
|
||||
const UDPPacketSize = 1024
|
||||
|
||||
const CeltCompatBitstream = -2147483638
|
||||
|
||||
// Client connection states
|
||||
const (
|
||||
StateClientConnected = iota
|
||||
StateServerSentVersion
|
||||
StateClientSentVersion
|
||||
StateClientAuthenticated
|
||||
StateClientDead
|
||||
)
|
||||
|
||||
// A Murmur server instance
|
||||
type Server struct {
|
||||
listener tls.Listener
|
||||
address string
|
||||
port int
|
||||
udpconn *net.UDPConn
|
||||
|
||||
incoming chan *Message
|
||||
outgoing chan *Message
|
||||
|
||||
udpsend chan *Message
|
||||
|
||||
// Config-related
|
||||
MaxUsers int
|
||||
MaxBandwidth uint32
|
||||
|
||||
session uint32
|
||||
|
||||
// A list of all connected clients
|
||||
cmutex *sync.RWMutex
|
||||
clients *list.List
|
||||
|
||||
// Codec information
|
||||
AlphaCodec int32
|
||||
BetaCodec int32
|
||||
PreferAlphaCodec bool
|
||||
|
||||
root *Channel
|
||||
}
|
||||
|
||||
// A Mumble channel
|
||||
type Channel struct {
|
||||
Id int
|
||||
Name string
|
||||
Description string
|
||||
Temporary bool
|
||||
Position int
|
||||
Channels *list.List
|
||||
}
|
||||
|
||||
// Allocate a new Murmur instance
|
||||
func NewServer(addr string, port int) (s *Server, err os.Error) {
|
||||
s = new(Server)
|
||||
|
||||
s.address = addr
|
||||
s.port = port
|
||||
|
||||
// Create the list of connected clients
|
||||
s.cmutex = new(sync.RWMutex)
|
||||
s.clients = list.New()
|
||||
|
||||
s.outgoing = make(chan *Message)
|
||||
s.incoming = make(chan *Message)
|
||||
s.udpsend = make(chan *Message)
|
||||
|
||||
s.MaxBandwidth = 300000
|
||||
s.MaxUsers = 10
|
||||
|
||||
// Allocate the root channel
|
||||
|
||||
s.root = &Channel{
|
||||
Id: 0,
|
||||
Name: "Root",
|
||||
}
|
||||
|
||||
go s.handler()
|
||||
go s.multiplexer()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Called by the server to initiate a new client connection.
|
||||
func (server *Server) NewClient(conn net.Conn) (err os.Error) {
|
||||
client := new(ClientConnection)
|
||||
|
||||
// Get the address of the connected client
|
||||
if addr := conn.RemoteAddr(); addr != nil {
|
||||
client.tcpaddr = addr.(*net.TCPAddr)
|
||||
log.Stdoutf("client connected: %s", client.tcpaddr.String())
|
||||
}
|
||||
|
||||
client.server = server
|
||||
client.conn = conn
|
||||
client.reader = bufio.NewReader(client.conn)
|
||||
client.writer = bufio.NewWriter(client.conn)
|
||||
client.state = StateClientConnected
|
||||
|
||||
client.msgchan = make(chan *Message)
|
||||
client.udprecv = make(chan []byte)
|
||||
|
||||
// New client connection....
|
||||
server.session += 1
|
||||
client.Session = server.session
|
||||
|
||||
// Add it to the list of connected clients
|
||||
server.cmutex.Lock()
|
||||
server.clients.PushBack(client)
|
||||
server.cmutex.Unlock()
|
||||
|
||||
go client.receiver()
|
||||
go client.udpreceiver()
|
||||
go client.sender()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup a client by it's session id. Optimize this by using a map.
|
||||
func (server *Server) getClientConnection(session uint32) (client *ClientConnection) {
|
||||
server.cmutex.RLock()
|
||||
defer server.cmutex.RUnlock()
|
||||
|
||||
for x := range server.clients.Iter() {
|
||||
user := x.(*ClientConnection)
|
||||
if user.Session == session {
|
||||
return user
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is the synchronous request handler for all incoming messages.
|
||||
func (server *Server) handler() {
|
||||
for {
|
||||
msg := <-server.incoming
|
||||
client := msg.client
|
||||
|
||||
if client.state == StateClientAuthenticated {
|
||||
server.handleIncomingMessage(client, msg)
|
||||
} else if client.state == StateClientSentVersion {
|
||||
server.handleAuthenticate(client, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) handleAuthenticate(client *ClientConnection, msg *Message) {
|
||||
// Is this message not an authenticate message? If not, discard it...
|
||||
if msg.kind != MessageAuthenticate {
|
||||
client.Panic("Unexpected message. Expected Authenticate.")
|
||||
return
|
||||
}
|
||||
|
||||
auth := &mumbleproto.Authenticate{}
|
||||
err := proto.Unmarshal(msg.buf, auth)
|
||||
if err != nil {
|
||||
client.Panic("Unable to unmarshal Authenticate message.")
|
||||
return
|
||||
}
|
||||
|
||||
// Did we get a username?
|
||||
if auth.Username == nil {
|
||||
client.Panic("No username in auth message...")
|
||||
return
|
||||
}
|
||||
|
||||
client.Username = *auth.Username
|
||||
|
||||
// Setup the cryptstate for the client.
|
||||
client.crypt, err = cryptstate.New()
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
err = client.crypt.GenerateKey()
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Send CryptState information to the client so it can establish an UDP connection
|
||||
// (if it wishes)...
|
||||
err = client.sendProtoMessage(MessageCryptSetup, &mumbleproto.CryptSetup{
|
||||
Key: client.crypt.RawKey[0:],
|
||||
ClientNonce: client.crypt.DecryptIV[0:],
|
||||
ServerNonce: client.crypt.EncryptIV[0:],
|
||||
})
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
|
||||
client.codecs = auth.CeltVersions
|
||||
server.updateCodecVersions()
|
||||
|
||||
client.sendChannelList()
|
||||
|
||||
client.state = StateClientAuthenticated
|
||||
|
||||
// Broadcast that we, the client, entered a channel...
|
||||
err = server.broadcastProtoMessage(MessageUserState, &mumbleproto.UserState{
|
||||
Session: proto.Uint32(client.Session),
|
||||
Name: proto.String(client.Username),
|
||||
ChannelId: proto.Uint32(0),
|
||||
})
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
|
||||
server.sendUserList(client)
|
||||
|
||||
err = client.sendProtoMessage(MessageServerSync, &mumbleproto.ServerSync{
|
||||
Session: proto.Uint32(client.Session),
|
||||
MaxBandwidth: proto.Uint32(server.MaxBandwidth),
|
||||
})
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
||||
err = client.sendProtoMessage(MessageServerConfig, &mumbleproto.ServerConfig{
|
||||
AllowHtml: proto.Bool(true),
|
||||
MessageLength: proto.Uint32(1000),
|
||||
ImageMessageLength: proto.Uint32(1000),
|
||||
})
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
||||
client.state = StateClientAuthenticated
|
||||
}
|
||||
|
||||
func (server *Server) updateCodecVersions() {
|
||||
codecusers := map[int32]int{}
|
||||
var winner int32
|
||||
var count int
|
||||
|
||||
server.cmutex.RLock()
|
||||
defer server.cmutex.RUnlock()
|
||||
|
||||
for x := range server.clients.Iter() {
|
||||
client := x.(*ClientConnection)
|
||||
for i := 0; i < len(client.codecs); i++ {
|
||||
codecusers[client.codecs[i]] += 1
|
||||
}
|
||||
}
|
||||
|
||||
// result?
|
||||
for codec, users := range codecusers {
|
||||
if users > count {
|
||||
count = users
|
||||
winner = codec
|
||||
}
|
||||
}
|
||||
|
||||
var current int32
|
||||
if server.PreferAlphaCodec {
|
||||
current = server.AlphaCodec
|
||||
} else {
|
||||
current = server.BetaCodec
|
||||
}
|
||||
|
||||
if winner == current {
|
||||
return
|
||||
}
|
||||
|
||||
if winner == CeltCompatBitstream {
|
||||
server.PreferAlphaCodec = true
|
||||
} else {
|
||||
server.PreferAlphaCodec = !server.PreferAlphaCodec
|
||||
}
|
||||
|
||||
if (server.PreferAlphaCodec) {
|
||||
server.AlphaCodec = winner
|
||||
} else {
|
||||
server.BetaCodec = winner
|
||||
}
|
||||
|
||||
err := server.broadcastProtoMessage(MessageCodecVersion, &mumbleproto.CodecVersion{
|
||||
Alpha: proto.Int32(server.AlphaCodec),
|
||||
Beta: proto.Int32(server.BetaCodec),
|
||||
PreferAlpha: proto.Bool(server.PreferAlphaCodec),
|
||||
})
|
||||
if err != nil {
|
||||
log.Stdoutf("Unable to broadcast..")
|
||||
return
|
||||
}
|
||||
|
||||
log.Stdoutf("CELT codec switch %v %v (PreferAlpha %v)", server.AlphaCodec, server.BetaCodec, server.PreferAlphaCodec)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (server *Server) sendUserList(client *ClientConnection) {
|
||||
server.cmutex.RLock()
|
||||
defer server.cmutex.RUnlock()
|
||||
|
||||
for x := range server.clients.Iter() {
|
||||
user := x.(*ClientConnection)
|
||||
if user.state != StateClientAuthenticated {
|
||||
continue
|
||||
}
|
||||
|
||||
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
|
||||
Session: proto.Uint32(user.Session),
|
||||
Name: proto.String(user.Username),
|
||||
ChannelId: proto.Uint32(0),
|
||||
})
|
||||
|
||||
log.Stdoutf("Sent One User...")
|
||||
|
||||
if err != nil {
|
||||
log.Stdoutf("unable to send!")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (server *Server) broadcastProtoMessage(kind uint16, msg interface{}) (err os.Error) {
|
||||
server.cmutex.RLock()
|
||||
defer server.cmutex.RUnlock()
|
||||
|
||||
for x := range server.clients.Iter() {
|
||||
client := x.(*ClientConnection)
|
||||
if client.state != StateClientAuthenticated {
|
||||
continue
|
||||
}
|
||||
err :=client.sendProtoMessage(kind, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (server *Server) handleIncomingMessage(client *ClientConnection, msg *Message) {
|
||||
log.Stdoutf("Handle Incoming Message")
|
||||
switch msg.kind {
|
||||
case MessagePing:
|
||||
server.handlePingMessage(msg.client, msg)
|
||||
case MessageChannelRemove:
|
||||
server.handlePingMessage(msg.client, msg)
|
||||
case MessageChannelState:
|
||||
server.handleChannelStateMessage(msg.client, msg)
|
||||
case MessageUserState:
|
||||
server.handleUserStateMessage(msg.client, msg)
|
||||
case MessageUserRemove:
|
||||
server.handleUserRemoveMessage(msg.client, msg)
|
||||
case MessageBanList:
|
||||
server.handleBanListMessage(msg.client, msg)
|
||||
case MessageTextMessage:
|
||||
server.handleTextMessage(msg.client, msg)
|
||||
case MessageACL:
|
||||
server.handleAclMessage(msg.client, msg)
|
||||
case MessageQueryUsers:
|
||||
server.handleQueryUsers(msg.client, msg)
|
||||
case MessageCryptSetup:
|
||||
server.handleCryptSetup(msg.client, msg)
|
||||
case MessageContextActionAdd:
|
||||
log.Stdoutf("MessageContextActionAdd from client")
|
||||
case MessageContextAction:
|
||||
log.Stdoutf("MessageContextAction from client")
|
||||
case MessageUserList:
|
||||
log.Stdoutf("MessageUserList from client")
|
||||
case MessageVoiceTarget:
|
||||
log.Stdoutf("MessageVoiceTarget from client")
|
||||
case MessagePermissionQuery:
|
||||
log.Stdoutf("MessagePermissionQuery from client")
|
||||
case MessageCodecVersion:
|
||||
log.Stdoutf("MessageCodecVersion from client")
|
||||
case MessageUserStats:
|
||||
server.handleUserStatsMessage(msg.client, msg)
|
||||
case MessageRequestBlob:
|
||||
log.Stdoutf("MessageRequestBlob from client")
|
||||
case MessageServerConfig:
|
||||
log.Stdoutf("MessageServerConfig from client")
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) multiplexer() {
|
||||
for {
|
||||
_ = <-server.outgoing
|
||||
log.Stdoutf("recvd message to multiplex")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) SetupUDP() (err os.Error) {
|
||||
addr := &net.UDPAddr{
|
||||
Port: s.port,
|
||||
}
|
||||
s.udpconn, err = net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) SendUDP() {
|
||||
for {
|
||||
msg := <-s.udpsend
|
||||
if msg.client != nil {
|
||||
// These are to be crypted...
|
||||
crypted := make([]byte, len(msg.buf)+4)
|
||||
msg.client.crypt.Encrypt(msg.buf, crypted)
|
||||
s.udpconn.WriteTo(crypted, msg.client.udpaddr)
|
||||
} else if msg.address != nil {
|
||||
s.udpconn.WriteTo(msg.buf, msg.address)
|
||||
} else {
|
||||
// Skipping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for and handle UDP packets.
|
||||
func (server *Server) ListenUDP() {
|
||||
buf := make([]byte, UDPPacketSize)
|
||||
for {
|
||||
nread, remote, err := server.udpconn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
// Not much to do here. This is bad, of course. Should we panic this server instance?
|
||||
continue
|
||||
}
|
||||
|
||||
udpaddr, ok := remote.(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Stdoutf("No UDPAddr in read packet. Disabling UDP. (Windows?)")
|
||||
return
|
||||
}
|
||||
|
||||
// Length 12 is for ping datagrams from the ConnectDialog.
|
||||
if nread == 12 {
|
||||
readbuf := bytes.NewBuffer(buf)
|
||||
var (
|
||||
tmp32 uint32
|
||||
rand uint64
|
||||
)
|
||||
_ = binary.Read(readbuf, binary.BigEndian, &tmp32)
|
||||
_ = binary.Read(readbuf, binary.BigEndian, &rand)
|
||||
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 24))
|
||||
_ = binary.Write(buffer, binary.BigEndian, uint32((1<<16)|(2<<8)|2))
|
||||
_ = binary.Write(buffer, binary.BigEndian, rand)
|
||||
_ = binary.Write(buffer, binary.BigEndian, uint32(server.clients.Len()))
|
||||
_ = binary.Write(buffer, binary.BigEndian, uint32(server.MaxUsers))
|
||||
_ = binary.Write(buffer, binary.BigEndian, uint32(server.MaxBandwidth))
|
||||
|
||||
server.udpsend <- &Message{
|
||||
buf: buffer.Bytes(),
|
||||
address: udpaddr,
|
||||
}
|
||||
} else {
|
||||
var match *ClientConnection
|
||||
plain := make([]byte, nread-4)
|
||||
decrypted := false
|
||||
|
||||
// First, check if any of our clients match the net.UDPAddr...
|
||||
server.cmutex.RLock()
|
||||
for x := range server.clients.Iter() {
|
||||
client := x.(*ClientConnection)
|
||||
if client.udpaddr.String() == udpaddr.String() {
|
||||
match = client
|
||||
}
|
||||
}
|
||||
server.cmutex.RUnlock()
|
||||
|
||||
// No matching client found. We must try to decrypt...
|
||||
if match == nil {
|
||||
server.cmutex.RLock()
|
||||
for x := range server.clients.Iter() {
|
||||
client := x.(*ClientConnection)
|
||||
|
||||
// Try to decrypt.
|
||||
err = client.crypt.Decrypt(buf[0:nread], plain[0:])
|
||||
if err != nil {
|
||||
// Decryption failed. Try another client...
|
||||
continue
|
||||
}
|
||||
|
||||
// Decryption succeeded.
|
||||
decrypted = true
|
||||
|
||||
// If we were able to successfully decrpyt, add
|
||||
// the UDPAddr to the ClientConnection struct.
|
||||
log.Stdoutf("Client UDP connection established.")
|
||||
client.udpaddr = remote.(*net.UDPAddr)
|
||||
match = client
|
||||
|
||||
break
|
||||
}
|
||||
server.cmutex.RUnlock()
|
||||
}
|
||||
|
||||
// We were not able to find a client that could decrypt the incoming
|
||||
// packet. Log it?
|
||||
if match == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !decrypted {
|
||||
err = match.crypt.Decrypt(buf[0:nread], plain[0:])
|
||||
if err != nil {
|
||||
log.Stdoutf("Unable to decrypt from client..")
|
||||
}
|
||||
}
|
||||
|
||||
match.udp = true
|
||||
match.udprecv <- plain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The accept loop of the server.
|
||||
func (s *Server) ListenAndMurmur() {
|
||||
|
||||
// Setup our UDP listener and spawn our reader and writer goroutines
|
||||
s.SetupUDP()
|
||||
go s.ListenUDP()
|
||||
go s.SendUDP()
|
||||
|
||||
// Create a new listening TLS socket.
|
||||
l := NewTLSListener(s.port)
|
||||
if l == nil {
|
||||
log.Stderrf("Unable to create TLS listener")
|
||||
return
|
||||
}
|
||||
|
||||
log.Stderrf("Created new Murmur instance on port %v", s.port)
|
||||
|
||||
// The main accept loop. Basically, we block
|
||||
// until we get a new client connection, and
|
||||
// when we do get a new connection, we spawn
|
||||
// a new Go-routine to handle the client.
|
||||
for {
|
||||
|
||||
// New client connected
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Stderrf("unable to accept()")
|
||||
}
|
||||
|
||||
tls, ok := conn.(*tls.Conn)
|
||||
if !ok {
|
||||
log.Stderrf("Not tls :(")
|
||||
}
|
||||
|
||||
// Force the TLS handshake to get going. We'd like
|
||||
// this to happen as soon as possible, so we can get
|
||||
// at client certificates sooner.
|
||||
tls.Handshake()
|
||||
|
||||
// Create a new client connection from our *tls.Conn
|
||||
// which wraps net.TCPConn.
|
||||
err = s.NewClient(conn)
|
||||
if err != nil {
|
||||
log.Stderrf("Unable to start new client")
|
||||
}
|
||||
|
||||
log.Stdoutf("num clients = %v", s.clients.Len())
|
||||
}
|
||||
}
|
||||
88
tlsserver.go
Normal file
88
tlsserver.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"net"
|
||||
"crypto/tls"
|
||||
"crypto/rand"
|
||||
"encoding/pem"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func NewTLSListener(port int) (rl *tls.Listener) {
|
||||
rl = nil
|
||||
|
||||
// Load the certificate
|
||||
pemBytes, err := ioutil.ReadFile("grumble.crt")
|
||||
if err != nil {
|
||||
log.Stderr("Failed to read server.crt:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode the certificate
|
||||
cert, _ := pem.Decode(pemBytes)
|
||||
if cert == nil {
|
||||
log.Stderr("Failed to parse server.crt")
|
||||
return
|
||||
}
|
||||
|
||||
// Load the private key
|
||||
keyBytes, err := ioutil.ReadFile("grumble.key")
|
||||
if err != nil {
|
||||
log.Stderr("Failed to read server.key.insecure: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode the private key
|
||||
pkPEM, _ := pem.Decode(keyBytes)
|
||||
if pkPEM == nil {
|
||||
log.Stderrf("Failed to parse server.key.insecure: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Determine if we are an RSA private key
|
||||
if pkPEM.Type != "RSA PRIVATE KEY" {
|
||||
log.Stderrf("server.key.insecure is not an RSA private key. Found '%s'",
|
||||
pkPEM.Type)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the PEM file has headers. This will typically
|
||||
// mean that it requires a passphrase to decrypt it. For now,
|
||||
// let us just assume that people will decrypt them for us, so
|
||||
// we can use them without too much work.
|
||||
if len(pkPEM.Headers) != 0 {
|
||||
log.Stderr("server.key.insecure has headers and is probably encrypted.")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the PKCS12 private key.
|
||||
priv, err := x509.ParsePKCS1PrivateKey(pkPEM.Bytes)
|
||||
if err != nil {
|
||||
log.Stderrf("Invalid key in server.key.insecure: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new TLS config.
|
||||
config := new(tls.Config)
|
||||
config.Rand = rand.Reader
|
||||
config.Time = time.Seconds
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0].Certificate = [][]byte{cert.Bytes}
|
||||
config.Certificates[0].PrivateKey = priv
|
||||
|
||||
l, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||
net.ParseIP("0.0.0.0"),
|
||||
port,
|
||||
})
|
||||
if err != nil {
|
||||
log.Stderrf("Cannot bind: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
rl = tls.NewListener(l, config)
|
||||
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue