Server-side crypt resync. Properly store ping stats.

This commit is contained in:
Mikkel Krautz 2011-05-13 22:26:15 +02:00
parent 0b086d6f57
commit a2c9a15386
4 changed files with 131 additions and 59 deletions

View file

@ -16,6 +16,7 @@ import (
"grumble/blobstore"
"io"
"packetdatastream"
"time"
)
// A client connection
@ -39,10 +40,19 @@ type Client struct {
disconnected bool
lastResync int64
crypt *cryptstate.CryptState
codecs []int32
udp bool
// Ping stats
UdpPingAvg float32
UdpPingVar float32
UdpPackets uint32
TcpPingAvg float32
TcpPingVar float32
TcpPackets uint32
// If the client is a registered user on the server,
// the user field will point to the registration record.
user *User
@ -573,3 +583,19 @@ func (client *Client) sendChannelTree(channel *Channel) {
client.sendChannelTree(subchannel)
}
}
// Try to do a crypto resync
func (client *Client) cryptResync() {
goodElapsed := time.Seconds() - client.crypt.LastGoodTime
if goodElapsed > 5 {
requestElapsed := time.Seconds() - client.lastResync
if requestElapsed > 5 {
client.lastResync = time.Seconds()
cryptsetup := &mumbleproto.CryptSetup{}
err := client.sendProtoMessage(MessageCryptSetup, cryptsetup)
if err != nil {
client.Panicf("%v", err)
}
}
}
}

View file

@ -113,8 +113,39 @@ func (server *Server) handlePingMessage(client *Client, msg *Message) {
return
}
// Phony response for ping messages. We don't keep stats
// for this yet.
if ping.Good != nil {
client.crypt.RemoteGood = uint32(*ping.Good)
}
if ping.Late != nil {
client.crypt.RemoteLate = *ping.Late
}
if ping.Lost != nil {
client.crypt.RemoteLost = *ping.Lost
}
if ping.Resync != nil {
client.crypt.RemoteResync = *ping.Resync
}
if ping.UdpPingAvg != nil {
client.UdpPingAvg = *ping.UdpPingAvg
}
if ping.UdpPingVar != nil {
client.UdpPingVar = *ping.UdpPingVar
}
if ping.UdpPackets != nil {
client.UdpPackets = *ping.UdpPackets
}
if ping.TcpPingAvg != nil {
client.TcpPingAvg = *ping.TcpPingAvg
}
if ping.TcpPingVar != nil {
client.TcpPingVar = *ping.TcpPingVar
}
if ping.TcpPackets != nil {
client.TcpPackets = *ping.TcpPackets
}
client.sendProtoMessage(MessagePing, &mumbleproto.Ping{
Timestamp: ping.Timestamp,
Good: proto.Uint32(uint32(client.crypt.Good)),

View file

@ -9,6 +9,7 @@ import (
"crypto/aes"
"crypto/rand"
"os"
"time"
)
const AESBlockSize = 16
@ -20,15 +21,16 @@ type CryptState struct {
DecryptIV [AESBlockSize]byte
decryptHistory [DecryptHistorySize]byte
Good int
Late int
Lost int
Resync int
LastGoodTime int64
RemoteGood int
RemoteLate int
RemoteLost int
RemoteResync int
Good uint32
Late uint32
Lost uint32
Resync uint32
RemoteGood uint32
RemoteLate uint32
RemoteLost uint32
RemoteResync uint32
cipher *aes.Cipher
}
@ -196,10 +198,18 @@ func (cs *CryptState) Decrypt(dst, src []byte) (err os.Error) {
}
cs.Good += 1
cs.Late += late
cs.Lost += lost
if late > 0 {
cs.Late += uint32(late)
} else {
cs.Late -= uint32(-late)
}
if lost > 0 {
cs.Lost = uint32(lost)
} else {
cs.Lost = uint32(-lost)
}
// restart timer
cs.LastGoodTime = time.Seconds()
return
}

View file

@ -492,6 +492,7 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
// Send CryptState information to the client so it can establish an UDP connection,
// if it wishes.
client.lastResync = time.Seconds()
err = client.sendProtoMessage(MessageCryptSetup, &mumbleproto.CryptSetup{
Key: client.crypt.RawKey[0:],
ClientNonce: client.crypt.DecryptIV[0:],
@ -923,6 +924,12 @@ func (server *Server) ListenUDP() {
address: udpaddr,
}
} else {
server.handleUdpPacket(udpaddr, buf, nread)
}
}
}
func (server *Server) handleUdpPacket(udpaddr *net.UDPAddr, buf []byte, nread int) {
var match *Client
plain := make([]byte, nread-4)
@ -933,20 +940,23 @@ func (server *Server) ListenUDP() {
// If we don't find any matches, we look in the 'hclients',
// which maps a host address to a slice of clients.
server.hmutex.Lock()
defer server.hmutex.Unlock()
client, ok := server.hpclients[udpaddr.String()]
if ok {
err = client.crypt.Decrypt(plain[0:], buf[0:nread])
err := client.crypt.Decrypt(plain[0:], buf[0:nread])
if err != nil {
server.Panicf("Unable to decrypt incoming packet for client %v (host-port matched)", client)
client.cryptResync()
return
}
match = client
} else {
host := udpaddr.IP.String()
hostclients := server.hclients[host]
for _, client := range hostclients {
err = client.crypt.Decrypt(plain[0:], buf[0:nread])
err := client.crypt.Decrypt(plain[0:], buf[0:nread])
if err != nil {
continue
client.cryptResync()
return
} else {
match = client
}
@ -956,19 +966,14 @@ func (server *Server) ListenUDP() {
server.hpclients[udpaddr.String()] = match
}
}
server.hmutex.Unlock()
// No client found.
if match == nil {
server.Printf("Sender of UDP packet could not be determined. Packet dropped.")
continue
return
}
match.udp = true
match.udprecv <- plain
}
}
}
// Clear the ACL cache
func (s *Server) ClearACLCache() {