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) { // send connectionless packet CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); } else { int Flags = 0; dbg_assert(pChunk->m_ClientID >= 0, "errornous client id"); dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id"); if(pChunk->m_Flags&NETSENDFLAG_VITAL) Flags = NET_CHUNKFLAG_VITAL; if(m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData) == 0) { if(pChunk->m_Flags&NETSENDFLAG_FLUSH) m_aSlots[pChunk->m_ClientID].m_Connection.Flush(); } else { Drop(pChunk->m_ClientID, CLIENTDROPTYPE_ERROR, "Error sending data"); } } return 0; }
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 }
int CNetServer::Update() { for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) Drop(i, m_aSlots[i].m_Connection.ErrorString()); } return 0; }
int CNetServer::Update() { for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR && (!m_aSlots[i].m_Connection.m_TimeoutProtected || !m_aSlots[i].m_Connection.m_TimeoutSituation)) { Drop(i, m_aSlots[i].m_Connection.ErrorString()); } } return 0; }
int CNetServer::Update() { int64 Now = time_get(); for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) { if(Now - m_aSlots[i].m_Connection.ConnectTime() < time_freq() && NetBan()) NetBan()->BanAddr(ClientAddr(i), 60, "Stressing network"); else Drop(i, CLIENTDROPTYPE_STRESSING, m_aSlots[i].m_Connection.ErrorString()); } } return 0; }
int CNetServer::Update() { int Now = time_timestamp(); for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) Drop(i, m_aSlots[i].m_Connection.ErrorString()); } // remove expired bans while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires > -1 && m_BanPool_FirstUsed->m_Info.m_Expires < Now) { CBan *pBan = m_BanPool_FirstUsed; BanRemoveByObject(pBan); } return 0; }
int CNetServer::Update() { int64 Now = time_get(); for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) { if(Now - m_aSlots[i].m_Connection.ConnectTime() < time_freq() && NetBan()) { if(NetBan()->BanAddr(ClientAddr(i), 60, "Stressing network") == -1) Drop(i, m_aSlots[i].m_Connection.ErrorString()); } else Drop(i, m_aSlots[i].m_Connection.ErrorString()); } } m_TokenManager.Update(); m_TokenCache.Update(); return 0; }
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 || (m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR && (!m_aSlots[i].m_Connection.m_TimeoutProtected || !m_aSlots[i].m_Connection.m_TimeoutSituation))) continue; OtherAddr = *m_aSlots[i].m_Connection.PeerAddress(); OtherAddr.port = 0; if(!net_addr_comp(&ThisAddr, &OtherAddr)) FoundAddr++; } return FoundAddr; }
/* 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) { // 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; }
int CNetServer::Send(CNetChunk *pChunk, TOKEN Token) { if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) { 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_ClientID == -1) for(int i = 0; i < MaxClients(); i++) if(net_addr_comp(&pChunk->m_Address, m_aSlots[i].m_Connection.PeerAddress()) == 0) { // upgrade the packet, now that we know its recipent pChunk->m_ClientID = i; break; } if(Token != NET_TOKEN_NONE) { CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, Token, m_TokenManager.GenerateToken(&pChunk->m_Address), pChunk->m_pData, pChunk->m_DataSize); } else { if(pChunk->m_ClientID == -1) { m_TokenCache.SendPacketConnless(&pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); } else { dbg_assert(pChunk->m_ClientID >= 0, "errornous client id"); dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id"); m_aSlots[pChunk->m_ClientID].m_Connection.SendPacketConnless((const char *)pChunk->m_pData, pChunk->m_DataSize); } } } else { if(pChunk->m_DataSize+NET_MAX_CHUNKHEADERSIZE >= NET_MAX_PAYLOAD) { dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize); return -1; } int Flags = 0; dbg_assert(pChunk->m_ClientID >= 0, "errornous client id"); dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id"); if(pChunk->m_Flags&NETSENDFLAG_VITAL) Flags = NET_CHUNKFLAG_VITAL; if(m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData) == 0) { if(pChunk->m_Flags&NETSENDFLAG_FLUSH) m_aSlots[pChunk->m_ClientID].m_Connection.Flush(); } else { Drop(pChunk->m_ClientID, "Error sending data"); } } return 0; }
/* TODO: chopp up this function into smaller working parts */ int CNetServer::Recv(CNetChunk *pChunk, TOKEN *pResponseToken) { 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 for bans char aBuf[128]; if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { // banned, reply with a message CNetBase::SendControlMsg(m_Socket, &Addr, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); continue; } bool Found = false; // try to 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) { if(!(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)) m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); else { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_Address = *m_aSlots[i].m_Connection.PeerAddress(); pChunk->m_ClientID = i; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; if(pResponseToken) *pResponseToken = NET_TOKEN_NONE; return 1; } } } Found = true; } } if(Found) continue; int Accept = m_TokenManager.ProcessMessage(&Addr, &m_RecvUnpacker.m_Data); if(Accept <= 0) continue; if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL) { if(m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { bool Found = false; // 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, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1); 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.SetToken(m_RecvUnpacker.m_Data.m_Token); 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, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); } } else if(m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_TOKEN) m_TokenCache.AddToken(&Addr, m_RecvUnpacker.m_Data.m_ResponseToken, NET_TOKENFLAG_RESPONSEONLY); } else 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; if(pResponseToken) *pResponseToken = m_RecvUnpacker.m_Data.m_ResponseToken; return 1; } } } 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) { // 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; }
int CNetServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason) { int IpHash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3]+Addr.ip[4]+Addr.ip[5]+Addr.ip[6]+Addr.ip[7]+ Addr.ip[8]+Addr.ip[9]+Addr.ip[10]+Addr.ip[11]+Addr.ip[12]+Addr.ip[13]+Addr.ip[14]+Addr.ip[15])&0xff; int Stamp = -1; CBan *pBan; // remove the port Addr.port = 0; if(Seconds) Stamp = time_timestamp() + Seconds; // search to see if it already exists pBan = m_aBans[IpHash]; MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); if(pBan) { // adjust the ban pBan->m_Info.m_Expires = Stamp; return 0; } if(!m_BanPool_FirstFree) return -1; // fetch and clear the new ban pBan = m_BanPool_FirstFree; MACRO_LIST_UNLINK(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); // setup the ban info pBan->m_Info.m_Expires = Stamp; pBan->m_Info.m_Addr = Addr; str_copy(pBan->m_Info.m_Reason, pReason, sizeof(pBan->m_Info.m_Reason)); // add it to the ban hash MACRO_LIST_LINK_FIRST(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext); // insert it into the used list { if(m_BanPool_FirstUsed) { CBan *pInsertAfter = m_BanPool_FirstUsed; MACRO_LIST_FIND(pInsertAfter, m_pNext, Stamp < pInsertAfter->m_Info.m_Expires); if(pInsertAfter) pInsertAfter = pInsertAfter->m_pPrev; else { // add to last pInsertAfter = m_BanPool_FirstUsed; while(pInsertAfter->m_pNext) pInsertAfter = pInsertAfter->m_pNext; } if(pInsertAfter) { MACRO_LIST_LINK_AFTER(pBan, pInsertAfter, m_pPrev, m_pNext); } else { MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); } } else { MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); } } // drop banned clients { char Buf[128]; NETADDR BanAddr; if(Stamp > -1) { int Mins = (Seconds + 59) / 60; if(Mins <= 1) str_format(Buf, sizeof(Buf), "You have been banned for 1 minute (%s)", pReason); else str_format(Buf, sizeof(Buf), "You have been banned for %d minutes (%s)", Mins, pReason); } else str_format(Buf, sizeof(Buf), "You have been banned for life (%s)", pReason); for(int i = 0; i < MaxClients(); i++) { BanAddr = m_aSlots[i].m_Connection.PeerAddress(); BanAddr.port = 0; if(net_addr_comp(&Addr, &BanAddr) == 0) Drop(i, Buf); } } return 0; }