ddnet/src/engine/shared/network.h

551 lines
15 KiB
C
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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. */
2010-05-29 07:25:38 +00:00
#ifndef ENGINE_SHARED_NETWORK_H
#define ENGINE_SHARED_NETWORK_H
2007-07-13 13:40:04 +00:00
#include "ringbuffer.h"
#include "stun.h"
2015-08-13 08:58:47 +00:00
#include <base/math.h>
2022-06-16 15:06:15 +00:00
#include <base/system.h>
2015-08-13 08:58:47 +00:00
2022-06-16 15:06:15 +00:00
class CHuffman;
class CMsgPacker;
class CNetBan;
2015-08-13 08:58:47 +00:00
2009-10-27 14:38:53 +00:00
/*
2009-10-27 14:38:53 +00:00
CURRENT:
packet header: 3 bytes
2020-04-13 09:27:30 +00:00
unsigned char flags_ack; // 6bit flags, 2bit ack
0.6: ORNCaaAA
0.6.5: ORNCTUAA
0.7: --NORCAA
2009-10-27 14:38:53 +00:00
unsigned char ack; // 8 bit ack
unsigned char num_chunks; // 8 bit chunks
2018-07-10 09:29:02 +00:00
(unsigned char padding[3]) // 24 bit extra in case it's a connection less packet
2009-10-27 14:38:53 +00:00
// this is to make sure that it's compatible with the
// old protocol
2007-07-13 13:40:04 +00:00
2009-10-27 14:38:53 +00:00
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
*/
2007-07-13 13:40:04 +00:00
enum
{
NETFLAG_ALLOWSTATELESS = 1,
NETSENDFLAG_VITAL = 1,
NETSENDFLAG_CONNLESS = 2,
NETSENDFLAG_FLUSH = 4,
NETSENDFLAG_EXTENDED = 8,
NETSTATE_OFFLINE = 0,
2007-07-13 13:40:04 +00:00
NETSTATE_CONNECTING,
NETSTATE_ONLINE,
NETBANTYPE_SOFT = 1,
NETBANTYPE_DROP = 2
2007-07-13 13:40:04 +00:00
};
2009-10-27 14:38:53 +00:00
enum
{
NET_VERSION = 2,
NET_MAX_PACKETSIZE = 1400,
NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE - 6,
2009-10-27 14:38:53 +00:00
NET_MAX_CHUNKHEADERSIZE = 5,
NET_PACKETHEADERSIZE = 3,
2013-12-31 05:13:57 +00:00
NET_MAX_CLIENTS = 64,
2011-07-30 11:40:01 +00:00
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_UNUSED = 1 << 0,
NET_PACKETFLAG_TOKEN = 1 << 1,
NET_PACKETFLAG_CONTROL = 1 << 2,
NET_PACKETFLAG_CONNLESS = 1 << 3,
NET_PACKETFLAG_RESEND = 1 << 4,
NET_PACKETFLAG_COMPRESSION = 1 << 5,
// NOT SENT VIA THE NETWORK DIRECTLY:
NET_PACKETFLAG_EXTENDED = 1 << 6,
2009-10-27 14:38:53 +00:00
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_CONNLIMIT_IPS = 16,
2016-04-27 20:09:18 +00:00
2009-10-27 14:38:53 +00:00
NET_ENUM_TERMINATOR
};
typedef int SECURITY_TOKEN;
2017-03-21 10:24:44 +00:00
SECURITY_TOKEN ToSecurityToken(unsigned char *pData);
extern const unsigned char SECURITY_TOKEN_MAGIC[4];
enum
{
NET_SECURITY_TOKEN_UNKNOWN = -1,
NET_SECURITY_TOKEN_UNSUPPORTED = 0,
};
2009-10-27 14:38:53 +00:00
2017-03-21 10:24:44 +00:00
typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char *pReason, void *pUser);
2020-03-29 02:36:38 +00:00
typedef int (*NETFUNC_NEWCLIENT_CON)(int ClientID, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser, bool Sixup);
typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientID, void *pUser);
2015-08-23 15:51:28 +00:00
typedef int (*NETFUNC_CLIENTREJOIN)(int ClientID, void *pUser);
2009-10-27 14:38:53 +00:00
struct CNetChunk
{
2010-05-29 07:25:38 +00:00
// -1 means that it's a stateless packet
// 0 on the client means the server
2009-10-27 14:38:53 +00:00
int m_ClientID;
2010-05-29 07:25:38 +00:00
NETADDR m_Address; // only used when client_id == -1
2009-10-27 14:38:53 +00:00
int m_Flags;
int m_DataSize;
const void *m_pData;
// only used if the flags contain NETSENDFLAG_EXTENDED and NETSENDFLAG_CONNLESS
unsigned char m_aExtraData[4];
2009-10-27 14:38:53 +00:00
};
2009-10-27 14:38:53 +00:00
class CNetChunkHeader
{
public:
int m_Flags;
int m_Size;
int m_Sequence;
2020-10-27 17:57:14 +00:00
unsigned char *Pack(unsigned char *pData, int Split = 4);
unsigned char *Unpack(unsigned char *pData, int Split = 4);
2009-10-27 14:38:53 +00:00
};
2007-07-13 13:40:04 +00:00
2009-10-27 14:38:53 +00:00
class CNetChunkResend
{
public:
int m_Flags;
int m_DataSize;
unsigned char *m_pData;
int m_Sequence;
2021-06-23 05:05:49 +00:00
int64_t m_LastSendTime;
int64_t m_FirstSendTime;
2009-10-27 14:38:53 +00:00
};
class CNetPacketConstruct
2007-07-13 13:40:04 +00:00
{
public:
2009-10-27 14:38:53 +00:00
int m_Flags;
int m_Ack;
int m_NumChunks;
int m_DataSize;
unsigned char m_aChunkData[NET_MAX_PAYLOAD];
unsigned char m_aExtraData[4];
2009-10-27 14:38:53 +00:00
};
enum class CONNECTIVITY
{
UNKNOWN,
CHECKING,
UNREACHABLE,
REACHABLE,
ADDRESS_KNOWN,
};
class CStun
{
class CProtocol
{
int m_Index;
NETSOCKET m_Socket;
CStunData m_Stun;
bool m_HaveStunServer = false;
NETADDR m_StunServer;
bool m_HaveAddr = false;
NETADDR m_Addr;
int64_t m_LastResponse = -1;
int64_t m_NextTry = -1;
int m_NumUnsuccessfulTries = -1;
public:
CProtocol(int Index, NETSOCKET Socket);
void FeedStunServer(NETADDR StunServer);
void Refresh();
void Update();
bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize);
CONNECTIVITY GetConnectivity(NETADDR *pGlobalAddr);
};
CProtocol m_aProtocols[2];
public:
CStun(NETSOCKET Socket);
void FeedStunServer(NETADDR StunServer);
void Refresh();
void Update();
bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize);
CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr);
};
2009-10-27 14:38:53 +00:00
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;
2009-10-27 14:38:53 +00:00
private:
unsigned short m_Sequence;
unsigned short m_Ack;
unsigned short m_PeerAck;
2009-10-27 14:38:53 +00:00
unsigned m_State;
SECURITY_TOKEN m_SecurityToken;
2009-10-27 14:38:53 +00:00
int m_RemoteClosed;
bool m_BlockCloseMsg;
bool m_UnknownSeq;
2020-10-27 17:57:14 +00:00
CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer;
2021-06-23 05:05:49 +00:00
int64_t m_LastUpdateTime;
int64_t m_LastRecvTime;
int64_t m_LastSendTime;
2020-10-27 17:57:14 +00:00
char m_aErrorString[256];
2009-10-27 14:38:53 +00:00
CNetPacketConstruct m_Construct;
NETADDR m_aConnectAddrs[16];
int m_NumConnectAddrs;
2009-10-27 14:38:53 +00:00
NETADDR m_PeerAddr;
NETSOCKET m_Socket;
NETSTATS m_Stats;
2009-10-27 14:38:53 +00:00
//
void ResetStats();
void SetError(const char *pString);
void AckChunks(int Ack);
2010-05-29 07:25:38 +00:00
int QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence);
void SendConnect();
2009-10-27 14:38:53 +00:00
void SendControl(int ControlMsg, const void *pExtra, int ExtraSize);
void ResendChunk(CNetChunkResend *pResend);
void Resend();
2009-10-27 14:38:53 +00:00
public:
bool m_TimeoutProtected;
bool m_TimeoutSituation;
void Reset(bool Rejoin = false);
void Init(NETSOCKET Socket, bool BlockCloseMsg);
int Connect(const NETADDR *pAddr, int NumAddrs);
2009-10-27 14:38:53 +00:00
void Disconnect(const char *pReason);
int Update();
int Flush();
2009-10-27 14:38:53 +00:00
int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED);
2010-05-29 07:25:38 +00:00
int QueueChunk(int Flags, int DataSize, const void *pData);
2009-10-27 14:38:53 +00:00
const char *ErrorString();
void SignalResend();
int State() const { return m_State; }
2011-12-29 22:36:53 +00:00
const NETADDR *PeerAddress() const { return &m_PeerAddr; }
void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const
{
*ppAddrs = m_aConnectAddrs;
*pNumAddrs = m_NumConnectAddrs;
}
2020-10-27 17:57:14 +00:00
void ResetErrorString() { m_aErrorString[0] = 0; }
const char *ErrorString() const { return m_aErrorString; }
2009-10-27 14:38:53 +00:00
// Needed for GotProblems in NetClient
2021-06-23 05:05:49 +00:00
int64_t LastRecvTime() const { return m_LastRecvTime; }
int64_t ConnectTime() const { return m_LastUpdateTime; }
2009-10-27 14:38:53 +00:00
int AckSequence() const { return m_Ack; }
2014-08-09 15:53:24 +00:00
int SeqSequence() const { return m_Sequence; }
int SecurityToken() const { return m_SecurityToken; }
CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *ResendBuffer() { return &m_Buffer; }
2020-10-27 17:57:14 +00:00
void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup);
2015-08-12 20:43:37 +00:00
// anti spoof
2020-03-29 02:36:38 +00:00
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup);
void SetUnknownSeq() { m_UnknownSeq = true; }
void SetSequence(int Sequence) { m_Sequence = Sequence; }
2020-03-29 02:36:38 +00:00
bool m_Sixup;
SECURITY_TOKEN m_Token;
2009-10-27 14:38:53 +00:00
};
2011-07-02 06:36:14 +00:00
class CConsoleNetConnection
{
private:
2011-07-30 11:40:01 +00:00
int m_State;
2011-07-02 06:36:14 +00:00
NETADDR m_PeerAddr;
NETSOCKET m_Socket;
char m_aBuffer[NET_MAX_PACKETSIZE];
2011-07-30 11:40:01 +00:00
int m_BufferOffset;
2011-07-02 06:36:14 +00:00
char m_aErrorString[256];
2011-07-30 11:40:01 +00:00
bool m_LineEndingDetected;
char m_aLineEnding[3];
2011-07-02 06:36:14 +00:00
public:
void Init(NETSOCKET Socket, const NETADDR *pAddr);
void Disconnect(const char *pReason);
int State() const { return m_State; }
2011-12-29 22:36:53 +00:00
const NETADDR *PeerAddress() const { return &m_PeerAddr; }
2011-07-02 06:36:14 +00:00
const char *ErrorString() const { return m_aErrorString; }
void Reset();
int Update();
int Send(const char *pLine);
int Recv(char *pLine, int MaxLength);
};
class CNetRecvUnpacker
2009-10-27 14:38:53 +00:00
{
public:
bool m_Valid;
2009-10-27 14:38:53 +00:00
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);
2009-10-27 14:38:53 +00:00
};
2010-05-29 07:25:38 +00:00
// server side
2009-10-27 14:38:53 +00:00
class CNetServer
{
struct CSlot
2009-10-27 14:38:53 +00:00
{
public:
CNetConnection m_Connection;
};
2016-04-27 20:09:18 +00:00
struct CSpamConn
{
NETADDR m_Addr;
2021-06-23 05:05:49 +00:00
int64_t m_Time;
2016-04-27 20:09:18 +00:00
int m_Conns;
};
NETADDR m_Address;
2009-10-27 14:38:53 +00:00
NETSOCKET m_Socket;
2022-06-16 15:06:15 +00:00
CNetBan *m_pNetBan;
2009-10-27 14:38:53 +00:00
CSlot m_aSlots[NET_MAX_CLIENTS];
int m_MaxClients;
int m_MaxClientsPerIP;
2009-10-27 14:38:53 +00:00
NETFUNC_NEWCLIENT m_pfnNewClient;
2015-08-13 08:58:47 +00:00
NETFUNC_NEWCLIENT_NOAUTH m_pfnNewClientNoAuth;
2009-10-27 14:38:53 +00:00
NETFUNC_DELCLIENT m_pfnDelClient;
2015-08-23 15:51:28 +00:00
NETFUNC_CLIENTREJOIN m_pfnClientRejoin;
2020-10-27 17:57:14 +00:00
void *m_pUser;
int m_NumConAttempts; // log flooding attacks
2021-06-23 05:05:49 +00:00
int64_t m_TimeNumConAttempts;
2020-10-27 17:57:14 +00:00
unsigned char m_aSecurityTokenSeed[16];
2016-04-23 15:23:01 +00:00
// vanilla connect flood detection
2021-06-23 05:05:49 +00:00
int64_t m_VConnFirst;
2016-04-23 15:23:01 +00:00
int m_VConnNum;
2016-04-27 20:09:18 +00:00
CSpamConn m_aSpamConns[NET_CONNLIMIT_IPS];
2009-10-27 14:38:53 +00:00
CNetRecvUnpacker m_RecvUnpacker;
2015-08-12 20:43:37 +00:00
void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
int OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token);
2015-08-13 08:58:47 +00:00
void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet);
2015-08-23 15:01:01 +00:00
void OnConnCtrlMsg(NETADDR &Addr, int ClientID, int ControlMsg, const CNetPacketConstruct &Packet);
bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; }
2015-08-23 10:29:41 +00:00
int GetClientSlot(const NETADDR &Addr);
2015-08-12 20:43:37 +00:00
void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth = false, bool Sixup = false, SECURITY_TOKEN Token = 0);
2015-08-12 20:43:37 +00:00
int NumClientsWithAddr(NETADDR Addr);
2016-04-27 20:09:18 +00:00
bool Connlimit(NETADDR Addr);
2020-10-27 17:57:14 +00:00
void SendMsgs(NETADDR &Addr, const CMsgPacker *apMsgs[], int Num);
2015-08-12 20:43:37 +00:00
2009-10-27 14:38:53 +00:00
public:
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
2015-08-23 15:51:28 +00:00
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
2009-10-27 14:38:53 +00:00
//
2022-06-16 15:06:15 +00:00
bool Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP);
2009-10-27 14:38:53 +00:00
int Close();
2009-10-27 14:38:53 +00:00
//
2020-10-27 17:57:14 +00:00
int Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken);
2009-10-27 14:38:53 +00:00
int Send(CNetChunk *pChunk);
int Update();
2009-10-27 14:38:53 +00:00
//
int Drop(int ClientID, const char *pReason);
2009-10-27 14:38:53 +00:00
// status requests
2011-12-29 22:36:53 +00:00
const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
2015-08-14 14:46:01 +00:00
bool HasSecurityToken(int ClientID) const { return m_aSlots[ClientID].m_Connection.SecurityToken() != NET_SECURITY_TOKEN_UNSUPPORTED; }
NETADDR Address() const { return m_Address; }
2009-10-27 14:38:53 +00:00
NETSOCKET Socket() const { return m_Socket; }
2022-06-16 15:06:15 +00:00
CNetBan *NetBan() const { return m_pNetBan; }
int NetType() const { return net_socket_type(m_Socket); }
2009-10-27 14:38:53 +00:00
int MaxClients() const { return m_MaxClients; }
void SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token);
int SendConnlessSixup(CNetChunk *pChunk, SECURITY_TOKEN ResponseToken);
//
void SetMaxClientsPerIP(int Max);
2014-08-09 15:25:29 +00:00
bool SetTimedOut(int ClientID, int OrigID);
void SetTimeoutProtected(int ClientID);
2014-08-17 03:04:37 +00:00
int ResetErrorString(int ClientID);
const char *ErrorString(int ClientID);
2015-08-12 20:43:37 +00:00
// anti spoof
Add HTTP masterserver registering and HTTP masterserver Registering ----------- The idea is that game servers push their server info to the masterservers every 15 seconds or when the server info changes, but not more than once per second. The game servers do not support the old registering protocol anymore, the backward compatibility is handled by the masterserver. The register call is a HTTP POST to a URL like `https://master1.ddnet.tw/ddnet/15/register` and looks like this: ```json POST /ddnet/15/register HTTP/1.1 Address: tw-0.6+udp://connecting-address.invalid:8303 Secret: 81fa3955-6f83-4290-818d-31c0906b1118 Challenge-Secret: 81fa3955-6f83-4290-818d-31c0906b1118:tw0.6/ipv6 Info-Serial: 0 { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } ``` The `Address` header declares that the server wants to register itself as a `tw-0.6+udp` server, i.e. a server speaking a Teeworlds-0.6-compatible protocol. The free-form `Secret` header is used as a server identity, the server list will be deduplicated via this secret. The free-form `Challenge-Secret` is sent back via UDP for a port forward check. This might have security implications as the masterserver can be asked to send a UDP packet containing some user-controlled bytes. This is somewhat mitigated by the fact that it can only go to an attacker-controlled IP address. The `Info-Serial` header is an integer field that should increase each time the server info (in the body) changes. The masterserver uses that field to ensure that it doesn't use old server infos. The body is a free-form JSON object set by the game server. It should contain certain keys in the correct form to be accepted by clients. The body is optional if the masterserver already confirmed the reception of the info with the given `Info-Serial`. Not shown in this payload is the `Connless-Token` header that is used for Teeworlds 0.7 style communication. Also not shown is the `Challenge-Token` that should be included once the server receives the challenge token via UDP. The masterserver responds with a `200 OK` with a body like this: ``` {"status":"success"} ``` The `status` field can be `success` if the server was successfully registered on the masterserver, `need_challenge` if the masterserver wants the correct `Challenge-Token` header before the register process is successful, `need_info` if the server sent an empty body but the masterserver doesn't actually know the server info. It can also be `error` if the request was malformed, only in this case an HTTP status code except `200 OK` is sent. Synchronization --------------- The masterserver keeps state and outputs JSON files every second. ```json { "servers": [ { "addresses": [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ], "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } ] } ``` `servers.json` (or configured by `--out`) is a server list that is compatible with DDNet 15.5+ clients. It is a JSON object containing a single key `servers` with a list of game servers. Each game server is represented by a JSON object with an `addresses` key containing a list of all known addresses of the server and an `info` key containing the free-form server info sent by the game server. The free-form `info` JSON object re-encoded by the master server and thus canonicalized and stripped of any whitespace characters outside strings. ```json { "kind": "mastersrv", "now": 1816002, "secrets": { "tw-0.6+udp://127.0.0.1:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" }, "tw-0.6+udp://[::1]:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" } }, "servers": { "42d8f991-f2fa-46e5-a9ae-ebcc93846feb": { "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } } } ``` `--write-dump` outputs a JSON file compatible with `--read-dump-dir`, this can be used to synchronize servers across different masterservers. `--read-dump-dir` is also used to ingest servers from the backward compatibility layer that pings each server for their server info using the old protocol. The `kind` field describe that this is `mastersrv` output and not from a `backcompat`. This is used for prioritizing `mastersrv` information over `backcompat` information. The `now` field contains an integer describing the current time in milliseconds relative an unspecified epoch that is fixed for each JSON file. This is done instead of using the current time as the epoch for better compression of non-changing data. `secrets` is a map from each server address and to a JSON object containing the last ping time (`ping_time`) in milliseconds relative to the same epoch as before, and the server secret (`secret`) that is used to unify server infos from different addresses of the same logical server. `servers` is a map from the aforementioned `secret`s to the corresponding `info_serial` and `info`. ```json [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ] ``` `--write-addresses` outputs a JSON file containing all addresses corresponding to servers that are registered to HTTP masterservers. It does not contain the servers that are obtained via backward compatibility measures. This file can be used by an old-style masterserver to also list new-style servers without the game servers having to register there. An implementation of this can be found at https://github.com/heinrich5991/teeworlds/tree/mastersrv_6_backcompat for Teeworlds 0.5/0.6 masterservers and at https://github.com/heinrich5991/teeworlds/tree/mastersrv_7_backcompat for Teeworlds 0.7 masterservers. All these JSON files can be sent over the network in an efficient way using https://github.com/heinrich5991/twmaster-collect. It establishes a zstd-compressed TCP connection authenticated by a string token that is sent in plain-text. It watches the specified file and transmits it every time it changes. Due to the zstd-compression, the data sent over the network is similar to the size of a diff. Implementation -------------- The masterserver implementation was done in Rust. The current gameserver register implementation doesn't support more than one masterserver for registering.
2022-05-19 20:03:17 +00:00
SECURITY_TOKEN GetGlobalToken();
2015-08-12 20:43:37 +00:00
SECURITY_TOKEN GetToken(const NETADDR &Addr);
2015-08-13 08:58:47 +00:00
// vanilla token/gametick shouldn't be negative
SECURITY_TOKEN GetVanillaToken(const NETADDR &Addr) { return absolute(GetToken(Addr)); }
2007-07-13 13:40:04 +00:00
};
2011-07-02 06:36:14 +00:00
class CNetConsole
{
struct CSlot
{
CConsoleNetConnection m_Connection;
};
NETSOCKET m_Socket;
2022-06-16 15:06:15 +00:00
CNetBan *m_pNetBan;
2011-07-30 11:40:01 +00:00
CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];
2011-07-02 06:36:14 +00:00
2020-03-29 02:36:38 +00:00
NETFUNC_NEWCLIENT_CON m_pfnNewClient;
2011-07-02 06:36:14 +00:00
NETFUNC_DELCLIENT m_pfnDelClient;
2020-10-27 17:57:14 +00:00
void *m_pUser;
2011-07-02 06:36:14 +00:00
CNetRecvUnpacker m_RecvUnpacker;
public:
2020-03-29 02:36:38 +00:00
void SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
2011-07-02 06:36:14 +00:00
//
2022-06-16 15:06:15 +00:00
bool Open(NETADDR BindAddr, CNetBan *pNetBan);
2011-07-02 06:36:14 +00:00
int Close();
//
int Recv(char *pLine, int MaxLength, int *pClientID = nullptr);
2011-07-02 06:36:14 +00:00
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
2011-12-29 22:36:53 +00:00
const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
2022-06-16 15:06:15 +00:00
CNetBan *NetBan() const { return m_pNetBan; }
2011-07-02 06:36:14 +00:00
};
2010-05-29 07:25:38 +00:00
// client side
2009-10-27 14:38:53 +00:00
class CNetClient
2007-07-13 13:40:04 +00:00
{
2009-10-27 14:38:53 +00:00
CNetConnection m_Connection;
CNetRecvUnpacker m_RecvUnpacker;
CStun *m_pStun = nullptr;
2007-07-13 13:40:04 +00:00
public:
2014-01-14 23:02:19 +00:00
NETSOCKET m_Socket;
2009-10-27 14:38:53 +00:00
// openness
bool Open(NETADDR BindAddr);
2009-10-27 14:38:53 +00:00
int Close();
2009-10-27 14:38:53 +00:00
// connection state
2020-10-27 17:57:14 +00:00
int Disconnect(const char *pReason);
int Connect(const NETADDR *pAddr, int NumAddrs);
2009-10-27 14:38:53 +00:00
// communication
2020-10-27 17:57:14 +00:00
int Recv(CNetChunk *pChunk);
int Send(CNetChunk *pChunk);
2009-10-27 14:38:53 +00:00
// pumping
int Update();
int Flush();
int ResetErrorString();
2009-10-27 14:38:53 +00:00
// error and state
int NetType() const { return net_socket_type(m_Socket); }
2009-10-27 14:38:53 +00:00
int State();
const NETADDR *ServerAddress() const { return m_Connection.PeerAddress(); }
void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const { m_Connection.ConnectAddresses(ppAddrs, pNumAddrs); }
2022-01-09 12:06:41 +00:00
int GotProblems(int64_t MaxLatency) const;
const char *ErrorString() const;
bool SecurityTokenUnknown() { return m_Connection.SecurityToken() == NET_SECURITY_TOKEN_UNKNOWN; }
// stun
void FeedStunServer(NETADDR StunServer);
void RefreshStun();
CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr);
2009-10-27 14:38:53 +00:00
};
// 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;
2010-05-29 07:25:38 +00:00
static CHuffman ms_Huffman;
2009-10-27 14:38:53 +00:00
public:
static void OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv);
static void CloseLog();
2009-10-27 14:38:53 +00:00
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);
2020-03-29 02:36:38 +00:00
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup = false);
static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[4]);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup = false, bool NoCompress = false);
2015-08-12 20:43:37 +00:00
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool &Sixup, SECURITY_TOKEN *pSecurityToken = nullptr, SECURITY_TOKEN *pResponseToken = nullptr);
2009-10-27 14:38:53 +00:00
2010-05-29 07:25:38 +00:00
// The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
static bool IsSeqInBackroom(int Seq, int Ack);
2007-07-13 13:40:04 +00:00
};
#endif