mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
anti spoof for token protocol added
This commit is contained in:
parent
a58ad6aef7
commit
a2174bf179
|
@ -178,6 +178,8 @@ private:
|
||||||
void ResendChunk(CNetChunkResend *pResend);
|
void ResendChunk(CNetChunkResend *pResend);
|
||||||
void Resend();
|
void Resend();
|
||||||
|
|
||||||
|
bool HasSecurityToken;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool m_TimeoutProtected;
|
bool m_TimeoutProtected;
|
||||||
bool m_TimeoutSituation;
|
bool m_TimeoutSituation;
|
||||||
|
@ -209,6 +211,9 @@ public:
|
||||||
int SeqSequence() const { return m_Sequence; }
|
int SeqSequence() const { return m_Sequence; }
|
||||||
int SecurityToken() const { return m_SecurityToken; }
|
int SecurityToken() const { return m_SecurityToken; }
|
||||||
void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken);
|
void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken);
|
||||||
|
|
||||||
|
// anti spoof
|
||||||
|
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CConsoleNetConnection
|
class CConsoleNetConnection
|
||||||
|
@ -282,6 +287,14 @@ class CNetServer
|
||||||
|
|
||||||
CNetRecvUnpacker m_RecvUnpacker;
|
CNetRecvUnpacker m_RecvUnpacker;
|
||||||
|
|
||||||
|
void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
|
||||||
|
void OnPreConnMsg(NETADDR &Addr, const 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);
|
||||||
|
int NumClientsWithAddr(NETADDR Addr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
|
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
|
||||||
|
|
||||||
|
@ -311,6 +324,9 @@ public:
|
||||||
|
|
||||||
int ResetErrorString(int ClientID);
|
int ResetErrorString(int ClientID);
|
||||||
const char *ErrorString(int ClientID);
|
const char *ErrorString(int ClientID);
|
||||||
|
|
||||||
|
// anti spoof
|
||||||
|
SECURITY_TOKEN GetToken(const NETADDR &Addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CNetConsole
|
class CNetConsole
|
||||||
|
@ -405,6 +421,8 @@ public:
|
||||||
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
|
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 SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize);
|
||||||
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken);
|
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken);
|
||||||
|
|
||||||
|
|
||||||
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket);
|
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
|
// The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
|
||||||
|
|
|
@ -203,6 +203,23 @@ void CNetConnection::Disconnect(const char *pReason)
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
|
||||||
|
m_State = NET_CONNSTATE_ONLINE;
|
||||||
|
|
||||||
|
m_PeerAddr = Addr;
|
||||||
|
mem_zero(m_ErrorString, sizeof(m_ErrorString));
|
||||||
|
|
||||||
|
int64 Now = time_get();
|
||||||
|
m_LastSendTime = Now;
|
||||||
|
m_LastRecvTime = Now;
|
||||||
|
m_LastUpdateTime = Now;
|
||||||
|
|
||||||
|
m_SecurityToken = SecurityToken;
|
||||||
|
}
|
||||||
|
|
||||||
int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken)
|
int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken)
|
||||||
{
|
{
|
||||||
if (State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
|
if (State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
|
||||||
|
|
|
@ -9,6 +9,11 @@
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include <engine/external/md5/md5.h>
|
#include <engine/external/md5/md5.h>
|
||||||
|
|
||||||
|
static SECURITY_TOKEN ToSecurityToken(const unsigned char* pData)
|
||||||
|
{
|
||||||
|
return (int)pData[0] | (pData[1] << 8) | (pData[2] << 16) | (pData[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags)
|
bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags)
|
||||||
{
|
{
|
||||||
// zero out the whole structure
|
// zero out the whole structure
|
||||||
|
@ -86,6 +91,148 @@ int CNetServer::Update()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECURITY_TOKEN CNetServer::GetToken(const NETADDR &Addr)
|
||||||
|
{
|
||||||
|
md5_state_t md5;
|
||||||
|
md5_byte_t digest[16];
|
||||||
|
SECURITY_TOKEN SecurityToken;
|
||||||
|
md5_init(&md5);
|
||||||
|
|
||||||
|
md5_append(&md5, (unsigned char*)m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed));
|
||||||
|
md5_append(&md5, (unsigned char*)&Addr, sizeof(Addr));
|
||||||
|
|
||||||
|
md5_finish(&md5, digest);
|
||||||
|
SecurityToken = *(SECURITY_TOKEN*)digest;
|
||||||
|
|
||||||
|
if (SecurityToken == NET_SECURITY_TOKEN_UNKNOWN ||
|
||||||
|
SecurityToken == NET_SECURITY_TOKEN_UNSUPPORTED)
|
||||||
|
SecurityToken = 1;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
NETADDR ThisAddr = Addr, OtherAddr;
|
||||||
|
|
||||||
|
int FoundAddr = 0;
|
||||||
|
ThisAddr.port = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < MaxClients(); ++i)
|
||||||
|
{
|
||||||
|
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
OtherAddr = *m_aSlots[i].m_Connection.PeerAddress();
|
||||||
|
OtherAddr.port = 0;
|
||||||
|
if(!net_addr_comp(&ThisAddr, &OtherAddr))
|
||||||
|
FoundAddr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FoundAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken)
|
||||||
|
{
|
||||||
|
// check for sv_max_clients_per_ip
|
||||||
|
if (NumClientsWithAddr(Addr) + 1 > m_MaxClientsPerIP)
|
||||||
|
{
|
||||||
|
char aBuf[128];
|
||||||
|
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP);
|
||||||
|
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf), SecurityToken);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const char FullMsg[] = "This server is full";
|
||||||
|
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), SecurityToken);
|
||||||
|
|
||||||
|
return -1; // failed to add client
|
||||||
|
}
|
||||||
|
|
||||||
|
// init connection slot
|
||||||
|
m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken);
|
||||||
|
|
||||||
|
if(m_pfnNewClient)
|
||||||
|
m_pfnNewClient(Slot, m_UserPtr);
|
||||||
|
|
||||||
|
return Slot; // done
|
||||||
|
}
|
||||||
|
|
||||||
|
void CNetServer::OnPreConnMsg(NETADDR &Addr, const CNetPacketConstruct &Packet)
|
||||||
|
{
|
||||||
|
dbg_msg("asd", "onpreconn");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet)
|
||||||
|
{
|
||||||
|
if (ClientExists(Addr))
|
||||||
|
return; // silently ignore
|
||||||
|
|
||||||
|
|
||||||
|
if (ControlMsg == NET_CTRLMSG_CONNECT)
|
||||||
|
{
|
||||||
|
bool SupportsToken = Packet.m_DataSize >=
|
||||||
|
(int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN));
|
||||||
|
|
||||||
|
if (SupportsToken)
|
||||||
|
{
|
||||||
|
// response connection request with token
|
||||||
|
SECURITY_TOKEN Token = GetToken(Addr);
|
||||||
|
SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC), Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ControlMsg == NET_CTRLMSG_ACCEPT && Packet.m_DataSize == 1 + sizeof(SECURITY_TOKEN))
|
||||||
|
{
|
||||||
|
SECURITY_TOKEN Token = ToSecurityToken(&Packet.m_aChunkData[1]);
|
||||||
|
if (Token == GetToken(Addr))
|
||||||
|
{
|
||||||
|
// correct token
|
||||||
|
// try to accept client
|
||||||
|
TryAcceptClient(Addr, Token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid token
|
||||||
|
if (g_Config.m_Debug)
|
||||||
|
dbg_msg("security", "invalid token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetServer::ClientExists(const NETADDR &Addr)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < MaxClients(); i++)
|
||||||
|
{
|
||||||
|
if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE &&
|
||||||
|
net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0)
|
||||||
|
{
|
||||||
|
// found
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't exist
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: chopp up this function into smaller working parts
|
TODO: chopp up this function into smaller working parts
|
||||||
*/
|
*/
|
||||||
|
@ -130,106 +277,34 @@ int CNetServer::Recv(CNetChunk *pChunk)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: check size here
|
// normal packet, find matching slot
|
||||||
if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT)
|
bool Found = false;
|
||||||
|
for(int i = 0; i < MaxClients(); i++)
|
||||||
{
|
{
|
||||||
Found = false;
|
if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0)
|
||||||
|
|
||||||
// check if we already got this client
|
|
||||||
for(int i = 0; i < MaxClients(); i++)
|
|
||||||
{
|
{
|
||||||
if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE &&
|
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE ||
|
||||||
m_aSlots[i].m_Connection.State() != NET_CONNSTATE_ERROR &&
|
m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)
|
||||||
net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0)
|
continue;
|
||||||
{
|
|
||||||
Found = true; // silent ignore.. we got this client already
|
|
||||||
//if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)
|
|
||||||
//{
|
|
||||||
// m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr);
|
|
||||||
// if(m_pfnNewClient)
|
|
||||||
// m_pfnNewClient(i, m_UserPtr);
|
|
||||||
//}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// client that wants to connect
|
Found = true;
|
||||||
if(!Found)
|
if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
|
||||||
{
|
|
||||||
// only allow a specific number of players with the same ip
|
|
||||||
NETADDR ThisAddr = Addr, OtherAddr;
|
|
||||||
int FoundAddr = 1;
|
|
||||||
ThisAddr.port = 0;
|
|
||||||
for(int i = 0; i < MaxClients(); ++i)
|
|
||||||
{
|
{
|
||||||
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
|
if(m_RecvUnpacker.m_Data.m_DataSize)
|
||||||
continue;
|
m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i);
|
||||||
|
|
||||||
OtherAddr = *m_aSlots[i].m_Connection.PeerAddress();
|
|
||||||
OtherAddr.port = 0;
|
|
||||||
if(!net_addr_comp(&ThisAddr, &OtherAddr))
|
|
||||||
{
|
|
||||||
if(FoundAddr++ >= m_MaxClientsPerIP)
|
|
||||||
{
|
|
||||||
char aBuf[128];
|
|
||||||
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP);
|
|
||||||
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf), NET_SECURITY_TOKEN_UNSUPPORTED);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; i < MaxClients(); i++)
|
|
||||||
{
|
|
||||||
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
|
|
||||||
{
|
|
||||||
Found = true;
|
|
||||||
long timestamp = time_get();
|
|
||||||
md5_state_t md5;
|
|
||||||
md5_byte_t digest[16];
|
|
||||||
SECURITY_TOKEN securityToken;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
md5_init(&md5);
|
|
||||||
md5_append(&md5, (unsigned char*)m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed));
|
|
||||||
md5_append(&md5, (unsigned char*)&Addr, sizeof(Addr));
|
|
||||||
md5_append(&md5, (unsigned char*)×tamp, sizeof(timestamp));
|
|
||||||
md5_finish(&md5, digest);
|
|
||||||
securityToken = ToSecurityToken(digest);
|
|
||||||
timestamp++;
|
|
||||||
}
|
|
||||||
while (securityToken == NET_SECURITY_TOKEN_UNKNOWN || securityToken == NET_SECURITY_TOKEN_UNSUPPORTED);
|
|
||||||
m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr, securityToken);
|
|
||||||
if(m_pfnNewClient)
|
|
||||||
m_pfnNewClient(i, m_UserPtr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Found)
|
|
||||||
{
|
|
||||||
const char FullMsg[] = "This server is full";
|
|
||||||
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), NET_SECURITY_TOKEN_UNSUPPORTED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!Found)
|
||||||
{
|
{
|
||||||
// normal packet, find matching slot
|
if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL &&
|
||||||
for(int i = 0; i < MaxClients(); i++)
|
m_RecvUnpacker.m_Data.m_DataSize > 1)
|
||||||
{
|
// got control msg with extra size (should support token)
|
||||||
if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0)
|
OnTokenCtrlMsg(Addr, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);
|
||||||
{
|
else
|
||||||
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE ||
|
// got connection-less ctrl or sys msg
|
||||||
m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)
|
OnPreConnMsg(Addr, m_RecvUnpacker.m_Data);
|
||||||
continue;
|
|
||||||
if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
|
|
||||||
{
|
|
||||||
if(m_RecvUnpacker.m_Data.m_DataSize)
|
|
||||||
m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue