From a2c9a15386ee7abc3adc77beaf320f1ec8ca6d2a Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Fri, 13 May 2011 22:26:15 +0200 Subject: [PATCH] Server-side crypt resync. Properly store ping stats. --- client.go | 32 +++++++++++-- message.go | 35 +++++++++++++- pkg/cryptstate/cryptstate.go | 32 ++++++++----- server.go | 91 +++++++++++++++++++----------------- 4 files changed, 131 insertions(+), 59 deletions(-) diff --git a/client.go b/client.go index d8ed6ce..d53b6fb 100644 --- a/client.go +++ b/client.go @@ -16,6 +16,7 @@ import ( "grumble/blobstore" "io" "packetdatastream" + "time" ) // A client connection @@ -39,9 +40,18 @@ type Client struct { disconnected bool - crypt *cryptstate.CryptState - codecs []int32 - udp 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. @@ -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) + } + } + } +} diff --git a/message.go b/message.go index ba12c37..1822acc 100644 --- a/message.go +++ b/message.go @@ -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)), diff --git a/pkg/cryptstate/cryptstate.go b/pkg/cryptstate/cryptstate.go index 7e48895..692d09f 100644 --- a/pkg/cryptstate/cryptstate.go +++ b/pkg/cryptstate/cryptstate.go @@ -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 } diff --git a/server.go b/server.go index 88056a4..84b3bbe 100644 --- a/server.go +++ b/server.go @@ -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,53 +924,57 @@ func (server *Server) ListenUDP() { address: udpaddr, } } else { - var match *Client - plain := make([]byte, nread-4) - - // Determine which client sent the the packet. First, we - // check the map 'hpclients' in the server struct. It maps - // a hort-post combination to a client. - // - // 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() - client, ok := server.hpclients[udpaddr.String()] - if ok { - 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) - } - match = client - } else { - host := udpaddr.IP.String() - hostclients := server.hclients[host] - for _, client := range hostclients { - err = client.crypt.Decrypt(plain[0:], buf[0:nread]) - if err != nil { - continue - } else { - match = client - } - } - if match != nil { - match.udpaddr = udpaddr - 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 - } - - match.udp = true - match.udprecv <- plain + server.handleUdpPacket(udpaddr, buf, nread) } } } +func (server *Server) handleUdpPacket(udpaddr *net.UDPAddr, buf []byte, nread int) { + var match *Client + plain := make([]byte, nread-4) + + // Determine which client sent the the packet. First, we + // check the map 'hpclients' in the server struct. It maps + // a hort-post combination to a client. + // + // 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]) + if err != nil { + 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]) + if err != nil { + client.cryptResync() + return + } else { + match = client + } + } + if match != nil { + match.udpaddr = udpaddr + server.hpclients[udpaddr.String()] = match + } + } + + if match == nil { + return + } + + match.udp = true + match.udprecv <- plain +} + // Clear the ACL cache func (s *Server) ClearACLCache() { s.aclcache = NewACLCache()