ddnet/src/engine/shared/network_server.cpp

822 lines
23 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. */
#include <base/hash_ctxt.h>
#include <base/system.h>
2009-10-27 14:38:53 +00:00
2011-12-29 22:36:53 +00:00
#include <engine/console.h>
#include "config.h"
2011-12-29 22:36:53 +00:00
#include "netban.h"
#include "network.h"
2015-08-14 16:33:42 +00:00
#include <engine/message.h>
#include <engine/shared/protocol.h>
2020-03-29 02:36:38 +00:00
#include <game/generated/protocol.h>
2016-05-26 09:06:56 +00:00
const int DummyMapCrc = 0x6c760ac4;
unsigned char g_aDummyMapData[] = {
2016-05-25 21:45:32 +00:00
0x44, 0x41, 0x54, 0x41, 0x04, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00,
0x14, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0xc3, 0x52,
0xff, 0x7f, 0x00, 0x00, 0x20, 0x5c, 0xf6, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
0x7c, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x63, 0x64, 0x60, 0x60, 0x60, 0x44,
0xc2, 0x00, 0x00, 0x38, 0x00, 0x05};
2015-08-13 08:58:47 +00:00
2017-03-21 10:24:44 +00:00
static SECURITY_TOKEN ToSecurityToken(const unsigned char *pData)
2015-08-12 20:43:37 +00:00
{
return (int)pData[0] | (pData[1] << 8) | (pData[2] << 16) | (pData[3] << 24);
}
2011-12-29 22:36:53 +00:00
bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags)
2009-10-27 14:38:53 +00:00
{
// zero out the whole structure
mem_zero(this, sizeof(*this));
2009-10-27 14:38:53 +00:00
// open socket
m_Socket = net_udp_create(BindAddr);
if(!m_Socket.type)
2009-10-27 14:38:53 +00:00
return false;
m_Address = BindAddr;
2011-12-29 22:36:53 +00:00
m_pNetBan = pNetBan;
m_MaxClients = clamp(MaxClients, 1, (int)NET_MAX_CLIENTS);
m_MaxClientsPerIP = MaxClientsPerIP;
m_NumConAttempts = 0;
m_TimeNumConAttempts = time_get();
2016-04-23 15:23:01 +00:00
m_VConnNum = 0;
m_VConnFirst = 0;
2020-10-27 17:57:14 +00:00
secure_random_fill(m_aSecurityTokenSeed, sizeof(m_aSecurityTokenSeed));
2020-10-26 14:14:07 +00:00
for(auto &Slot : m_aSlots)
Slot.m_Connection.Init(m_Socket, true);
net_init_mmsgs(&m_MMSGS);
2009-10-27 14:38:53 +00:00
return true;
}
int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
{
m_pfnNewClient = pfnNewClient;
m_pfnDelClient = pfnDelClient;
2020-10-27 17:57:14 +00:00
m_pUser = pUser;
2009-10-27 14:38:53 +00:00
return 0;
}
2015-08-23 15:51:28 +00:00
int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
2015-08-13 08:58:47 +00:00
{
m_pfnNewClient = pfnNewClient;
m_pfnNewClientNoAuth = pfnNewClientNoAuth;
2015-08-23 15:51:28 +00:00
m_pfnClientRejoin = pfnClientRejoin;
2015-08-13 08:58:47 +00:00
m_pfnDelClient = pfnDelClient;
2020-10-27 17:57:14 +00:00
m_pUser = pUser;
2015-08-13 08:58:47 +00:00
return 0;
}
2009-10-27 14:38:53 +00:00
int CNetServer::Close()
{
2010-05-29 07:25:38 +00:00
// TODO: implement me
2009-10-27 14:38:53 +00:00
return 0;
}
int CNetServer::Drop(int ClientID, const char *pReason)
{
2010-05-29 07:25:38 +00:00
// TODO: insert lots of checks here
2011-05-04 23:43:27 +00:00
/*NETADDR Addr = ClientAddr(ClientID);
2009-10-27 14:38:53 +00:00
2011-05-04 23:43:27 +00:00
dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
2009-10-27 14:38:53 +00:00
ClientID,
Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3],
pReason
);*/
if(m_pfnDelClient)
2020-10-27 17:57:14 +00:00
m_pfnDelClient(ClientID, pReason, m_pUser);
2009-10-27 14:38:53 +00:00
m_aSlots[ClientID].m_Connection.Disconnect(pReason);
2009-10-27 14:38:53 +00:00
return 0;
}
int CNetServer::Update()
{
for(int i = 0; i < MaxClients(); i++)
{
m_aSlots[i].m_Connection.Update();
2014-08-09 15:25:29 +00:00
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR &&
(!m_aSlots[i].m_Connection.m_TimeoutProtected ||
!m_aSlots[i].m_Connection.m_TimeoutSituation))
{
2014-08-09 13:37:10 +00:00
Drop(i, m_aSlots[i].m_Connection.ErrorString());
}
2009-10-27 14:38:53 +00:00
}
2009-10-27 14:38:53 +00:00
return 0;
}
2015-08-12 20:43:37 +00:00
SECURITY_TOKEN CNetServer::GetToken(const NETADDR &Addr)
{
SHA256_CTX Sha256;
sha256_init(&Sha256);
2020-10-27 17:57:14 +00:00
sha256_update(&Sha256, (unsigned char *)m_aSecurityTokenSeed, sizeof(m_aSecurityTokenSeed));
sha256_update(&Sha256, (unsigned char *)&Addr, 20); // omit port, bad idea!
2015-08-12 20:43:37 +00:00
SECURITY_TOKEN SecurityToken = ToSecurityToken(sha256_finish(&Sha256).data);
2015-08-12 20:43:37 +00:00
if(SecurityToken == NET_SECURITY_TOKEN_UNKNOWN ||
2015-08-12 20:43:37 +00:00
SecurityToken == NET_SECURITY_TOKEN_UNSUPPORTED)
SecurityToken = 1;
2015-08-12 20:43:37 +00:00
return SecurityToken;
}
void CNetServer::SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken)
{
CNetBase::SendControlMsg(m_Socket, &Addr, 0, ControlMsg, pExtra, ExtraSize, SecurityToken);
}
int CNetServer::NumClientsWithAddr(NETADDR Addr)
{
int FoundAddr = 0;
for(int i = 0; i < MaxClients(); ++i)
{
2015-08-23 10:29:41 +00:00
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE ||
(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR &&
(!m_aSlots[i].m_Connection.m_TimeoutProtected ||
!m_aSlots[i].m_Connection.m_TimeoutSituation)))
2015-08-12 20:43:37 +00:00
continue;
2018-10-08 18:04:04 +00:00
if(!net_addr_comp_noport(&Addr, m_aSlots[i].m_Connection.PeerAddress()))
2015-08-12 20:43:37 +00:00
FoundAddr++;
}
return FoundAddr;
}
2016-04-27 20:09:18 +00:00
bool CNetServer::Connlimit(NETADDR Addr)
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get();
2016-04-27 20:09:18 +00:00
int Oldest = 0;
for(int i = 0; i < NET_CONNLIMIT_IPS; ++i)
{
if(!net_addr_comp(&m_aSpamConns[i].m_Addr, &Addr))
{
if(m_aSpamConns[i].m_Time > Now - time_freq() * g_Config.m_SvConnlimitTime)
{
if(m_aSpamConns[i].m_Conns >= g_Config.m_SvConnlimit)
return true;
}
else
{
m_aSpamConns[i].m_Time = Now;
m_aSpamConns[i].m_Conns = 0;
}
m_aSpamConns[i].m_Conns++;
return false;
}
if(m_aSpamConns[i].m_Time < m_aSpamConns[Oldest].m_Time)
Oldest = i;
}
m_aSpamConns[Oldest].m_Addr = Addr;
m_aSpamConns[Oldest].m_Time = Now;
m_aSpamConns[Oldest].m_Conns = 1;
return false;
}
2015-08-12 20:43:37 +00:00
2020-03-29 02:36:38 +00:00
int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth, bool Sixup, SECURITY_TOKEN Token)
2015-08-12 20:43:37 +00:00
{
if(Sixup && !g_Config.m_SvSixup)
{
2020-10-27 17:57:14 +00:00
const char aMsg[] = "0.7 connections are not accepted at this time";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aMsg, sizeof(aMsg), SecurityToken, Sixup);
return -1; // failed to add client?
}
if(Connlimit(Addr))
2016-04-27 20:09:18 +00:00
{
2020-10-27 17:57:14 +00:00
const char aMsg[] = "Too many connections in a short time";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aMsg, sizeof(aMsg), SecurityToken, Sixup);
2016-04-27 20:09:18 +00:00
return -1; // failed to add client
}
2015-08-12 20:43:37 +00:00
// check for sv_max_clients_per_ip
if(NumClientsWithAddr(Addr) + 1 > m_MaxClientsPerIP)
2015-08-12 20:43:37 +00:00
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP);
2020-03-29 02:36:38 +00:00
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, SecurityToken, Sixup);
2015-08-12 20:43:37 +00:00
return -1; // failed to add client
}
int Slot = -1;
for(int i = 0; i < MaxClients(); i++)
{
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
{
Slot = i;
break;
}
}
if(Slot == -1)
2015-08-12 20:43:37 +00:00
{
2020-10-27 17:57:14 +00:00
const char aFullMsg[] = "This server is full";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aFullMsg, sizeof(aFullMsg), SecurityToken, Sixup);
2015-08-12 20:43:37 +00:00
return -1; // failed to add client
}
// init connection slot
2020-03-29 02:36:38 +00:00
m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken, Token, Sixup);
2015-08-12 20:43:37 +00:00
if(VanillaAuth)
{
// client sequence is unknown if the auth was done
// connection-less
m_aSlots[Slot].m_Connection.SetUnknownSeq();
// correct sequence
m_aSlots[Slot].m_Connection.SetSequence(6);
}
if(g_Config.m_Debug)
2015-08-13 08:58:47 +00:00
{
char aAddrStr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
dbg_msg("security", "client accepted %s", aAddrStr);
2015-08-13 08:58:47 +00:00
}
if(VanillaAuth)
2020-10-27 17:57:14 +00:00
m_pfnNewClientNoAuth(Slot, m_pUser);
2015-08-13 08:58:47 +00:00
else
2020-10-27 17:57:14 +00:00
m_pfnNewClient(Slot, m_pUser, Sixup);
2015-08-12 20:43:37 +00:00
return Slot; // done
}
2020-10-27 17:57:14 +00:00
void CNetServer::SendMsgs(NETADDR &Addr, const CMsgPacker *apMsgs[], int Num)
2015-08-13 08:58:47 +00:00
{
2020-10-27 17:57:14 +00:00
CNetPacketConstruct Construct;
mem_zero(&Construct, sizeof(Construct));
unsigned char *pChunkData = &Construct.m_aChunkData[Construct.m_DataSize];
2015-08-13 08:58:47 +00:00
2020-10-27 17:57:14 +00:00
for(int i = 0; i < Num; i++)
2015-08-13 08:58:47 +00:00
{
2020-10-27 17:57:14 +00:00
const CMsgPacker *pMsg = apMsgs[i];
2015-08-13 08:58:47 +00:00
CNetChunkHeader Header;
Header.m_Flags = NET_CHUNKFLAG_VITAL;
2015-08-13 08:58:47 +00:00
Header.m_Size = pMsg->Size();
Header.m_Sequence = i + 1;
2015-08-13 08:58:47 +00:00
pChunkData = Header.Pack(pChunkData);
mem_copy(pChunkData, pMsg->Data(), pMsg->Size());
*pChunkData <<= 1;
*pChunkData |= 1;
2015-08-13 08:58:47 +00:00
pChunkData += pMsg->Size();
2020-10-27 17:57:14 +00:00
Construct.m_NumChunks++;
2015-08-13 08:58:47 +00:00
}
//
2020-10-27 17:57:14 +00:00
Construct.m_DataSize = (int)(pChunkData - Construct.m_aChunkData);
CNetBase::SendPacket(m_Socket, &Addr, &Construct, NET_SECURITY_TOKEN_UNSUPPORTED);
2015-08-13 08:58:47 +00:00
}
// connection-less msg packet without token-support
void CNetServer::OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet)
2015-08-12 20:43:37 +00:00
{
bool IsCtrl = Packet.m_Flags & NET_PACKETFLAG_CONTROL;
2015-08-13 08:58:47 +00:00
int CtrlMsg = m_RecvUnpacker.m_Data.m_aChunkData[0];
// log flooding
//TODO: remove
if(g_Config.m_Debug)
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get();
if(Now - m_TimeNumConAttempts > time_freq())
// reset
m_NumConAttempts = 0;
m_NumConAttempts++;
if(m_NumConAttempts > 100)
{
dbg_msg("security", "flooding detected");
m_TimeNumConAttempts = Now;
m_NumConAttempts = 0;
}
}
if(IsCtrl && CtrlMsg == NET_CTRLMSG_CONNECT)
2015-08-13 08:58:47 +00:00
{
if(g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0')
{
bool Flooding = false;
if(g_Config.m_SvVanConnPerSecond)
2016-04-23 15:23:01 +00:00
{
// detect flooding
Flooding = m_VConnNum > g_Config.m_SvVanConnPerSecond;
2021-06-23 05:05:49 +00:00
const int64_t Now = time_get();
if(Now <= m_VConnFirst + time_freq())
{
m_VConnNum++;
}
else
{
m_VConnNum = 1;
m_VConnFirst = Now;
}
2016-04-23 15:23:01 +00:00
}
if(g_Config.m_Debug && Flooding)
2016-04-23 15:23:01 +00:00
{
dbg_msg("security", "vanilla connection flooding detected");
}
// simulate accept
SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, NULL, 0, NET_SECURITY_TOKEN_UNSUPPORTED);
// Begin vanilla compatible token handshake
// The idea is to pack a security token in the gametick
// parameter of NETMSG_SNAPEMPTY. The Client then will
// return the token/gametick in NETMSG_INPUT, allowing
// us to validate the token.
// https://github.com/eeeee/ddnet/commit/b8e40a244af4e242dc568aa34854c5754c75a39a
// Before we can send NETMSG_SNAPEMPTY, the client needs
// to load a map, otherwise it might crash. The map
// should be as small as is possible and directly available
2018-07-10 09:29:02 +00:00
// to the client. Therefore a dummy map is sent in the same
2016-04-23 15:44:42 +00:00
// packet. To reduce the traffic we'll fallback to a default
// map if there are too many connection attempts at once.
// send mapchange + map data + con_ready + 3 x empty snap (with token)
CMsgPacker MapChangeMsg(NETMSG_MAP_CHANGE);
if(Flooding)
2016-04-23 15:23:01 +00:00
{
2016-04-23 15:44:42 +00:00
// Fallback to dm1
2016-04-23 15:23:01 +00:00
MapChangeMsg.AddString("dm1", 0);
MapChangeMsg.AddInt(0xf2159e6e);
MapChangeMsg.AddInt(5805);
}
else
{
2016-04-23 15:44:42 +00:00
// dummy map
2016-04-23 15:23:01 +00:00
MapChangeMsg.AddString("dummy", 0);
MapChangeMsg.AddInt(DummyMapCrc);
MapChangeMsg.AddInt(sizeof(g_aDummyMapData));
}
CMsgPacker MapDataMsg(NETMSG_MAP_DATA);
if(Flooding)
2016-04-23 15:23:01 +00:00
{
// send empty map data to keep 0.6.4 support
MapDataMsg.AddInt(1); // last chunk
MapDataMsg.AddInt(0); // crc
MapDataMsg.AddInt(0); // chunk index
MapDataMsg.AddInt(0); // map size
MapDataMsg.AddRaw(NULL, 0); // map data
}
else
{
2016-04-23 15:44:42 +00:00
// send dummy map data
2016-04-23 15:23:01 +00:00
MapDataMsg.AddInt(1); // last chunk
MapDataMsg.AddInt(DummyMapCrc); // crc
MapDataMsg.AddInt(0); // chunk index
MapDataMsg.AddInt(sizeof(g_aDummyMapData)); // map size
MapDataMsg.AddRaw(g_aDummyMapData, sizeof(g_aDummyMapData)); // map data
}
CMsgPacker ConReadyMsg(NETMSG_CON_READY);
CMsgPacker SnapEmptyMsg(NETMSG_SNAPEMPTY);
SECURITY_TOKEN SecurityToken = GetVanillaToken(Addr);
SnapEmptyMsg.AddInt(SecurityToken);
SnapEmptyMsg.AddInt(SecurityToken + 1);
// send all chunks/msgs in one packet
2020-10-27 17:57:14 +00:00
const CMsgPacker *apMsgs[] = {&MapChangeMsg, &MapDataMsg, &ConReadyMsg,
&SnapEmptyMsg, &SnapEmptyMsg, &SnapEmptyMsg};
2020-10-27 17:57:14 +00:00
SendMsgs(Addr, apMsgs, 6);
}
else
{
2018-07-10 09:29:02 +00:00
// accept client directly
SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, NULL, 0, NET_SECURITY_TOKEN_UNSUPPORTED);
TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED);
}
2015-08-13 08:58:47 +00:00
}
else if(!IsCtrl && g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0')
2015-08-13 08:58:47 +00:00
{
CNetChunkHeader h;
unsigned char *pData = Packet.m_aChunkData;
pData = h.Unpack(pData);
CUnpacker Unpacker;
Unpacker.Reset(pData, h.m_Size);
int Msg = Unpacker.GetInt() >> 1;
if(Msg == NETMSG_INPUT)
2015-08-13 08:58:47 +00:00
{
SECURITY_TOKEN SecurityToken = Unpacker.GetInt();
if(SecurityToken == GetVanillaToken(Addr))
2015-08-13 08:58:47 +00:00
{
if(g_Config.m_Debug)
dbg_msg("security", "new client (vanilla handshake)");
2015-08-13 08:58:47 +00:00
// try to accept client skipping auth state
TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED, true);
}
else if(g_Config.m_Debug)
2015-08-13 08:58:47 +00:00
dbg_msg("security", "invalid token (vanilla handshake)");
}
else
{
if(g_Config.m_Debug)
2015-08-13 08:58:47 +00:00
{
dbg_msg("security", "invalid preconn msg %d", Msg);
}
}
}
2015-08-12 20:43:37 +00:00
}
2015-08-23 15:01:01 +00:00
void CNetServer::OnConnCtrlMsg(NETADDR &Addr, int ClientID, int ControlMsg, const CNetPacketConstruct &Packet)
{
if(ControlMsg == NET_CTRLMSG_CONNECT)
2015-08-23 15:01:01 +00:00
{
// got connection attempt inside of valid session
// the client probably wants to reconnect
bool SupportsToken = Packet.m_DataSize >=
(int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN)) &&
!mem_comp(&Packet.m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC));
2015-08-23 15:01:01 +00:00
if(SupportsToken)
2015-08-23 15:01:01 +00:00
{
// response connection request with token
SECURITY_TOKEN Token = GetToken(Addr);
SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC), Token);
}
if(g_Config.m_Debug)
2015-08-23 15:01:01 +00:00
dbg_msg("security", "client %d wants to reconnect", ClientID);
}
else if(ControlMsg == NET_CTRLMSG_ACCEPT && Packet.m_DataSize == 1 + sizeof(SECURITY_TOKEN))
2015-08-23 15:01:01 +00:00
{
SECURITY_TOKEN Token = ToSecurityToken(&Packet.m_aChunkData[1]);
if(Token == GetToken(Addr))
2015-08-23 15:01:01 +00:00
{
// correct token
// try to accept client
if(g_Config.m_Debug)
dbg_msg("security", "client %d reconnect", ClientID);
2015-08-23 15:01:01 +00:00
2015-08-23 16:12:13 +00:00
// reset netconn and process rejoin
m_aSlots[ClientID].m_Connection.Reset(true);
2020-10-27 17:57:14 +00:00
m_pfnClientRejoin(ClientID, m_pUser);
2015-08-23 15:01:01 +00:00
}
}
}
2015-08-12 20:43:37 +00:00
void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet)
{
if(ClientExists(Addr))
2015-08-12 20:43:37 +00:00
return; // silently ignore
if(Addr.type == NETTYPE_WEBSOCKET_IPV4)
2015-08-21 11:08:40 +00:00
{
// websocket client doesn't send token
// direct accept
SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC), NET_SECURITY_TOKEN_UNSUPPORTED);
TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED);
}
else if(ControlMsg == NET_CTRLMSG_CONNECT)
2015-08-12 20:43:37 +00:00
{
// response connection request with token
SECURITY_TOKEN Token = GetToken(Addr);
SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC), Token);
2015-08-12 20:43:37 +00:00
}
else if(ControlMsg == NET_CTRLMSG_ACCEPT)
2015-08-12 20:43:37 +00:00
{
SECURITY_TOKEN Token = ToSecurityToken(&Packet.m_aChunkData[1]);
if(Token == GetToken(Addr))
2015-08-12 20:43:37 +00:00
{
// correct token
// try to accept client
if(g_Config.m_Debug)
dbg_msg("security", "new client (ddnet token)");
2015-08-12 20:43:37 +00:00
TryAcceptClient(Addr, Token);
}
else
{
// invalid token
if(g_Config.m_Debug)
2015-08-12 20:43:37 +00:00
dbg_msg("security", "invalid token");
}
}
}
int CNetServer::OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token)
2020-03-29 02:36:38 +00:00
{
if(m_RecvUnpacker.m_Data.m_DataSize < 5 || ClientExists(Addr))
return 0; // silently ignore
2020-03-29 02:36:38 +00:00
mem_copy(&ResponseToken, Packet.m_aChunkData + 1, 4);
2020-03-29 02:36:38 +00:00
if(ControlMsg == 5)
{
if(m_RecvUnpacker.m_Data.m_DataSize >= 512)
{
SendTokenSixup(Addr, ResponseToken);
return 0;
}
// Is this behaviour safe to rely on?
pChunk->m_Flags = 0;
pChunk->m_ClientID = -1;
pChunk->m_Address = Addr;
pChunk->m_DataSize = 0;
return 1;
2020-03-29 02:36:38 +00:00
}
else if(ControlMsg == NET_CTRLMSG_CONNECT)
{
SECURITY_TOKEN MyToken = GetToken(Addr);
unsigned char aToken[4];
mem_copy(aToken, &MyToken, 4);
2020-03-29 02:36:38 +00:00
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CONNECTACCEPT, aToken, sizeof(aToken), ResponseToken, true);
if(Token == MyToken)
TryAcceptClient(Addr, ResponseToken, false, true, Token);
}
return 0;
2020-03-29 02:36:38 +00:00
}
2015-08-23 10:29:41 +00:00
int CNetServer::GetClientSlot(const NETADDR &Addr)
2015-08-12 20:43:37 +00:00
{
int Slot = -1;
2015-08-12 20:43:37 +00:00
for(int i = 0; i < MaxClients(); i++)
{
if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE &&
2015-08-23 10:29:41 +00:00
m_aSlots[i].m_Connection.State() != NET_CONNSTATE_ERROR &&
2015-08-12 20:43:37 +00:00
net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0)
2015-08-23 10:29:41 +00:00
2015-08-12 20:43:37 +00:00
{
Slot = i;
2015-08-12 20:43:37 +00:00
}
}
return Slot;
2015-08-12 20:43:37 +00:00
}
static bool IsDDNetControlMsg(const CNetPacketConstruct *pPacket)
{
if(!(pPacket->m_Flags & NET_PACKETFLAG_CONTROL) || pPacket->m_DataSize < 1)
{
return false;
}
if(pPacket->m_aChunkData[0] == NET_CTRLMSG_CONNECT && pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN)) && mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)) == 0)
{
// DDNet CONNECT
return true;
}
if(pPacket->m_aChunkData[0] == NET_CTRLMSG_ACCEPT && pPacket->m_DataSize >= 1 + (int)sizeof(SECURITY_TOKEN))
{
// DDNet ACCEPT
return true;
}
return false;
}
2009-10-27 14:38:53 +00:00
/*
TODO: chopp up this function into smaller working parts
*/
2020-10-27 17:57:14 +00:00
int CNetServer::Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken)
2009-10-27 14:38:53 +00:00
{
2022-02-14 23:12:52 +00:00
while(true)
2009-10-27 14:38:53 +00:00
{
NETADDR Addr;
2010-05-29 07:25:38 +00:00
// check for a chunk
2009-10-27 14:38:53 +00:00
if(m_RecvUnpacker.FetchChunk(pChunk))
return 1;
2010-05-29 07:25:38 +00:00
// TODO: empty the recvinfo
2018-12-17 21:15:41 +00:00
unsigned char *pData;
int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE, &m_MMSGS, &pData);
2009-10-27 14:38:53 +00:00
2010-05-29 07:25:38 +00:00
// no more packets for now
2009-10-27 14:38:53 +00:00
if(Bytes <= 0)
break;
// check if we just should drop the packet
char aBuf[128];
if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf)))
{
// banned, reply with a message
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, NET_SECURITY_TOKEN_UNSUPPORTED);
continue;
}
SECURITY_TOKEN Token;
bool Sixup = false;
2020-10-27 17:57:14 +00:00
*pResponseToken = NET_SECURITY_TOKEN_UNKNOWN;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup, &Token, pResponseToken) == 0)
{
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONNLESS)
2009-10-27 14:38:53 +00:00
{
if(Sixup && Token != GetToken(Addr))
continue;
2009-10-27 14:38:53 +00:00
pChunk->m_Flags = NETSENDFLAG_CONNLESS;
pChunk->m_ClientID = -1;
pChunk->m_Address = Addr;
pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize;
pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData;
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_EXTENDED)
{
pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
mem_copy(pChunk->m_aExtraData, m_RecvUnpacker.m_Data.m_aExtraData, sizeof(pChunk->m_aExtraData));
}
2009-10-27 14:38:53 +00:00
return 1;
}
else
{
// drop invalid ctrl packets
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONTROL &&
m_RecvUnpacker.m_Data.m_DataSize == 0)
continue;
2015-08-23 15:01:01 +00:00
// normal packet, find matching slot
int Slot = GetClientSlot(Addr);
if(!Sixup && Slot != -1 && m_aSlots[Slot].m_Connection.m_Sixup)
2020-03-29 02:36:38 +00:00
{
Sixup = true;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup, &Token))
2020-03-29 02:36:38 +00:00
continue;
}
if(Slot != -1)
{
// found
// control
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONTROL)
2015-08-23 15:01:01 +00:00
OnConnCtrlMsg(Addr, Slot, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);
2020-03-29 02:36:38 +00:00
if(m_aSlots[Slot].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr, Token))
2009-10-27 14:38:53 +00:00
{
2015-08-23 10:29:41 +00:00
if(m_RecvUnpacker.m_Data.m_DataSize)
m_RecvUnpacker.Start(&Addr, &m_aSlots[Slot].m_Connection, Slot);
2009-10-27 14:38:53 +00:00
}
}
else
2009-10-27 14:38:53 +00:00
{
// not found, client that wants to connect
2020-03-29 02:36:38 +00:00
if(Sixup)
{
2020-03-29 02:36:38 +00:00
// got 0.7 control msg
2020-10-27 17:57:14 +00:00
if(OnSixupCtrlMsg(Addr, pChunk, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data, *pResponseToken, Token) == 1)
return 1;
}
2020-03-29 02:36:38 +00:00
else if(IsDDNetControlMsg(&m_RecvUnpacker.m_Data))
// got ddnet control msg
2015-08-12 20:43:37 +00:00
OnTokenCtrlMsg(Addr, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);
else
// got connection-less ctrl or sys msg
OnPreConnMsg(Addr, m_RecvUnpacker.m_Data);
2009-10-27 14:38:53 +00:00
}
}
}
}
return 0;
}
int CNetServer::Send(CNetChunk *pChunk)
{
if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
{
dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize);
return -1;
}
if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
2009-10-27 14:38:53 +00:00
{
2010-05-29 07:25:38 +00:00
// send connectionless packet
CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize,
pChunk->m_Flags & NETSENDFLAG_EXTENDED, pChunk->m_aExtraData);
2009-10-27 14:38:53 +00:00
}
else
{
int Flags = 0;
2022-02-21 14:54:55 +00:00
dbg_assert(pChunk->m_ClientID >= 0, "erroneous client id");
dbg_assert(pChunk->m_ClientID < MaxClients(), "erroneous client id");
if(pChunk->m_Flags & NETSENDFLAG_VITAL)
2009-10-27 14:38:53 +00:00
Flags = NET_CHUNKFLAG_VITAL;
2010-05-29 07:25:38 +00:00
if(m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData) == 0)
{
if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
2010-05-29 07:25:38 +00:00
m_aSlots[pChunk->m_ClientID].m_Connection.Flush();
}
else
{
2013-11-17 01:27:28 +00:00
//Drop(pChunk->m_ClientID, "Error sending data");
2010-05-29 07:25:38 +00:00
}
2009-10-27 14:38:53 +00:00
}
return 0;
}
void CNetServer::SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token)
{
SECURITY_TOKEN MyToken = GetToken(Addr);
unsigned char aBuf[512] = {};
mem_copy(aBuf, &MyToken, 4);
int Size = (Token == NET_SECURITY_TOKEN_UNKNOWN) ? 512 : 4;
CNetBase::SendControlMsg(m_Socket, &Addr, 0, 5, aBuf, Size, Token, true);
}
int CNetServer::SendConnlessSixup(CNetChunk *pChunk, SECURITY_TOKEN ResponseToken)
{
if(pChunk->m_DataSize > NET_MAX_PACKETSIZE - 9)
return -1;
unsigned char aBuffer[NET_MAX_PACKETSIZE];
aBuffer[0] = NET_PACKETFLAG_CONNLESS << 2 | 1;
SECURITY_TOKEN Token = GetToken(pChunk->m_Address);
mem_copy(aBuffer + 1, &ResponseToken, 4);
mem_copy(aBuffer + 5, &Token, 4);
mem_copy(aBuffer + 9, pChunk->m_pData, pChunk->m_DataSize);
net_udp_send(m_Socket, &pChunk->m_Address, aBuffer, pChunk->m_DataSize + 9);
return 0;
}
void CNetServer::SetMaxClientsPerIP(int Max)
{
// clamp
if(Max < 1)
Max = 1;
else if(Max > NET_MAX_CLIENTS)
Max = NET_MAX_CLIENTS;
m_MaxClientsPerIP = Max;
}
2014-08-09 15:25:29 +00:00
bool CNetServer::SetTimedOut(int ClientID, int OrigID)
{
if(m_aSlots[ClientID].m_Connection.State() != NET_CONNSTATE_ERROR)
2014-08-09 15:25:29 +00:00
return false;
2020-06-20 16:50:14 +00:00
m_aSlots[ClientID].m_Connection.SetTimedOut(ClientAddr(OrigID), m_aSlots[OrigID].m_Connection.SeqSequence(), m_aSlots[OrigID].m_Connection.AckSequence(), m_aSlots[OrigID].m_Connection.SecurityToken(), m_aSlots[OrigID].m_Connection.ResendBuffer(), m_aSlots[OrigID].m_Connection.m_Sixup);
2014-08-09 15:25:29 +00:00
m_aSlots[OrigID].m_Connection.Reset();
return true;
}
void CNetServer::SetTimeoutProtected(int ClientID)
{
m_aSlots[ClientID].m_Connection.m_TimeoutProtected = true;
}
2014-08-17 03:04:37 +00:00
int CNetServer::ResetErrorString(int ClientID)
{
m_aSlots[ClientID].m_Connection.ResetErrorString();
return 0;
}
const char *CNetServer::ErrorString(int ClientID)
{
return m_aSlots[ClientID].m_Connection.ErrorString();
}