/* (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 #include /* 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 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; } void SetSequence(int Sequence) { m_Sequence = Sequence; } }; 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) { return GetClientSlot(Addr) != -1; }; int GetClientSlot(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 VanillaAuth=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