www.pudn.com > eMule0.42e-Sources.zip > ClientList.cpp


//this file is part of eMule 
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net ) 
// 
//This program is free software; you can redistribute it and/or 
//modify it under the terms of the GNU General Public License 
//as published by the Free Software Foundation; either 
//version 2 of the License, or (at your option) any later version. 
// 
//This program is distributed in the hope that it will be useful, 
//but WITHOUT ANY WARRANTY; without even the implied warranty of 
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
//GNU General Public License for more details. 
// 
//You should have received a copy of the GNU General Public License 
//along with this program; if not, write to the Free Software 
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
#include "stdafx.h" 
#include "emule.h" 
#include "ClientList.h" 
#include "otherfunctions.h" 
#include "Kademlia/Kademlia/kademlia.h" 
#include "Kademlia/routing/contact.h" 
#include "Kademlia/net/kademliaudplistener.h" 
#include "LastCommonRouteFinder.h" 
#include "UploadQueue.h" 
#include "DownloadQueue.h" 
#include "UpDownClient.h" 
#include "ClientCredits.h" 
#include "ListenSocket.h" 
#include "Opcodes.h" 
#include "Sockets.h" 
#ifndef _CONSOLE 
#include "emuledlg.h" 
#include "TransferWnd.h" 
#endif 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
 
CClientList::CClientList(){ 
	m_dwLastBannCleanUp = 0; 
	m_dwLastTrackedCleanUp = 0; 
	m_bannedList.InitHashTable(331); 
	m_trackedClientsList.InitHashTable(2011); 
} 
 
CClientList::~CClientList(){ 
	POSITION pos = m_trackedClientsList.GetStartPosition(); 
	uint32 nKey; 
	CDeletedClient* pResult; 
	while (pos != NULL){ 
		m_trackedClientsList.GetNextAssoc( pos, nKey, pResult ); 
		m_trackedClientsList.RemoveKey(nKey); 
		delete pResult; 
	} 
} 
 
// xrmb : statsclientstatus 
// -khaos--+++> Rewritten to accomodate some new statistics, and just for cleanup's sake. 
//				I've added three new stats: Number of cDonkey clients, # errored clients, # banned clients. 
//				We also now support LMule 
void CClientList::GetStatistics(uint32 &totalclient, int stats[], CMap *clientVersionEDonkey, CMap *clientVersionEDonkeyHybrid, CMap *clientVersionEMule, CMap *clientVersionLMule){ 
	totalclient = list.GetCount(); 
	if(clientVersionEDonkeyHybrid)	clientVersionEDonkeyHybrid->RemoveAll(); 
	if(clientVersionEDonkey)		clientVersionEDonkey->RemoveAll(); 
	if(clientVersionEMule)			clientVersionEMule->RemoveAll(); 
	if(clientVersionLMule)			clientVersionLMule->RemoveAll(); 
 
	for (int i=0;i<15;i++) stats[i]=0; 
 
	stats[7]=m_bannedList.GetCount(); 
 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;){ 
		CUpDownClient* cur_client =	list.GetNext(pos); 
 
		if (cur_client->HasLowID()) 
			++stats[14]; 
		 
		switch (cur_client->GetClientSoft()) { 
 
			case SO_EDONKEY :				 
				if(clientVersionEDonkey) 
				{ 
					++stats[1]; 
					(*clientVersionEDonkey)[cur_client->GetVersion()]++; 
				} 
				break; 
 
			case SO_EDONKEYHYBRID :  
				if(clientVersionEDonkeyHybrid) 
				{ 
					++stats[4]; 
					(*clientVersionEDonkeyHybrid)[cur_client->GetVersion()]++; 
				} 
				break; 
			case SO_EMULE   : 
			case SO_OLDEMULE: 
				if(clientVersionEMule) 
				{ 
					++stats[2]; 
					uint8 version = cur_client->GetMuleVersion(); 
					if (version == 0xFF || version == 0x66 || version==0x69 || version==0x90 || version==0x33 || version==0x60) 
						continue; 
					(*clientVersionEMule)[cur_client->GetVersion()]++; 
				} 
				break; 
 
			case SO_XMULE: 
				if(clientVersionLMule) 
				{ 
					++stats[10]; 
					uint8 version = cur_client->GetMuleVersion(); 
					if (version == 0x66 || version==0x69 || version==0x90 || version==0x33) 
						continue; 
					(*clientVersionLMule)[cur_client->GetVersion()]++; 
				} 
 
				break; 
 
			case SO_UNKNOWN :	++stats[0];		break; 
			case SO_CDONKEY :	++stats[5];		break; 
			case SO_MLDONKEY:	++stats[3];		break; 
			case SO_SHAREAZA:   ++stats[11];    break; 
		} 
 
		if (cur_client->Credits() != NULL){ 
			switch(cur_client->Credits()->GetCurrentIdentState(cur_client->GetIP())){ 
				case IS_IDENTIFIED: 
					stats[12]++; 
					break; 
				case IS_IDFAILED: 
				case IS_IDNEEDED: 
				case IS_IDBADGUY: 
					stats[13]++; 
			} 
		} 
 
		if (cur_client->GetDownloadState()==DS_ERROR || cur_client->GetUploadState()==US_ERROR ) 
				++stats[6]; // Error 
 
		switch (cur_client->GetUserPort()) { 
			case 4662: 
				++stats[8]; // Default Port 
				break; 
			default: 
				++stats[9]; // Other Port 
		} 
	} 
} 
// <-----khaos- 
 
 
void CClientList::AddClient(CUpDownClient* toadd, bool bSkipDupTest) 
{ 
	// skipping the check for duplicate list entries is only to be done for optimization purposes, if the calling 
	// function has ensured that this client instance is not already within the list -> there are never duplicate 
	// client instances in this list. 
	if (!bSkipDupTest){ 
		if(list.Find(toadd)) 
			return; 
	} 
	theApp.emuledlg->transferwnd->clientlistctrl.AddClient(toadd); 
	list.AddTail(toadd); 
} 
 
// ZZ:UploadSpeedSense --> 
bool CClientList::GiveClientsForTraceRoute() { 
    // this is a host that lastCommonRouteFinder can use to traceroute 
    return theApp.lastCommonRouteFinder->AddHostsToCheck(list); 
} 
// ZZ:UploadSpeedSense <-- 
 
void CClientList::RemoveClient(CUpDownClient* toremove){ 
	POSITION pos = list.Find(toremove); 
	if (pos){ 
		//just to be sure... 
		theApp.uploadqueue->RemoveFromUploadQueue(toremove); 
		theApp.uploadqueue->RemoveFromWaitingQueue(toremove); 
		theApp.downloadqueue->RemoveSource(toremove); 
		theApp.emuledlg->transferwnd->clientlistctrl.RemoveClient(toremove); 
		RemoveTCP(toremove); 
		list.RemoveAt(pos); 
	} 
} 
 
void CClientList::DeleteAll(){ 
	theApp.uploadqueue->DeleteAll(); 
	theApp.downloadqueue->DeleteAll(); 
	POSITION pos1, pos2; 
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){ 
		list.GetNext(pos1); 
		CUpDownClient* cur_client =	list.GetAt(pos2); 
		list.RemoveAt(pos2); 
		delete cur_client; // recursiv: this will call RemoveClient 
	} 
} 
 
bool CClientList::AttachToAlreadyKnown(CUpDownClient** client, CClientReqSocket* sender){ 
	POSITION pos1, pos2; 
	CUpDownClient* tocheck = (*client); 
	CUpDownClient* found_client = NULL; 
	CUpDownClient* found_client2 = NULL; 
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){	// 
		list.GetNext(pos1); 
		CUpDownClient* cur_client =	list.GetAt(pos2); 
		if (tocheck->Compare(cur_client,false)){ //matching userhash 
			found_client2 = cur_client; 
		} 
		if (tocheck->Compare(cur_client,true)){	 //matching IP 
			found_client = cur_client; 
			break; 
		} 
	} 
	if (found_client == NULL) 
		found_client = found_client2; 
 
	if (found_client != NULL){ 
		if (tocheck == found_client){ 
			//we found the same client instance (client may have sent more than one OP_HELLO). do not delete that client! 
			return true; 
		} 
		if (sender){ 
			if (found_client->socket){ 
				if (found_client->socket->IsConnected()  
					&& (found_client->GetIP() != tocheck->GetIP() || found_client->GetUserPort() != tocheck->GetUserPort() ) ) 
				{ 
					// if found_client is connected and has the IS_IDENTIFIED, it's safe to say that the other one is a bad guy 
					if (found_client->Credits() && found_client->Credits()->GetCurrentIdentState(found_client->GetIP()) == IS_IDENTIFIED){ 
						if (thePrefs.GetLogBannedClients()) 
							AddDebugLogLine(false, "Clients: %s (%s), Banreason: Userhash invalid", tocheck->GetUserName(), ipstr(tocheck->GetConnectIP())); 
						tocheck->Ban(); 
						return false; 
					} 
	 
					//IDS_CLIENTCOL Warning: Found matching client, to a currently connected client: %s (%s) and %s (%s) 
					if (thePrefs.GetLogBannedClients()) 
						AddDebugLogLine(true,GetResString(IDS_CLIENTCOL), tocheck->GetUserName(), ipstr(tocheck->GetConnectIP()), found_client->GetUserName(), ipstr(found_client->GetConnectIP())); 
					return false; 
				} 
				found_client->socket->client = 0; 
				found_client->socket->Safe_Delete(); 
			} 
			found_client->socket = sender; 
			tocheck->socket = 0; 
		} 
		*client = 0; 
		delete tocheck; 
		*client = found_client; 
		return true; 
	} 
	return false; 
} 
 
CUpDownClient* CClientList::FindClientByIP(uint32 clientip, UINT port) const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client = list.GetNext(pos); 
		if (cur_client->GetIP() == clientip && cur_client->GetUserPort() == port) 
			return cur_client; 
	} 
	return 0; 
} 
 
CUpDownClient* CClientList::FindClientByUserHash(const uchar* clienthash) const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client = list.GetNext(pos); 
		if (!md4cmp(cur_client->GetUserHash() ,clienthash)) 
			return cur_client; 
	} 
	return 0; 
} 
 
CUpDownClient* CClientList::FindClientByIP(uint32 clientip) const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client = list.GetNext(pos); 
		if (cur_client->GetIP() == clientip) 
			return cur_client; 
	} 
	return 0; 
} 
 
CUpDownClient* CClientList::FindClientByIP_UDP(uint32 clientip, UINT nUDPport) const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client = list.GetNext(pos); 
		if (cur_client->GetIP() == clientip && cur_client->GetUDPPort() == nUDPport) 
			return cur_client; 
	} 
	return 0; 
} 
 
CUpDownClient* CClientList::FindClientByUserID_KadPort(uint32 clientID, uint16 kadPort) const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client = list.GetNext(pos); 
		if (cur_client->GetUserIDHybrid() == clientID && cur_client->GetKadPort() == kadPort) 
			return cur_client; 
	} 
	return 0; 
} 
 
CUpDownClient* CClientList::FindClientByIP_KadPort(uint32 ip, uint16 port) const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client = list.GetNext(pos); 
		if (cur_client->GetIP() == ip && cur_client->GetKadPort() == port) 
			return cur_client; 
	} 
	return 0; 
} 
 
//TODO: This needs to change to a random Kad user. 
CUpDownClient* CClientList::GetRandomKadClient() const 
{ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client =	list.GetNext(pos); 
		if (cur_client->GetKadPort()) 
			return cur_client; 
	} 
	return 0; 
} 
 
CUpDownClient* CClientList::FindClientByServerID(uint32 uServerIP, uint32 uED2KUserID) const 
{ 
	uint32 uHybridUserID = ntohl(uED2KUserID); 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;) 
	{ 
		CUpDownClient* cur_client =	list.GetNext(pos); 
		if (cur_client->GetServerIP() == uServerIP && cur_client->GetUserIDHybrid() == uHybridUserID) 
			return cur_client; 
	} 
	return 0; 
} 
 
void CClientList::AddBannedClient(uint32 dwIP){ 
	m_bannedList.SetAt(dwIP, ::GetTickCount()); 
} 
 
bool CClientList::IsBannedClient(uint32 dwIP){ 
	uint32 dwBantime = 0; 
	if (m_bannedList.Lookup(dwIP, dwBantime)){ 
		if (dwBantime + CLIENTBANTIME > ::GetTickCount() ) 
			return true; 
		else 
			RemoveBannedClient(dwIP); 
	} 
	return false;  
} 
 
void CClientList::RemoveBannedClient(uint32 dwIP){ 
	m_bannedList.RemoveKey(dwIP); 
} 
 
void CClientList::AddTrackClient(CUpDownClient* toadd){ 
	CDeletedClient* pResult = 0; 
	if (m_trackedClientsList.Lookup(toadd->GetIP(), pResult)){ 
		pResult->m_dwInserted = ::GetTickCount(); 
		for (int i = 0; i != pResult->m_ItemsList.GetCount(); i++){ 
			if (pResult->m_ItemsList[i].nPort == toadd->GetUserPort()){ 
				// already tracked, update 
				pResult->m_ItemsList[i].pHash = toadd->Credits(); 
				return; 
			} 
		} 
		PORTANDHASH porthash = { toadd->GetUserPort(), toadd->Credits()}; 
		pResult->m_ItemsList.Add(porthash); 
	} 
	else{ 
		m_trackedClientsList.SetAt(toadd->GetIP(), new CDeletedClient(toadd)); 
	} 
} 
 
// true = everything ok, hash didn't changed 
// false = hash changed 
bool CClientList::ComparePriorUserhash(uint32 dwIP, uint16 nPort, void* pNewHash){ 
	CDeletedClient* pResult = 0; 
	if (m_trackedClientsList.Lookup(dwIP, pResult)){ 
		for (int i = 0; i != pResult->m_ItemsList.GetCount(); i++){ 
			if (pResult->m_ItemsList[i].nPort == nPort){ 
				if (pResult->m_ItemsList[i].pHash != pNewHash) 
					return false; 
				else 
					break; 
			} 
		} 
	} 
	return true; 
} 
 
uint16 CClientList::GetClientsFromIP(uint32 dwIP){ 
	CDeletedClient* pResult = 0; 
	if (m_trackedClientsList.Lookup(dwIP, pResult)){ 
		return pResult->m_ItemsList.GetCount(); 
	} 
	return 0; 
} 
 
void CClientList::Process(){ 
	const uint32 cur_tick = ::GetTickCount(); 
	if (m_dwLastBannCleanUp + BAN_CLEANUP_TIME < cur_tick){ 
		m_dwLastBannCleanUp = cur_tick; 
		 
		POSITION pos = m_bannedList.GetStartPosition(); 
		uint32 nKey; 
		uint32 dwBantime; 
		while (pos != NULL){ 
			m_bannedList.GetNextAssoc( pos, nKey, dwBantime ); 
			if (dwBantime + CLIENTBANTIME < cur_tick ) 
				RemoveBannedClient(nKey); 
		} 
	} 
 
	 
	if (m_dwLastTrackedCleanUp + TRACKED_CLEANUP_TIME < cur_tick ){ 
		m_dwLastTrackedCleanUp = cur_tick; 
		if (thePrefs.GetLogBannedClients()) 
			AddDebugLogLine(false, "Cleaning up TrackedClientList, %i clients on List...", m_trackedClientsList.GetCount()); 
		POSITION pos = m_trackedClientsList.GetStartPosition(); 
		uint32 nKey; 
		CDeletedClient* pResult; 
		while (pos != NULL){ 
			m_trackedClientsList.GetNextAssoc( pos, nKey, pResult ); 
			if (pResult->m_dwInserted + KEEPTRACK_TIME < cur_tick ){ 
				m_trackedClientsList.RemoveKey(nKey); 
				delete pResult; 
			} 
		} 
		if (thePrefs.GetLogBannedClients()) 
			AddDebugLogLine(false, "...done, %i clients left on list", m_trackedClientsList.GetCount()); 
	} 
 
	//We need to try to connect to the clients in RequestTCPList 
	//If connected, remove them from the list and send a message back to Kad so we can send a ACK. 
	//If we don't connect, we need to remove the client.. 
	//The sockets timeout should delete this object. 
	POSITION pos1, pos2; 
	for (pos1 = RequestTCPList.GetHeadPosition();( pos2 = pos1 ) != NULL;) 
	{ 
		RequestTCPList.GetNext(pos1); 
		CUpDownClient* cur_client =	RequestTCPList.GetAt(pos2); 
		switch(cur_client->GetKadState()) 
		{ 
			case KS_QUEUED_FWCHECK: 
				//I removed the self check here because we already did it when we added the client object. 
				cur_client->TryToConnect(); 
				break; 
 
			case KS_CONNECTING_FWCHECK: 
				//Ignore this state as we are just waiting for results. 
				break; 
 
			case KS_CONNECTED_FWCHECK: 
				//Send the Kademlia client a TCP connection ack! 
				Kademlia::CKademlia::getUDPListener()->sendNullPacket(KADEMLIA_FIREWALLED_ACK, ntohl(cur_client->GetIP()), cur_client->GetKadPort()); 
				cur_client->SetKadState(KS_NONE); 
				break; 
			case KS_QUEUED_BUDDY: 
				//a client lowID client wanting to be in the Kad network 
				break; 
			case KS_CONNECTED_BUDDY: 
				//a client lowID client wanting to be in the Kad network 
				//Not working at the moment so just set to none.. 
				cur_client->SetKadState(KS_NONE); 
			default: 
				RemoveTCP(cur_client); 
		} 
	} 
} 
 
#ifdef _DEBUG 
void CClientList::Debug_SocketDeleted(CClientReqSocket* deleted){ 
	for (POSITION pos = list.GetHeadPosition(); pos != NULL;){ 
		CUpDownClient* cur_client =	list.GetNext(pos); 
		if (!AfxIsValidAddress(cur_client, sizeof(CUpDownClient))) { 
			AfxDebugBreak(); 
		} 
		if (thePrefs.m_iDbgHeap >= 2) 
			ASSERT_VALID(cur_client); 
		if (cur_client->socket == deleted){ 
			AfxDebugBreak(); 
		} 
	} 
} 
#endif 
 
bool CClientList::IsValidClient(CUpDownClient* tocheck) 
{ 
	if (thePrefs.m_iDbgHeap >= 2) 
		ASSERT_VALID(tocheck); 
	return list.Find(tocheck); 
} 
 
void CClientList::RequestTCP(Kademlia::CContact* contact) 
{ 
	uint32 nContactIP = ntohl(contact->getIPAddress()); 
	//If eMule already knows this client, abort this.. It could cause conflicts. 
	//Although the odds of this happening is very small, it could still happen. 
	if (FindClientByIP(nContactIP, contact->getTCPPort())) 
	{ 
		return; 
	} 
 
	// don't connect ourself 
	if (theApp.serverconnect->GetLocalIP() == nContactIP && thePrefs.GetPort() == contact->getTCPPort()) 
		return; 
 
	//Add client to the lists to be processed. 
	CUpDownClient* test = new CUpDownClient(0, contact->getTCPPort(), contact->getIPAddress(), 0, 0, false ); 
	test->SetKadPort(contact->getUDPPort()); 
	test->SetKadState(KS_QUEUED_FWCHECK); 
	RequestTCPList.AddTail(test); 
	AddClient(test); 
} 
 
void CClientList::RemoveTCP(CUpDownClient* torem){ 
	POSITION pos = RequestTCPList.Find(torem); 
	if(pos) 
	{ 
		RequestTCPList.RemoveAt(pos); 
	} 
} 
 
CDeletedClient::CDeletedClient(CUpDownClient* pClient) 
{ 
	m_dwInserted = ::GetTickCount(); 
	PORTANDHASH porthash = { pClient->GetUserPort(), pClient->Credits()}; 
	m_ItemsList.Add(porthash); 
}