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