int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth) { // 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 (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) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true); dbg_msg("security", "Client accepted %s", aAddrStr); } if (VanillaAuth) m_pfnNewClientNoAuth(Slot, true, m_UserPtr); else m_pfnNewClient(Slot, m_UserPtr); return Slot; // done }
// manipulate entries CServerEntry *CServerBrowser::Add(int ServerlistType, const NETADDR &Addr) { // create new pEntry CServerEntry *pEntry = (CServerEntry *)m_aServerlist[ServerlistType].m_ServerlistHeap.Allocate(sizeof(CServerEntry)); mem_zero(pEntry, sizeof(CServerEntry)); // set the info pEntry->m_Addr = Addr; pEntry->m_InfoState = CServerEntry::STATE_INVALID; pEntry->m_CurrentToken = GetNewToken(); pEntry->m_Info.m_NetAddr = Addr; pEntry->m_Info.m_Latency = 999; net_addr_str(&Addr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), true); str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName)); str_copy(pEntry->m_Info.m_aHostname, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aHostname)); // check if it's a favorite if(m_ServerBrowserFavorites.FindFavoriteByAddr(Addr, 0)) pEntry->m_Info.m_Favorite = 1; // add to the hash list int Hash = AddrHash(&Addr); pEntry->m_pNextIp = m_aServerlist[ServerlistType].m_aServerlistIp[Hash]; m_aServerlist[ServerlistType].m_aServerlistIp[Hash] = pEntry; if(m_aServerlist[ServerlistType].m_NumServers == m_aServerlist[ServerlistType].m_NumServerCapacity) { if(m_aServerlist[ServerlistType].m_NumServerCapacity == 0) { // alloc start size m_aServerlist[ServerlistType].m_NumServerCapacity = 1000; m_aServerlist[ServerlistType].m_ppServerlist = (CServerEntry **)mem_alloc(m_aServerlist[ServerlistType].m_NumServerCapacity*sizeof(CServerEntry*), 1); } else { // increase size m_aServerlist[ServerlistType].m_NumServerCapacity += 100; CServerEntry **ppNewlist = (CServerEntry **)mem_alloc(m_aServerlist[ServerlistType].m_NumServerCapacity*sizeof(CServerEntry*), 1); mem_copy(ppNewlist, m_aServerlist[ServerlistType].m_ppServerlist, m_aServerlist[ServerlistType].m_NumServers*sizeof(CServerEntry*)); mem_free(m_aServerlist[ServerlistType].m_ppServerlist); m_aServerlist[ServerlistType].m_ppServerlist = ppNewlist; } } // add to list m_aServerlist[ServerlistType].m_ppServerlist[m_aServerlist[ServerlistType].m_NumServers] = pEntry; pEntry->m_Info.m_ServerIndex = m_aServerlist[ServerlistType].m_NumServers; m_aServerlist[ServerlistType].m_NumServers++; return pEntry; }
void CNetServer::BanRemoveByObject(CBan *pBan) { int IpHash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3]+ pBan->m_Info.m_Addr.ip[4]+pBan->m_Info.m_Addr.ip[5]+pBan->m_Info.m_Addr.ip[6]+pBan->m_Info.m_Addr.ip[7]+ pBan->m_Info.m_Addr.ip[8]+pBan->m_Info.m_Addr.ip[9]+pBan->m_Info.m_Addr.ip[10]+pBan->m_Info.m_Addr.ip[11]+ pBan->m_Info.m_Addr.ip[12]+pBan->m_Info.m_Addr.ip[13]+pBan->m_Info.m_Addr.ip[14]+pBan->m_Info.m_Addr.ip[15])&0xff; char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&pBan->m_Info.m_Addr, aAddrStr, sizeof(aAddrStr)); dbg_msg("netserver", "removing ban on %s", aAddrStr); MACRO_LIST_UNLINK(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); MACRO_LIST_UNLINK(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext); MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); }
int CEcon::DelClientCallback(int ClientID, const char *pReason, void *pUser) { CEcon *pThis = (CEcon *)pUser; char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(pThis->m_NetConsole.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf); pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY; return 0; }
void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData) { CServerBrowser *pSelf = (CServerBrowser *)pUserData; char aAddrStr[128]; char aBuffer[256]; for(int i = 0; i < pSelf->m_NumFavoriteServers; i++) { net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr), true); str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr); pConfig->WriteLine(aBuffer); } }
void CSpoofRemote::StartConnection(void *pUserData) { #if defined(CONF_SPOOFING) CSpoofRemote *pSelf = (CSpoofRemote *)pUserData; pSelf->m_ConnState = CONNSTATE_CONNECTING; pSelf->Console()->Print(0, "spfrmt", "Connecting to zervor...", false); NETADDR BindAddr; mem_zero(&pSelf->m_HostAddress, sizeof(pSelf->m_HostAddress)); mem_zero(&BindAddr, sizeof(BindAddr)); // lookup if(net_host_lookup(g_Config.m_ClSpoofSrvIP, &pSelf->m_HostAddress, NETTYPE_IPV4) != 0) { pSelf->Console()->Printf(IConsole::OUTPUT_LEVEL_STANDARD, "spfrmt", "ERROR: Can't resolve %s", g_Config.m_ClSpoofSrvIP); pSelf->m_ConnState = CONNSTATE_CONNECTING; return; } pSelf->m_HostAddress.port = (unsigned short)g_Config.m_ClSpoofSrvPort; // connect BindAddr.type = NETTYPE_IPV4; pSelf->m_Socket = net_tcp_create(BindAddr); if(net_tcp_connect(pSelf->m_Socket, &pSelf->m_HostAddress) != 0) { net_tcp_close(pSelf->m_Socket); char aBuf[128]; net_addr_str(&pSelf->m_HostAddress, aBuf, sizeof(aBuf), 0); pSelf->Console()->Printf(IConsole::OUTPUT_LEVEL_STANDARD, "spfrmt", "ERROR: Can't connect to '%s:%d' on type=%i", aBuf, pSelf->m_HostAddress.port, pSelf->m_HostAddress.type); pSelf->m_ConnState = CONNSTATE_DISCONNECTED; int error = net_errno(); #if defined(CONF_FAMILY_WINDOWS) dbg_msg("spfrmt", " : (errorcode=%d)", error); #else dbg_msg("spfrmt", " : (%d '%s')", error, strerror(error)); #endif return; } pSelf->m_LastAck = time_get(); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "spfrmt", "connected, creating threads...", false); pSelf->m_ConnState = CONNSTATE_CONNECTED; pSelf->m_pWorkerThread = thread_init(CSpoofRemote::Worker, pUserData); pSelf->m_pListenerThread = thread_init(CSpoofRemote::Listener, pUserData); #endif }
void CNetBan::ConBansSave(IConsole::IResult *pResult, void *pUser) { CNetBan *pThis = static_cast<CNetBan *>(pUser); char aBuf[256]; IOHANDLE File = pThis->Storage()->OpenFile(pResult->GetString(0), IOFLAG_WRITE, IStorage::TYPE_SAVE); if(!File) { str_format(aBuf, sizeof(aBuf), "failed to save banlist to '%s'", pResult->GetString(0)); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); return; } int Now = time_timestamp(); char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE]; for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext) { int Min = pBan->m_Info.m_Expires>-1 ? (pBan->m_Info.m_Expires-Now+59)/60 : -1; net_addr_str(&pBan->m_Data, aAddrStr1, sizeof(aAddrStr1), false); str_format(aBuf, sizeof(aBuf), "ban %s %i %s", aAddrStr1, Min, pBan->m_Info.m_aReason); io_write(File, aBuf, str_length(aBuf)); io_write_newline(File); } for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext) { int Min = pBan->m_Info.m_Expires>-1 ? (pBan->m_Info.m_Expires-Now+59)/60 : -1; net_addr_str(&pBan->m_Data.m_LB, aAddrStr1, sizeof(aAddrStr1), false); net_addr_str(&pBan->m_Data.m_UB, aAddrStr2, sizeof(aAddrStr2), false); str_format(aBuf, sizeof(aBuf), "ban_range %s %s %i %s", aAddrStr1, aAddrStr2, Min, pBan->m_Info.m_aReason); io_write(File, aBuf, str_length(aBuf)); io_write_newline(File); } io_close(File); str_format(aBuf, sizeof(aBuf), "saved banlist to '%s'", pResult->GetString(0)); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); }
CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr) { int Hash = Addr.ip[0]; CServerEntry *pEntry = 0; int i; // create new pEntry pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry)); mem_zero(pEntry, sizeof(CServerEntry)); // set the info pEntry->m_Addr = Addr; pEntry->m_Info.m_NetAddr = Addr; pEntry->m_Info.m_Latency = 999; net_addr_str(&Addr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), true); str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName)); // check if it's a favorite for(i = 0; i < m_NumFavoriteServers; i++) { if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0) pEntry->m_Info.m_Favorite = 1; } // add to the hash list pEntry->m_pNextIp = m_aServerlistIp[Hash]; m_aServerlistIp[Hash] = pEntry; if(m_NumServers == m_NumServerCapacity) { CServerEntry **ppNewlist; m_NumServerCapacity += 100; ppNewlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1); mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*)); mem_free(m_ppServerlist); m_ppServerlist = ppNewlist; } // add to list m_ppServerlist[m_NumServers] = pEntry; pEntry->m_Info.m_ServerIndex = m_NumServers; m_NumServers++; return pEntry; }
int CEcon::NewClientCallback(int ClientID, void *pUser) { CEcon *pThis = (CEcon *)pUser; char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(pThis->m_NetConsole.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[128]; str_format(aBuf, sizeof(aBuf), "client accepted. cid=%d addr=%s'", ClientID, aAddrStr); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf); pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED; pThis->m_aClients[ClientID].m_TimeConnected = time_get(); pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_NetConsole.Send(ClientID, "Enter password:"); return 0; }
// unmute by mute list index void CGameContext::ConUnmute(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; char aIpBuf[64]; char aBuf[64]; int Victim = pResult->GetVictim(); if (Victim < 0 || Victim >= pSelf->m_NumMutes) return; pSelf->m_NumMutes--; pSelf->m_aMutes[Victim] = pSelf->m_aMutes[pSelf->m_NumMutes]; net_addr_str(&pSelf->m_aMutes[Victim].m_Addr, aIpBuf, sizeof(aIpBuf)); str_format(aBuf, sizeof(aBuf), "Unmuted %s", aIpBuf); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mutes", aBuf); }
void CServerBrowser::AddRecent(const NETADDR& Addr) { // add the address into the database { char aNetAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&Addr, aNetAddrStr, sizeof(aNetAddrStr), Addr.port); char *pQueryBuf = sqlite3_mprintf("INSERT OR REPLACE INTO recent (addr) VALUES ('%q');", aNetAddrStr); CQueryRecent *pQuery = new CQueryRecent(); pQuery->Query(m_pRecentDB, pQueryBuf); sqlite3_free(pQueryBuf); } // add it to our current session recent cache RecentServer e(Addr, m_aRecentServers.size()); for(int i = 0; i < m_aRecentServers.size(); i++) if(m_aRecentServers[i] == e) m_aRecentServers.remove_index(i); m_aRecentServers.add(e); }
// list mutes void CGameContext::ConMutes(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; char aIpBuf[64]; char aBuf[128]; pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mutes", "Active mutes:"); for (int i = 0; i < pSelf->m_NumMutes; i++) { net_addr_str(&pSelf->m_aMutes[i].m_Addr, aIpBuf, sizeof(aIpBuf), false); str_format( aBuf, sizeof aBuf, "%d: \"%s\", %d seconds left", i, aIpBuf, (pSelf->m_aMutes[i].m_Expire - pSelf->Server()->Tick()) / pSelf->Server()->TickSpeed()); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mutes", aBuf); } }
int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) { CServer *pThis = (CServer *)pUser; char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(pThis->m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); // notify the mod about the drop if(pThis->m_aClients[ClientID].m_State >= CClient::STATE_READY) pThis->GameServer()->OnClientDrop(ClientID, pReason); pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY; pThis->m_aClients[ClientID].m_aName[0] = 0; pThis->m_aClients[ClientID].m_aClan[0] = 0; pThis->m_aClients[ClientID].m_Country = -1; pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].m_Snapshots.PurgeAll(); return 0; }
/* TODO: chopp up this function into smaller working parts */ int CNetServer::Recv(CNetChunk *pChunk) { while(1) { NETADDR Addr; // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { // 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); continue; } if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { 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; return 1; } else { // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { bool Found = false; // check if we already got this client 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 = true; // silent ignore.. we got this client already break; } } // client that wants to connect if(!Found) { CNetChunk Packet; char aBuffer[sizeof(BANMASTER_IPCHECK) + NET_BANMASTER_NR_SIZE + NETADDR_MAXSTRSIZE]; mem_copy(aBuffer, BANMASTER_IPCHECK, sizeof(BANMASTER_IPCHECK)); net_addr_str(&Addr, aBuffer + sizeof(BANMASTER_IPCHECK) + NET_BANMASTER_NR_SIZE, sizeof(aBuffer) - NET_BANMASTER_NR_SIZE - sizeof(BANMASTER_IPCHECK), false); Packet.m_ClientID = -1; Packet.m_Flags = NETSENDFLAG_CONNLESS; Packet.m_DataSize = str_length(aBuffer + sizeof(BANMASTER_IPCHECK) + NET_BANMASTER_NR_SIZE) + sizeof(BANMASTER_IPCHECK) + NET_BANMASTER_NR_SIZE + 1; Packet.m_pData = aBuffer; int64 Now = time_get(); for(int i = 0; i < m_NumBanmasters; i++) { Packet.m_Address = m_aBanmasters[i]; m_aTimeouts[i] = Now; for(int j = 0; j < NET_BANMASTER_NR_SIZE; j++) { m_aSequenceNumbers[i][j] = rand() % 256; } mem_copy(aBuffer + sizeof(BANMASTER_IPCHECK), m_aSequenceNumbers[i], NET_BANMASTER_NR_SIZE); Send(&Packet); } // 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) continue; 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)); return 0; } } } for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) { Found = true; m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); 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)); } } } else { // normal packet, find matching slot for(int i = 0; i < MaxClients(); i++) { if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) { 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); } } } } } } } return 0; }
void CServer::ProcessClientPacket(CNetChunk *pPacket) { int ClientID = pPacket->m_ClientID; CUnpacker Unpacker; Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize); // unpack msgid and system flag int Msg = Unpacker.GetInt(); int Sys = Msg&1; Msg >>= 1; if(Unpacker.Error()) return; if(Sys) { // system message if(Msg == NETMSG_INFO) { if(m_aClients[ClientID].m_State == CClient::STATE_AUTH) { char aVersion[64]; str_copy(aVersion, Unpacker.GetString(CUnpacker::SANITIZE_CC), 64); bool CustClt = str_comp(aVersion, GameServer()->NetVersionCust()) == 0; dbg_msg("es", "%s client connected!", CustClt?"cust":"vanilla"); if(!CustClt && str_comp(aVersion, GameServer()->NetVersion()) != 0) { // wrong version char aReason[256]; str_format(aReason, sizeof(aReason), "Wrong version. Server is running '%s' and client '%s'", GameServer()->NetVersion(), aVersion); m_NetServer.Drop(ClientID, aReason); return; } const char *pPassword = Unpacker.GetString(CUnpacker::SANITIZE_CC); if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0) { // wrong password m_NetServer.Drop(ClientID, "Wrong password"); return; } m_aClients[ClientID].m_State = CClient::STATE_CONNECTING; m_aClients[ClientID].m_CustClt = CustClt; SendMap(ClientID); } } else if(Msg == NETMSG_REQUEST_MAP_DATA) { int Chunk = Unpacker.GetInt(); int ChunkSize = 1024-128; int Offset = Chunk * ChunkSize; int Last = 0; // drop faulty map data requests if(Chunk < 0 || Offset > m_CurrentMapSize) return; if(Offset+ChunkSize >= m_CurrentMapSize) { ChunkSize = m_CurrentMapSize-Offset; if(ChunkSize < 0) ChunkSize = 0; Last = 1; } CMsgPacker Msg(NETMSG_MAP_DATA); Msg.AddInt(Last); Msg.AddInt(m_CurrentMapCrc); Msg.AddInt(Chunk); Msg.AddInt(ChunkSize); Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize); SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true); if(g_Config.m_Debug) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf); } } else if(Msg == NETMSG_READY) { if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x addr=%s", ClientID, aAddrStr); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); m_aClients[ClientID].m_State = CClient::STATE_READY; GameServer()->OnClientConnected(ClientID); SendConnectionReady(ClientID); } } else if(Msg == NETMSG_ENTERGAME) { if(m_aClients[ClientID].m_State == CClient::STATE_READY && GameServer()->IsClientReady(ClientID)) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x addr=%s", ClientID, aAddrStr); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); m_aClients[ClientID].m_State = CClient::STATE_INGAME; GameServer()->OnClientEnter(ClientID); } } else if(Msg == NETMSG_INPUT) { CClient::CInput *pInput; int64 TagTime; m_aClients[ClientID].m_LastAckedSnapshot = Unpacker.GetInt(); int IntendedTick = Unpacker.GetInt(); int Size = Unpacker.GetInt(); // check for errors if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE) return; if(m_aClients[ClientID].m_LastAckedSnapshot > 0) m_aClients[ClientID].m_SnapRate = CClient::SNAPRATE_FULL; if(m_aClients[ClientID].m_Snapshots.Get(m_aClients[ClientID].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0) m_aClients[ClientID].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq()); // add message to report the input timing // skip packets that are old if(IntendedTick > m_aClients[ClientID].m_LastInputTick) { int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq(); CMsgPacker Msg(NETMSG_INPUTTIMING); Msg.AddInt(IntendedTick); Msg.AddInt(TimeLeft); SendMsgEx(&Msg, 0, ClientID, true); } m_aClients[ClientID].m_LastInputTick = IntendedTick; pInput = &m_aClients[ClientID].m_aInputs[m_aClients[ClientID].m_CurrentInput]; if(IntendedTick <= Tick()) IntendedTick = Tick()+1; pInput->m_GameTick = IntendedTick; for(int i = 0; i < Size/4; i++) pInput->m_aData[i] = Unpacker.GetInt(); mem_copy(m_aClients[ClientID].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); m_aClients[ClientID].m_CurrentInput++; m_aClients[ClientID].m_CurrentInput %= 200; // call the mod with the fresh input data if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) GameServer()->OnClientDirectInput(ClientID, m_aClients[ClientID].m_LatestInput.m_aData); } else if(Msg == NETMSG_RCON_CMD) { const char *pCmd = Unpacker.GetString(); if(Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); m_RconClientID = ClientID; m_RconAuthLevel = m_aClients[ClientID].m_Authed; Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER); Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); m_RconClientID = IServer::RCON_CID_SERV; m_RconAuthLevel = AUTHED_ADMIN; } } else if(Msg == NETMSG_RCON_AUTH) { const char *pPw; Unpacker.GetString(); // login name, not used pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC); if(Unpacker.Error() == 0) { if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0) { SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console."); } else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0) { CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); Msg.AddInt(1); //authed Msg.AddInt(1); //cmdlist SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); m_aClients[ClientID].m_Authed = AUTHED_ADMIN; int SendRconCmds = Unpacker.GetInt(); if(Unpacker.Error() == 0 && SendRconCmds) m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER); SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted."); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0) { CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); Msg.AddInt(1); //authed Msg.AddInt(1); //cmdlist SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); m_aClients[ClientID].m_Authed = AUTHED_MOD; int SendRconCmds = Unpacker.GetInt(); if(Unpacker.Error() == 0 && SendRconCmds) m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER); SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted."); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } else if(g_Config.m_SvRconMaxTries) { m_aClients[ClientID].m_AuthTries++; char aBuf[128]; str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, g_Config.m_SvRconMaxTries); SendRconLine(ClientID, aBuf); if(m_aClients[ClientID].m_AuthTries >= g_Config.m_SvRconMaxTries) { if(!g_Config.m_SvRconBantime) m_NetServer.Drop(ClientID, "Too many remote console authentication tries"); else m_ServerBan.BanAddr(m_NetServer.ClientAddr(ClientID), g_Config.m_SvRconBantime*60, "Too many remote console authentication tries"); } } else { SendRconLine(ClientID, "Wrong password."); } } } else if(Msg == NETMSG_PING) { CMsgPacker Msg(NETMSG_PING_REPLY); SendMsgEx(&Msg, 0, ClientID, true); } else { if(g_Config.m_Debug) { char aHex[] = "0123456789ABCDEF"; char aBuf[512]; for(int b = 0; b < pPacket->m_DataSize && b < 32; b++) { aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4]; aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; aBuf[b*3+2] = ' '; aBuf[b*3+3] = 0; } char aBufMsg[256]; str_format(aBufMsg, sizeof(aBufMsg), "strange message ClientID=%d msg=%d data_size=%d", ClientID, Msg, pPacket->m_DataSize); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBufMsg); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf); } } }
void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size) { if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME) net_addr_str(m_NetServer.ClientAddr(ClientID), pAddrStr, Size, false); }
int main(int argc, char **argv) // ignore_convention { NETADDR BindAddr; dbg_logger_stdout(); net_init(); mem_zero(&BindAddr, sizeof(BindAddr)); BindAddr.type = NETTYPE_ALL; BindAddr.port = VERSIONSRV_PORT; if(!g_NetOp.Open(BindAddr, 0)) { dbg_msg("mastersrv", "couldn't start network"); return -1; } BuildPackets(); ReadNews(); ReadServerList(); dbg_msg("versionsrv", "started"); while(1) { g_NetOp.Update(); // process packets CNetChunk Packet; while(g_NetOp.Recv(&Packet)) { if(Packet.m_DataSize == sizeof(VERSIONSRV_GETVERSION) && mem_comp(Packet.m_pData, VERSIONSRV_GETVERSION, sizeof(VERSIONSRV_GETVERSION)) == 0) { SendVer(&Packet.m_Address); char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&Packet.m_Address, aAddrStr, sizeof(aAddrStr), false); dbg_msg("versionsrv", "version request by %s", aAddrStr); } if(Packet.m_DataSize == sizeof(VERSIONSRV_GETNEWS) && mem_comp(Packet.m_pData, VERSIONSRV_GETNEWS, sizeof(VERSIONSRV_GETNEWS)) == 0) { SendNews(&Packet.m_Address); } if(Packet.m_DataSize == sizeof(VERSIONSRV_GETMAPLIST) && mem_comp(Packet.m_pData, VERSIONSRV_GETMAPLIST, sizeof(VERSIONSRV_GETMAPLIST)) == 0) { CNetChunk p; p.m_ClientID = -1; p.m_Address = Packet.m_Address; p.m_Flags = NETSENDFLAG_CONNLESS; for(int i = 0; i < m_NumPackets; i++) { p.m_DataSize = m_aPackets[i].m_Size; p.m_pData = &m_aPackets[i].m_Data; g_NetOp.Send(&p); } } if(m_ServerListLoaded && Packet.m_DataSize == sizeof(VERSIONSRV_GETDDNETLIST) + 4 && mem_comp(Packet.m_pData, VERSIONSRV_GETDDNETLIST, sizeof(VERSIONSRV_GETDDNETLIST)) == 0) { char aToken[4]; mem_copy(aToken, (char*)Packet.m_pData+sizeof(VERSIONSRV_GETDDNETLIST), 4); SendServerList(&Packet.m_Address, aToken); } } // wait for input net_socket_read_wait(g_NetOp.m_Socket, 1000000); } return 0; }
/* TODO: chopp up this function into smaller working parts */ int CNetServer::Recv(CNetChunk *pChunk) { unsigned Now = time_timestamp(); while(1) { NETADDR Addr; // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { CBan *pBan = 0; NETADDR BanAddr = Addr; int IpHash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3]+BanAddr.ip[4]+BanAddr.ip[5]+BanAddr.ip[6]+BanAddr.ip[7]+ BanAddr.ip[8]+BanAddr.ip[9]+BanAddr.ip[10]+BanAddr.ip[11]+BanAddr.ip[12]+BanAddr.ip[13]+BanAddr.ip[14]+BanAddr.ip[15])&0xff; int Found = 0; BanAddr.port = 0; // search a ban for(pBan = m_aBans[IpHash]; pBan; pBan = pBan->m_pHashNext) { if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0) break; } // check if we just should drop the packet if(pBan) { // banned, reply with a message char BanStr[128]; if(pBan->m_Info.m_Expires > -1) { int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60; if(Mins <= 1) str_format(BanStr, sizeof(BanStr), "Banned for 1 minute (%s)", pBan->m_Info.m_Reason); else str_format(BanStr, sizeof(BanStr), "Banned for %d minutes (%s)", Mins, pBan->m_Info.m_Reason); } else str_format(BanStr, sizeof(BanStr), "Banned for life (%s)", pBan->m_Info.m_Reason); CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, BanStr, str_length(BanStr)+1); continue; } if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { 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; return 1; } else { // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { Found = 0; // check if we already got this client for(int i = 0; i < MaxClients(); i++) { NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && net_addr_comp(&PeerAddr, &Addr) == 0) { Found = 1; // silent ignore.. we got this client already break; } } // client that wants to connect if(!Found) { CNetChunk Packet; char aBuffer[sizeof(BANMASTER_IPCHECK) + NETADDR_MAXSTRSIZE]; mem_copy(aBuffer, BANMASTER_IPCHECK, sizeof(BANMASTER_IPCHECK)); net_addr_str(&Addr, aBuffer + sizeof(BANMASTER_IPCHECK), sizeof(aBuffer) - sizeof(BANMASTER_IPCHECK)); Packet.m_ClientID = -1; Packet.m_Flags = NETSENDFLAG_CONNLESS; Packet.m_DataSize = str_length(aBuffer) + 1; Packet.m_pData = aBuffer; for(int i = 0; i < m_NumBanmasters; i++) { Packet.m_Address = m_aBanmasters[i]; Send(&Packet); } // 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) continue; 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)); return 0; } } } for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) { Found = 1; m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); 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)); } } } else { // normal packet, find matching slot for(int i = 0; i < MaxClients(); i++) { NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); if(net_addr_comp(&PeerAddr, &Addr) == 0) { 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); } } } } } } } return 0; }
void Run(int Port, NETADDR Dest) { NETADDR Src = {NETTYPE_IPV4, {0,0,0,0}, Port}; NETSOCKET Socket = net_udp_create(Src); char aBuffer[1024*2]; int ID = 0; int Delaycounter = 0; while(1) { static int Lastcfg = 0; int n = ((time_get()/time_freq())/m_ConfigInterval) % m_ConfigNumpingconfs; CPingConfig Ping = m_aConfigPings[n]; if(n != Lastcfg) dbg_msg("crapnet", "cfg = %d", n); Lastcfg = n; // handle incomming packets while(1) { // fetch data int DataTrash = 0; NETADDR From; int Bytes = net_udp_recv(Socket, &From, aBuffer, 1024*2); if(Bytes <= 0) break; if((rand()%100) < Ping.m_Loss) // drop the packet { if(m_ConfigLog) dbg_msg("crapnet", "dropped packet"); continue; } // create new packet CPacket *p = (CPacket *)mem_alloc(sizeof(CPacket)+Bytes, 1); if(net_addr_comp(&From, &Dest) == 0) p->m_SendTo = Src; // from the server else { Src = From; // from the client p->m_SendTo = Dest; } // queue packet p->m_pPrev = m_pLast; p->m_pNext = 0; if(m_pLast) m_pLast->m_pNext = p; else { m_pFirst = p; m_pLast = p; } m_pLast = p; // set data in packet p->m_Timestamp = time_get(); p->m_DataSize = Bytes; p->m_ID = ID++; mem_copy(p->m_aData, aBuffer, Bytes); if(ID > 20 && Bytes > 6 && DataTrash) { p->m_aData[6+(rand()%(Bytes-6))] = rand()&255; // modify a byte if((rand()%10) == 0) { p->m_DataSize -= rand()%32; if(p->m_DataSize < 6) p->m_DataSize = 6; } } if(Delaycounter <= 0) { if(Ping.m_Delay) p->m_Timestamp += (time_freq()*1000)/Ping.m_Delay; Delaycounter = Ping.m_DelayFreq; } Delaycounter--; if(m_ConfigLog) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&From, aAddrStr, sizeof(aAddrStr), true); dbg_msg("crapnet", "<< %08d %s (%d)", p->m_ID, aAddrStr, p->m_DataSize); } } // /*while(1) {*/ CPacket *p = 0; CPacket *pNext = m_pFirst; while(1) { p = pNext; if(!p) break; pNext = p->m_pNext; if((time_get()-p->m_Timestamp) > m_CurrentLatency) { char aFlags[] = " "; if(m_ConfigReorder && (rand()%2) == 0 && p->m_pNext) { aFlags[0] = 'R'; p = m_pFirst->m_pNext; } if(p->m_pNext) p->m_pNext->m_pPrev = p->m_pPrev; else m_pLast = p->m_pPrev; if(p->m_pPrev) p->m_pPrev->m_pNext = p->m_pNext; else m_pFirst = p->m_pNext; /*CPacket *cur = first; while(cur) { dbg_assert(cur != p, "p still in list"); cur = cur->next; }*/ // send and remove packet //if((rand()%20) != 0) // heavy packetloss net_udp_send(Socket, &p->m_SendTo, p->m_aData, p->m_DataSize); // update lag double Flux = rand()/(double)RAND_MAX; int MsSpike = Ping.m_Spike; int MsFlux = Ping.m_Flux; int MsPing = Ping.m_Base; m_CurrentLatency = ((time_freq()*MsPing)/1000) + (int64)(((time_freq()*MsFlux)/1000)*Flux); // 50ms if(MsSpike && (p->m_ID%100) == 0) { m_CurrentLatency += (time_freq()*MsSpike)/1000; aFlags[1] = 'S'; } if(m_ConfigLog) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&p->m_SendTo, aAddrStr, sizeof(aAddrStr), true); dbg_msg("crapnet", ">> %08d %s (%d) %s", p->m_ID, aAddrStr, p->m_DataSize, aFlags); } mem_free(p); } } thread_sleep(1); } }