mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-11 02:28:18 +00:00
450 lines
11 KiB
C++
450 lines
11 KiB
C++
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
|
#ifndef ENGINE_SHARED_NETWORK_H
|
|
#define ENGINE_SHARED_NETWORK_H
|
|
|
|
#include "ringbuffer.h"
|
|
#include "huffman.h"
|
|
|
|
#include <base/math.h>
|
|
|
|
#include <engine/message.h>
|
|
|
|
/*
|
|
|
|
CURRENT:
|
|
packet header: 3 bytes
|
|
unsigned char flags_ack; // 4bit flags, 4bit ack
|
|
unsigned char ack; // 8 bit ack
|
|
unsigned char num_chunks; // 8 bit chunks
|
|
|
|
(unsigned char padding[3]) // 24 bit extra incase it's a connection less packet
|
|
// this is to make sure that it's compatible with the
|
|
// old protocol
|
|
|
|
chunk header: 2-3 bytes
|
|
unsigned char flags_size; // 2bit flags, 6 bit size
|
|
unsigned char size_seq; // 4bit size, 4bit seq
|
|
(unsigned char seq;) // 8bit seq, if vital flag is set
|
|
*/
|
|
|
|
enum
|
|
{
|
|
NETFLAG_ALLOWSTATELESS=1,
|
|
NETSENDFLAG_VITAL=1,
|
|
NETSENDFLAG_CONNLESS=2,
|
|
NETSENDFLAG_FLUSH=4,
|
|
|
|
NETSTATE_OFFLINE=0,
|
|
NETSTATE_CONNECTING,
|
|
NETSTATE_ONLINE,
|
|
|
|
NETBANTYPE_SOFT=1,
|
|
NETBANTYPE_DROP=2
|
|
};
|
|
|
|
|
|
enum
|
|
{
|
|
NET_VERSION = 2,
|
|
|
|
NET_MAX_PACKETSIZE = 1400,
|
|
NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE-6,
|
|
NET_MAX_CHUNKHEADERSIZE = 5,
|
|
NET_PACKETHEADERSIZE = 3,
|
|
NET_MAX_CLIENTS = 64,
|
|
NET_MAX_CONSOLE_CLIENTS = 4,
|
|
NET_MAX_SEQUENCE = 1<<10,
|
|
NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
|
|
|
|
NET_CONNSTATE_OFFLINE=0,
|
|
NET_CONNSTATE_CONNECT=1,
|
|
NET_CONNSTATE_PENDING=2,
|
|
NET_CONNSTATE_ONLINE=3,
|
|
NET_CONNSTATE_ERROR=4,
|
|
|
|
NET_PACKETFLAG_CONTROL=1,
|
|
NET_PACKETFLAG_CONNLESS=2,
|
|
NET_PACKETFLAG_RESEND=4,
|
|
NET_PACKETFLAG_COMPRESSION=8,
|
|
|
|
NET_CHUNKFLAG_VITAL=1,
|
|
NET_CHUNKFLAG_RESEND=2,
|
|
|
|
NET_CTRLMSG_KEEPALIVE=0,
|
|
NET_CTRLMSG_CONNECT=1,
|
|
NET_CTRLMSG_CONNECTACCEPT=2,
|
|
NET_CTRLMSG_ACCEPT=3,
|
|
NET_CTRLMSG_CLOSE=4,
|
|
|
|
NET_CONN_BUFFERSIZE=1024*32,
|
|
|
|
NET_ENUM_TERMINATOR
|
|
};
|
|
|
|
typedef int SECURITY_TOKEN;
|
|
|
|
SECURITY_TOKEN ToSecurityToken(unsigned char* pData);
|
|
|
|
static const unsigned char SECURITY_TOKEN_MAGIC[] = {'T', 'K', 'E', 'N'};
|
|
|
|
enum
|
|
{
|
|
NET_SECURITY_TOKEN_UNKNOWN = -1,
|
|
NET_SECURITY_TOKEN_UNSUPPORTED = 0,
|
|
};
|
|
|
|
typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char* pReason, void *pUser);
|
|
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);
|
|
typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientID, void *pUser);
|
|
|
|
struct CNetChunk
|
|
{
|
|
// -1 means that it's a stateless packet
|
|
// 0 on the client means the server
|
|
int m_ClientID;
|
|
NETADDR m_Address; // only used when client_id == -1
|
|
int m_Flags;
|
|
int m_DataSize;
|
|
const void *m_pData;
|
|
};
|
|
|
|
class CNetChunkHeader
|
|
{
|
|
public:
|
|
int m_Flags;
|
|
int m_Size;
|
|
int m_Sequence;
|
|
|
|
unsigned char *Pack(unsigned char *pData);
|
|
unsigned char *Unpack(unsigned char *pData);
|
|
};
|
|
|
|
class CNetChunkResend
|
|
{
|
|
public:
|
|
int m_Flags;
|
|
int m_DataSize;
|
|
unsigned char *m_pData;
|
|
|
|
int m_Sequence;
|
|
int64 m_LastSendTime;
|
|
int64 m_FirstSendTime;
|
|
};
|
|
|
|
class CNetPacketConstruct
|
|
{
|
|
public:
|
|
int m_Flags;
|
|
int m_Ack;
|
|
int m_NumChunks;
|
|
int m_DataSize;
|
|
unsigned char m_aChunkData[NET_MAX_PAYLOAD];
|
|
};
|
|
|
|
|
|
class CNetConnection
|
|
{
|
|
// TODO: is this needed because this needs to be aware of
|
|
// the ack sequencing number and is also responible for updating
|
|
// that. this should be fixed.
|
|
friend class CNetRecvUnpacker;
|
|
private:
|
|
unsigned short m_Sequence;
|
|
unsigned short m_Ack;
|
|
unsigned m_State;
|
|
|
|
int m_Token;
|
|
SECURITY_TOKEN m_SecurityToken;
|
|
int m_RemoteClosed;
|
|
bool m_BlockCloseMsg;
|
|
bool m_UnknownSeq;
|
|
|
|
TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer;
|
|
|
|
int64 m_LastUpdateTime;
|
|
int64 m_LastRecvTime;
|
|
int64 m_LastSendTime;
|
|
|
|
char m_ErrorString[256];
|
|
|
|
CNetPacketConstruct m_Construct;
|
|
|
|
NETADDR m_PeerAddr;
|
|
NETSOCKET m_Socket;
|
|
NETSTATS m_Stats;
|
|
|
|
//
|
|
void ResetStats();
|
|
void SetError(const char *pString);
|
|
void AckChunks(int Ack);
|
|
|
|
int QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence);
|
|
void SendControl(int ControlMsg, const void *pExtra, int ExtraSize);
|
|
void ResendChunk(CNetChunkResend *pResend);
|
|
void Resend();
|
|
|
|
bool HasSecurityToken;
|
|
|
|
public:
|
|
bool m_TimeoutProtected;
|
|
bool m_TimeoutSituation;
|
|
|
|
void Reset();
|
|
void Init(NETSOCKET Socket, bool BlockCloseMsg);
|
|
int Connect(NETADDR *pAddr);
|
|
void Disconnect(const char *pReason);
|
|
|
|
int Update();
|
|
int Flush();
|
|
|
|
int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED);
|
|
int QueueChunk(int Flags, int DataSize, const void *pData);
|
|
|
|
const char *ErrorString();
|
|
void SignalResend();
|
|
int State() const { return m_State; }
|
|
const NETADDR *PeerAddress() const { return &m_PeerAddr; }
|
|
|
|
void ResetErrorString() { m_ErrorString[0] = 0; }
|
|
const char *ErrorString() const { return m_ErrorString; }
|
|
|
|
// Needed for GotProblems in NetClient
|
|
int64 LastRecvTime() const { return m_LastRecvTime; }
|
|
int64 ConnectTime() const { return m_LastUpdateTime; }
|
|
|
|
int AckSequence() const { return m_Ack; }
|
|
int SeqSequence() const { return m_Sequence; }
|
|
int SecurityToken() const { return m_SecurityToken; }
|
|
void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken);
|
|
|
|
// anti spoof
|
|
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken);
|
|
void SetUnknownSeq() { m_UnknownSeq = true; }
|
|
};
|
|
|
|
class CConsoleNetConnection
|
|
{
|
|
private:
|
|
int m_State;
|
|
|
|
NETADDR m_PeerAddr;
|
|
NETSOCKET m_Socket;
|
|
|
|
char m_aBuffer[NET_MAX_PACKETSIZE];
|
|
int m_BufferOffset;
|
|
|
|
char m_aErrorString[256];
|
|
|
|
bool m_LineEndingDetected;
|
|
char m_aLineEnding[3];
|
|
|
|
public:
|
|
void Init(NETSOCKET Socket, const NETADDR *pAddr);
|
|
void Disconnect(const char *pReason);
|
|
|
|
int State() const { return m_State; }
|
|
const NETADDR *PeerAddress() const { return &m_PeerAddr; }
|
|
const char *ErrorString() const { return m_aErrorString; }
|
|
|
|
void Reset();
|
|
int Update();
|
|
int Send(const char *pLine);
|
|
int Recv(char *pLine, int MaxLength);
|
|
};
|
|
|
|
class CNetRecvUnpacker
|
|
{
|
|
public:
|
|
bool m_Valid;
|
|
|
|
NETADDR m_Addr;
|
|
CNetConnection *m_pConnection;
|
|
int m_CurrentChunk;
|
|
int m_ClientID;
|
|
CNetPacketConstruct m_Data;
|
|
unsigned char m_aBuffer[NET_MAX_PACKETSIZE];
|
|
|
|
CNetRecvUnpacker() { Clear(); }
|
|
void Clear();
|
|
void Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID);
|
|
int FetchChunk(CNetChunk *pChunk);
|
|
};
|
|
|
|
// server side
|
|
class CNetServer
|
|
{
|
|
struct CSlot
|
|
{
|
|
public:
|
|
CNetConnection m_Connection;
|
|
};
|
|
|
|
NETSOCKET m_Socket;
|
|
class CNetBan *m_pNetBan;
|
|
CSlot m_aSlots[NET_MAX_CLIENTS];
|
|
int m_MaxClients;
|
|
int m_MaxClientsPerIP;
|
|
|
|
NETFUNC_NEWCLIENT m_pfnNewClient;
|
|
NETFUNC_NEWCLIENT_NOAUTH m_pfnNewClientNoAuth;
|
|
NETFUNC_DELCLIENT m_pfnDelClient;
|
|
void *m_UserPtr;
|
|
|
|
|
|
int m_NumConAttempts; // log flooding attacks
|
|
int64 m_TimeNumConAttempts;
|
|
unsigned char m_SecurityTokenSeed[16];
|
|
|
|
CNetRecvUnpacker m_RecvUnpacker;
|
|
|
|
void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
|
|
void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet);
|
|
bool ClientExists(const NETADDR &Addr);
|
|
void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
|
|
|
|
int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool NoAuth=false);
|
|
int NumClientsWithAddr(NETADDR Addr);
|
|
void SendMsgs(NETADDR &Addr, const CMsgPacker *Msgs[], int num);
|
|
|
|
public:
|
|
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
|
|
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
|
|
|
|
//
|
|
bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags);
|
|
int Close();
|
|
|
|
//
|
|
int Recv(CNetChunk *pChunk);
|
|
int Send(CNetChunk *pChunk);
|
|
int Update();
|
|
|
|
//
|
|
int Drop(int ClientID, const char *pReason);
|
|
|
|
// status requests
|
|
const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
|
|
bool HasSecurityToken(int ClientID) const { return m_aSlots[ClientID].m_Connection.SecurityToken() != NET_SECURITY_TOKEN_UNSUPPORTED; }
|
|
NETSOCKET Socket() const { return m_Socket; }
|
|
class CNetBan *NetBan() const { return m_pNetBan; }
|
|
int NetType() const { return m_Socket.type; }
|
|
int MaxClients() const { return m_MaxClients; }
|
|
|
|
//
|
|
void SetMaxClientsPerIP(int Max);
|
|
bool SetTimedOut(int ClientID, int OrigID);
|
|
void SetTimeoutProtected(int ClientID);
|
|
|
|
int ResetErrorString(int ClientID);
|
|
const char *ErrorString(int ClientID);
|
|
|
|
// anti spoof
|
|
SECURITY_TOKEN GetToken(const NETADDR &Addr);
|
|
// vanilla token/gametick shouldn't be negative
|
|
SECURITY_TOKEN GetVanillaToken(const NETADDR &Addr) { return absolute(GetToken(Addr)); }
|
|
};
|
|
|
|
class CNetConsole
|
|
{
|
|
struct CSlot
|
|
{
|
|
CConsoleNetConnection m_Connection;
|
|
};
|
|
|
|
NETSOCKET m_Socket;
|
|
class CNetBan *m_pNetBan;
|
|
CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];
|
|
|
|
NETFUNC_NEWCLIENT m_pfnNewClient;
|
|
NETFUNC_DELCLIENT m_pfnDelClient;
|
|
void *m_UserPtr;
|
|
|
|
CNetRecvUnpacker m_RecvUnpacker;
|
|
|
|
public:
|
|
void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
|
|
|
|
//
|
|
bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int Flags);
|
|
int Close();
|
|
|
|
//
|
|
int Recv(char *pLine, int MaxLength, int *pClientID = 0);
|
|
int Send(int ClientID, const char *pLine);
|
|
int Update();
|
|
|
|
//
|
|
int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr);
|
|
int Drop(int ClientID, const char *pReason);
|
|
|
|
// status requests
|
|
const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
|
|
class CNetBan *NetBan() const { return m_pNetBan; }
|
|
};
|
|
|
|
|
|
|
|
// client side
|
|
class CNetClient
|
|
{
|
|
CNetConnection m_Connection;
|
|
CNetRecvUnpacker m_RecvUnpacker;
|
|
public:
|
|
NETSOCKET m_Socket;
|
|
// openness
|
|
bool Open(NETADDR BindAddr, int Flags);
|
|
int Close();
|
|
|
|
// connection state
|
|
int Disconnect(const char *Reason);
|
|
int Connect(NETADDR *Addr);
|
|
|
|
// communication
|
|
int Recv(CNetChunk *Chunk);
|
|
int Send(CNetChunk *Chunk);
|
|
|
|
// pumping
|
|
int Update();
|
|
int Flush();
|
|
|
|
int ResetErrorString();
|
|
|
|
// error and state
|
|
int NetType() const { return m_Socket.type; }
|
|
int State();
|
|
int GotProblems();
|
|
const char *ErrorString();
|
|
|
|
bool SecurityTokenUnknown() { return m_Connection.SecurityToken() == NET_SECURITY_TOKEN_UNKNOWN; }
|
|
};
|
|
|
|
|
|
|
|
// TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywere
|
|
class CNetBase
|
|
{
|
|
static IOHANDLE ms_DataLogSent;
|
|
static IOHANDLE ms_DataLogRecv;
|
|
static CHuffman ms_Huffman;
|
|
public:
|
|
static void OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv);
|
|
static void CloseLog();
|
|
static void Init();
|
|
static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
|
|
static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);
|
|
|
|
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
|
|
static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize);
|
|
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken);
|
|
|
|
|
|
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket);
|
|
|
|
// The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
|
|
static int IsSeqInBackroom(int Seq, int Ack);
|
|
};
|
|
|
|
|
|
#endif
|