Esempio n. 1
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
}
Esempio n. 2
0
// 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;
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
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;
}
Esempio n. 5
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);
	}
}
Esempio n. 6
0
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
}
Esempio n. 7
0
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);
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
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);
}
Esempio n. 11
0
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);
}
Esempio n. 12
0
// 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);
	}
}
Esempio n. 13
0
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;
}
Esempio n. 14
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;
}
Esempio n. 15
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);
			}
		}
	}
Esempio n. 16
0
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);
}
Esempio n. 17
0
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;
}
Esempio n. 18
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;
}
Esempio n. 19
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);
	}
}