nex-go/client.go
2023-06-07 23:18:05 -04:00

246 lines
7 KiB
Go

package nex
import (
"crypto/rc4"
"fmt"
"net"
"time"
)
// Client represents a connected or non-connected PRUDP client
type Client struct {
address *net.UDPAddr
server *Server
cipher *rc4.Cipher
decipher *rc4.Cipher
prudpProtocolMinorVersion int
supportedFunctions int
signatureKey []byte
signatureBase int
serverConnectionSignature []byte
clientConnectionSignature []byte
sessionKey []byte
sequenceIDIn *Counter
sequenceIDOut *Counter
pid uint32
stationURLs []string
connectionID uint32
pingCheckTimer *time.Timer
pingKickTimer *time.Timer
connected bool
}
// Reset resets the Client to default values
func (client *Client) Reset() error {
client.sequenceIDIn = NewCounter(0)
client.sequenceIDOut = NewCounter(0)
client.UpdateAccessKey(client.Server().AccessKey())
err := client.UpdateRC4Key([]byte("CD&ML"))
if err != nil {
return fmt.Errorf("Failed to update client RC4 key. %s", err.Error())
}
if client.Server().PRUDPVersion() == 0 {
client.SetServerConnectionSignature(make([]byte, 4))
client.SetClientConnectionSignature(make([]byte, 4))
} else {
client.SetServerConnectionSignature([]byte{})
client.SetClientConnectionSignature([]byte{})
}
client.SetConnected(false)
return nil
}
// Address returns the clients UDP address
func (client *Client) Address() *net.UDPAddr {
return client.address
}
// Server returns the server the client is currently connected to
func (client *Client) Server() *Server {
return client.server
}
// PRUDPProtocolMinorVersion returns the client PRUDP minor version
func (client *Client) PRUDPProtocolMinorVersion() int {
return client.prudpProtocolMinorVersion
}
// SetPRUDPProtocolMinorVersion sets the client PRUDP minor
func (client *Client) SetPRUDPProtocolMinorVersion(prudpProtocolMinorVersion int) {
client.prudpProtocolMinorVersion = prudpProtocolMinorVersion
}
// SupportedFunctions returns the supported PRUDP functions by the client
func (client *Client) SupportedFunctions() int {
return client.supportedFunctions
}
// SetSupportedFunctions sets the supported PRUDP functions by the client
func (client *Client) SetSupportedFunctions(supportedFunctions int) {
client.supportedFunctions = supportedFunctions
}
// UpdateRC4Key sets the client RC4 stream key
func (client *Client) UpdateRC4Key(key []byte) error {
cipher, err := rc4.NewCipher(key)
if err != nil {
return fmt.Errorf("Failed to create RC4 cipher. %s", err.Error())
}
client.cipher = cipher
decipher, err := rc4.NewCipher(key)
if err != nil {
return fmt.Errorf("Failed to create RC4 decipher. %s", err.Error())
}
client.decipher = decipher
return nil
}
// Cipher returns the RC4 cipher stream for out-bound packets
func (client *Client) Cipher() *rc4.Cipher {
return client.cipher
}
// Decipher returns the RC4 cipher stream for in-bound packets
func (client *Client) Decipher() *rc4.Cipher {
return client.decipher
}
// UpdateAccessKey sets the client signature base and signature key
func (client *Client) UpdateAccessKey(accessKey string) {
client.signatureBase = sum([]byte(accessKey))
client.signatureKey = MD5Hash([]byte(accessKey))
}
// SignatureBase returns the v0 checksum signature base
func (client *Client) SignatureBase() int {
return client.signatureBase
}
// SignatureKey returns signature key
func (client *Client) SignatureKey() []byte {
return client.signatureKey
}
// SetServerConnectionSignature sets the clients server-side connection signature
func (client *Client) SetServerConnectionSignature(serverConnectionSignature []byte) {
client.serverConnectionSignature = serverConnectionSignature
}
// ServerConnectionSignature returns the clients server-side connection signature
func (client *Client) ServerConnectionSignature() []byte {
return client.serverConnectionSignature
}
// SetClientConnectionSignature sets the clients client-side connection signature
func (client *Client) SetClientConnectionSignature(clientConnectionSignature []byte) {
client.clientConnectionSignature = clientConnectionSignature
}
// ClientConnectionSignature returns the clients client-side connection signature
func (client *Client) ClientConnectionSignature() []byte {
return client.clientConnectionSignature
}
// SequenceIDCounterOut returns the clients packet SequenceID counter for out-going packets
func (client *Client) SequenceIDCounterOut() *Counter {
return client.sequenceIDOut
}
// SequenceIDCounterIn returns the clients packet SequenceID counter for incoming packets
func (client *Client) SequenceIDCounterIn() *Counter {
return client.sequenceIDIn
}
// SetSessionKey sets the clients session key
func (client *Client) SetSessionKey(sessionKey []byte) {
client.sessionKey = sessionKey
}
// SessionKey returns the clients session key
func (client *Client) SessionKey() []byte {
return client.sessionKey
}
// SetPID sets the clients NEX PID
func (client *Client) SetPID(pid uint32) {
client.pid = pid
}
// PID returns the clients NEX PID
func (client *Client) PID() uint32 {
return client.pid
}
// SetStationURLs sets the clients Station URLs
func (client *Client) SetStationURLs(stationURLs []string) {
client.stationURLs = stationURLs
}
// StationURLs returns the clients Station URLs
func (client *Client) StationURLs() []string {
return client.stationURLs
}
// SetConnectionID sets the clients Connection ID
func (client *Client) SetConnectionID(connectionID uint32) {
client.connectionID = connectionID
}
// ConnectionID returns the clients Connection ID
func (client *Client) ConnectionID() uint32 {
return client.connectionID
}
// SetConnected sets the clients connection status
func (client *Client) SetConnected(connected bool) {
client.connected = connected
}
// IncreasePingTimeoutTime adds a number of seconds to the check timer
func (client *Client) IncreasePingTimeoutTime(seconds int) {
//Stop the kick timer if we get something back
if client.pingKickTimer != nil {
client.pingKickTimer.Stop()
}
//and reset the check timer
if client.pingCheckTimer != nil {
client.pingCheckTimer.Reset(time.Second * time.Duration(seconds))
}
}
// StartTimeoutTimer begins the packet timeout timer
func (client *Client) StartTimeoutTimer() {
//if we haven't gotten a ping *from* the client, send them one to check all is well
client.pingCheckTimer = time.AfterFunc(time.Second*time.Duration(client.server.PingTimeout()), func() {
client.server.SendPing(client)
//if we *still* get nothing, they're gone
client.pingKickTimer = time.AfterFunc(time.Second*time.Duration(client.server.PingTimeout()), func() {
client.server.TimeoutKick(client)
})
})
}
// NewClient returns a new PRUDP client
func NewClient(address *net.UDPAddr, server *Server) *Client {
client := &Client{
address: address,
server: server,
}
err := client.Reset()
if err != nil {
// TODO - Should this return the error too?
logger.Error(err.Error())
return nil
}
return client
}