//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" #ifdef _DEBUG #include "DebugHelpers.h" #endif #include "emule.h" #include "UpDownClient.h" #include "FriendList.h" #include "Clientlist.h" #include "OtherFunctions.h" #include "PartFile.h" #include "ListenSocket.h" #include "Friend.h" #include #include "Packets.h" #include "Opcodes.h" #include "SafeFile.h" #include "Preferences.h" #include "Server.h" #include "ClientCredits.h" #include "IPFilter.h" #include "UploadQueue.h" #include "Version.h" #include "Sockets.h" #include "DownloadQueue.h" #include "SearchList.h" #include "SharedFileList.h" #include "Kademlia/Kademlia/Kademlia.h" #include "Kademlia/Net/KademliaUDPListener.h" #ifndef _CONSOLE #include "emuledlg.h" #include "ServerWnd.h" #include "TransferWnd.h" #include "ChatWnd.h" #include "CxImage/xImage.h" #include "PreviewDlg.h" #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // members of CUpDownClient // which are used by down and uploading functions CUpDownClient::CUpDownClient(CClientReqSocket* sender) { socket = sender; reqfile = NULL; Init(); } CUpDownClient::CUpDownClient(CPartFile* in_reqfile, uint16 in_port, uint32 in_userid,uint32 in_serverip, uint16 in_serverport, bool ed2kID) { //Converting to the HybridID system.. The ED2K system didn't take into account of IP address ending in 0.. //All IP addresses ending in 0 were assumed to be a lowID because of the calculations. socket = NULL; reqfile = in_reqfile; Init(); m_nUserPort = in_port; //If this is a ED2K source, check if it's a lowID.. If not, convert it to a HyrbidID. //TODO: Find out how Servers deal with these IP addresses ending with .0 and see what is the lowest we can put here to catch some of them. if(ed2kID && !IsLowIDED2K(in_userid)) m_nUserIDHybrid = ntohl(in_userid); else m_nUserIDHybrid = in_userid; //If create the FullIP address depending on source type. if (!HasLowID() && ed2kID){ m_nConnectIP = in_userid; } else if(!HasLowID()){ in_userid = ntohl(in_userid); m_nConnectIP = in_userid; } m_dwServerIP = in_serverip; m_nServerPort = in_serverport; } void CUpDownClient::Init() { credits = 0; sumavgUDR = 0; // by BadWolf - Accurate Speed Measurement m_bAddNextConnect = false; // VQB Fix for LowID slots only on connection m_nChatstate = MS_NONE; m_nKadState = KS_NONE; m_cShowDR = 0; m_nUDPPort = 0; m_nKadPort = 0; m_nTransferedUp = 0; m_cAsked = 0; m_cDownAsked = 0; dataratems = 0; m_nUpDatarate = 0; m_pszUsername = 0; m_nUserIDHybrid = 0; m_nServerPort = 0; m_iFileListRequested = 0; m_dwLastUpRequest = 0; m_bEmuleProtocol = false; m_bCompleteSource = false; m_bFriendSlot = false; m_bCommentDirty = false; m_bReaskPending = false; m_bUDPPending = false; m_byEmuleVersion = 0; m_nUserPort = 0; m_nPartCount = 0; m_nUpPartCount = 0; m_abyPartStatus = 0; m_abyUpPartStatus = 0; m_dwLastAskedTime = 0; m_nDownloadState = DS_NONE; m_dwUploadTime = 0; m_nTransferedDown = 0; m_nDownDatarate = 0; m_nDownDataRateMS = 0; m_nUploadState = US_NONE; m_dwLastBlockReceived = 0; m_byDataCompVer = 0; m_byUDPVer = 0; m_bySourceExchangeVer = 0; m_byAcceptCommentVer = 0; m_byExtendedRequestsVer = 0; m_nRemoteQueueRank = 0; m_dwLastSourceRequest = 0; m_dwLastSourceAnswer = 0; m_dwLastAskedForSources = 0; m_byCompatibleClient = 0; m_nSourceFrom = SF_SERVER; m_bIsHybrid = false; m_bIsML=false; m_Friend = NULL; m_iRate=0; m_fMessageFiltered = 0; m_fIsSpammer = 0; m_cMessagesReceived = 0; m_cMessagesSent = 0; m_strComment=""; m_nCurSessionUp = 0; m_nSumForAvgDownDataRate = 0; m_clientSoft=SO_UNKNOWN; m_bRemoteQueueFull = false; md4clr(m_achUserHash); if (socket){ SOCKADDR_IN sockAddr = {0}; int nSockAddrLen = sizeof(sockAddr); socket->GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen); m_dwUserIP = sockAddr.sin_addr.S_un.S_addr; m_nConnectIP = m_dwUserIP; } else{ m_dwUserIP = 0; m_nConnectIP = 0; } m_fHashsetRequesting = 0; m_fSharedDirectories = 0; m_fSentCancelTransfer = 0; m_nClientVersion = 0; m_lastRefreshedDLDisplay = 0; m_dwDownStartTime = 0; m_nLastBlockOffset = 0; m_SecureIdentState = IS_UNAVAILABLE; m_dwLastSignatureIP = 0; m_bySupportSecIdent = 0; m_byInfopacketsReceived = IP_NONE; m_lastPartAsked = 0xffff; m_nUpCompleteSourcesCount= 0; m_fSupportsPreview = 0; m_fPreviewReqPending = 0; m_fPreviewAnsPending = 0; m_bTransferredDownMini = false; m_addedPayloadQueueSession = 0; m_nCurQueueSessionPayloadUp = 0; // PENDING: Is this necessary? ResetSessionUp()... m_lastRefreshedULDisplay = ::GetTickCount(); m_bGPLEvildoer = false; m_bHelloAnswerPending = false; m_fNoViewSharedFiles = 0; m_bMultiPacket = 0; md4clr(requpfileid); } CUpDownClient::~CUpDownClient(){ theApp.clientlist->RemoveClient(this); if (m_Friend){ m_Friend->m_LinkedClient = NULL; theApp.friendlist->RefreshFriend(m_Friend); m_Friend = NULL; } if (socket){ socket->client = 0; socket->Safe_Delete(); } delete[] m_pszUsername; delete[] m_abyPartStatus; delete[] m_abyUpPartStatus; ClearUploadBlockRequests(); for (POSITION pos = m_DownloadBlocks_list.GetHeadPosition();pos != 0;) delete m_DownloadBlocks_list.GetNext(pos); for (POSITION pos = m_RequestedFiles_list.GetHeadPosition();pos != 0;) delete m_RequestedFiles_list.GetNext(pos); for (POSITION pos = m_PendingBlocks_list.GetHeadPosition();pos != 0;){ Pending_Block_Struct *pending = m_PendingBlocks_list.GetNext(pos); delete pending->block; // Not always allocated if (pending->zStream){ inflateEnd(pending->zStream); delete pending->zStream; } delete pending; } for (POSITION pos = m_WaitingPackets_list.GetHeadPosition();pos != 0;) delete m_WaitingPackets_list.GetNext(pos); if (m_iRate>0 || !m_strComment.IsEmpty()){ m_iRate = 0; m_strComment.Empty(); reqfile->UpdateFileRatingCommentAvail(); } DEBUG_ONLY (theApp.listensocket->Debug_ClientDeleted(this)); SetUploadFileID(NULL); } void CUpDownClient::ClearHelloProperties() { m_nUDPPort = 0; m_byUDPVer = 0; m_byDataCompVer = 0; m_byEmuleVersion = 0; m_bySourceExchangeVer = 0; m_byAcceptCommentVer = 0; m_byExtendedRequestsVer = 0; m_byCompatibleClient = 0; m_nKadPort = 0; m_bySupportSecIdent = 0; m_fSupportsPreview = 0; m_nClientVersion = 0; m_fSharedDirectories = 0; m_bMultiPacket = 0; } bool CUpDownClient::ProcessHelloPacket(char* pachPacket, uint32 nSize){ CSafeMemFile data((BYTE*)pachPacket,nSize); data.ReadUInt8(); // read size of userhash // reset all client properties; a client may not send a particular emule tag any longer ClearHelloProperties(); return ProcessHelloTypePacket(&data); } bool CUpDownClient::ProcessHelloAnswer(char* pachPacket, uint32 nSize) { CSafeMemFile data((BYTE*)pachPacket,nSize); bool bIsMule = ProcessHelloTypePacket(&data); m_bHelloAnswerPending = false; return bIsMule; } bool CUpDownClient::ProcessHelloTypePacket(CSafeMemFile* data) { bool bDbgInfo = thePrefs.GetDebugClientTCPLevel() > 0; m_strHelloInfo.Empty(); // clear hello properties which can be changed _only_ on receiving OP_Hello/OP_HelloAnswer m_bIsHybrid = false; m_bIsML = false; m_fNoViewSharedFiles = 0; data->ReadHash16(m_achUserHash); if (bDbgInfo) m_strHelloInfo.AppendFormat("Hash=%s (%s)", md4str(m_achUserHash), DbgGetHashTypeString(m_achUserHash)); m_nUserIDHybrid = data->ReadUInt32(); if (bDbgInfo) m_strHelloInfo.AppendFormat(" UserID=%u (%s)", m_nUserIDHybrid, ipstr(m_nUserIDHybrid)); uint16 nUserPort = data->ReadUInt16(); // hmm clientport is sent twice - why? if (bDbgInfo) m_strHelloInfo.AppendFormat(" Port=%u", nUserPort); DWORD dwEmuleTags = 0; uint32 tagcount = data->ReadUInt32(); if (bDbgInfo) m_strHelloInfo.AppendFormat(" Tags=%u", tagcount); for (uint32 i = 0;i < tagcount; i++){ CTag temptag(data); switch(temptag.tag.specialtag){ case CT_NAME: if (m_pszUsername){ delete[] m_pszUsername; m_pszUsername = NULL; // needed, in case 'nstrdup' fires an exception!! } if( temptag.tag.stringvalue ) m_pszUsername = nstrdup(temptag.tag.stringvalue); if (bDbgInfo){ if (m_pszUsername){//filter username for bad chars char* psz = m_pszUsername; while (*psz != '\0') { if (*psz == '\n' || *psz == '\r') *psz = ' '; psz++; } } m_strHelloInfo.AppendFormat(" NAME='%s'", m_pszUsername); } break; case CT_VERSION: if (bDbgInfo) m_strHelloInfo.AppendFormat(" VERSION=%u", temptag.tag.intvalue); m_nClientVersion = temptag.tag.intvalue; break; case CT_PORT: if (bDbgInfo) m_strHelloInfo.AppendFormat(" PORT=%u", temptag.tag.intvalue); nUserPort = temptag.tag.intvalue; break; case ET_MOD_VERSION: if (temptag.tag.type == 2) m_strModVersion = temptag.tag.stringvalue; else if (temptag.tag.type == 3) m_strModVersion.Format(_T("ModID=%u"), temptag.tag.intvalue); else m_strModVersion = _T("ModID="); if (bDbgInfo) m_strHelloInfo.AppendFormat(" Mod=%s", m_strModVersion); CheckForGPLEvilDoer(); break; case CT_EMULE_UDPPORTS: // 16 KAD Port // 16 UDP Port m_nKadPort = (uint16)(temptag.tag.intvalue >> 16); m_nUDPPort = (uint16)temptag.tag.intvalue; if (bDbgInfo) m_strHelloInfo.AppendFormat(" KadPort=%u UDPPort=%u", m_nKadPort, m_nUDPPort); dwEmuleTags |= 1; break; case CT_EMULE_MISCOPTIONS1: // 4 --Reserved for future use-- // 4 UDP version // 4 Data compression version // 4 Secure Ident // 4 Source Exchange // 4 Ext. Requests // 4 Comments // 1 --Reserved for future use-- // 1 No 'View Shared Files' supported // 1 MultiPacket // 1 Preview m_byUDPVer = (temptag.tag.intvalue >> 4*6) & 0x0f; m_byDataCompVer = (temptag.tag.intvalue >> 4*5) & 0x0f; m_bySupportSecIdent = (temptag.tag.intvalue >> 4*4) & 0x0f; m_bySourceExchangeVer = (temptag.tag.intvalue >> 4*3) & 0x0f; m_byExtendedRequestsVer = (temptag.tag.intvalue >> 4*2) & 0x0f; m_byAcceptCommentVer = (temptag.tag.intvalue >> 4*1) & 0x0f; m_fNoViewSharedFiles = (temptag.tag.intvalue >> 1*2) & 0x01; m_bMultiPacket = (temptag.tag.intvalue >> 1*1) & 0x01; m_fSupportsPreview = (temptag.tag.intvalue >> 1*0) & 0x01; dwEmuleTags |= 2; if (bDbgInfo) m_strHelloInfo.AppendFormat(" UDPVer=%u DataComp=%u SecIdent=%u SrcExchg=%u ExtReq=%u Commnt=%u Preview=%u NoViewFiles=%u", m_byUDPVer, m_byDataCompVer, m_bySupportSecIdent, m_bySourceExchangeVer, m_byExtendedRequestsVer, m_byAcceptCommentVer, m_fSupportsPreview, m_fNoViewSharedFiles); break; case CT_EMULE_VERSION: // 8 Compatible Client ID // 7 Mjr Version (Doesn't really matter..) // 7 Min Version (Only need 0-99) // 3 Upd Version (Only need 0-5) // 7 Bld Version (Only need 0-99) -- currently not used m_byCompatibleClient = (temptag.tag.intvalue >> 24); m_nClientVersion = temptag.tag.intvalue & 0x00ffffff; m_byEmuleVersion = 0x99; m_fSharedDirectories = 1; dwEmuleTags |= 4; if (bDbgInfo) m_strHelloInfo.AppendFormat(" Comptbl=%u ClientVer=%u.%u.%u.%u", m_byCompatibleClient, (m_nClientVersion >> 17) & 0x7f, (m_nClientVersion >> 10) & 0x7f, (m_nClientVersion >> 7) & 0x07, m_nClientVersion & 0x7f); break; default: if (bDbgInfo) m_strHelloInfo.AppendFormat(" ***UnkTag: %s", temptag.GetFullInfo()); } } m_nUserPort = nUserPort; m_dwServerIP = data->ReadUInt32(); m_nServerPort = data->ReadUInt16(); if (bDbgInfo) m_strHelloInfo.AppendFormat(" Server=%s:%u", inet_ntoa(*(in_addr*)&m_dwServerIP), m_nServerPort); // Hybrid now has an extra uint32.. What is it for? // Also, many clients seem to send an extra 6? These are not eDonkeys or Hybrids.. if ( data->GetLength() - data->GetPosition() == sizeof(uint32) ){ uint32 test = data->ReadUInt32(); if (test == 'KDLM') { m_bIsML = true; if (bDbgInfo) m_strHelloInfo += " ***AddData: \"MLDK\""; } else{ m_bIsHybrid = true; m_fSharedDirectories = 1; if (bDbgInfo) m_strHelloInfo.AppendFormat(" ***AddData: uint32=%u (0x%08x)", test, test); } } else if (bDbgInfo && data->GetPosition() < data->GetLength()){ UINT uAddHelloDataSize = (UINT)(data->GetLength() - data->GetPosition()); if (uAddHelloDataSize == sizeof(uint32)){ DWORD dwAddHelloInt32 = data->ReadUInt32(); m_strHelloInfo.AppendFormat(" ***AddData: uint32=%u (0x%08x)", dwAddHelloInt32, dwAddHelloInt32); } else if (uAddHelloDataSize == sizeof(uint32)+sizeof(uint16)){ DWORD dwAddHelloInt32 = data->ReadUInt32(); WORD w = data->ReadUInt16(); m_strHelloInfo.AppendFormat(" ***AddData: uint32=%u (0x%08x), uint16=%u (0x%04x)", dwAddHelloInt32, dwAddHelloInt32, w, w); } else m_strHelloInfo.AppendFormat(" ***AddData: %u bytes", uAddHelloDataSize); } SOCKADDR_IN sockAddr = {0}; int nSockAddrLen = sizeof(sockAddr); socket->GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen); m_dwUserIP = sockAddr.sin_addr.S_un.S_addr; m_nConnectIP = m_dwUserIP; if (thePrefs.AddServersFromClient() && m_dwServerIP && m_nServerPort){ in_addr addhost; addhost.S_un.S_addr = m_dwServerIP; CServer* addsrv = new CServer(m_nServerPort, inet_ntoa(addhost)); addsrv->SetListName(addsrv->GetAddress()); if (!theApp.emuledlg->serverwnd->serverlistctrl.AddServer(addsrv, true)) delete addsrv; } //Because most sources are from ED2K, I removed the m_nUserIDHybrid != m_dwUserIP check since this will trigger 90% of the time anyway. if(!HasLowID() || m_nUserIDHybrid == 0) m_nUserIDHybrid = ntohl(m_dwUserIP); CClientCredits* pFoundCredits = theApp.clientcredits->GetCredit(m_achUserHash); if (credits == NULL){ credits = pFoundCredits; if (!theApp.clientlist->ComparePriorUserhash(m_dwUserIP, m_nUserPort, pFoundCredits)){ if (thePrefs.GetLogBannedClients()) AddDebugLogLine(false, "Clients: %s (%s), Banreason: Userhash changed (Found in TrackedClientsList)", GetUserName(), ipstr(GetConnectIP())); Ban(); } } else if (credits != pFoundCredits){ // userhash change ok, however two hours "waittime" before it can be used credits = pFoundCredits; if (thePrefs.GetLogBannedClients()) AddDebugLogLine(false, "Clients: %s (%s), Banreason: Userhash changed", GetUserName(), ipstr(GetConnectIP())); Ban(); } if ((m_Friend = theApp.friendlist->SearchFriend(m_achUserHash, m_dwUserIP, m_nUserPort)) != NULL){ // Link the friend to that client if (m_Friend->m_LinkedClient){ if (m_Friend->m_LinkedClient != this){ bool bFriendSlot = m_Friend->m_LinkedClient->GetFriendSlot(); // avoid that an unwanted client instance keeps a friend slot m_Friend->m_LinkedClient->SetFriendSlot(false); m_Friend->m_LinkedClient->m_Friend = NULL; m_Friend->m_LinkedClient = this; // move an assigned friend slot between different client instances which are/were also friends m_Friend->m_LinkedClient->SetFriendSlot(bFriendSlot); } } else m_Friend->m_LinkedClient = this; md4cpy(m_Friend->m_abyUserhash,GetUserHash()); m_Friend->m_dwHasHash = md4cmp(m_Friend->m_abyUserhash, CFriend::sm_abyNullHash) ? 1 : 0; m_Friend->m_strName = m_pszUsername; m_Friend->m_dwLastUsedIP = m_dwUserIP; m_Friend->m_nLastUsedPort = m_nUserPort; m_Friend->m_dwLastSeen = time(NULL); theApp.friendlist->RefreshFriend(m_Friend); } else{ // avoid that an unwanted client instance keeps a friend slot SetFriendSlot(false); } // check for known major gpl breaker CString strBuffer = m_pszUsername; strBuffer.MakeUpper(); strBuffer.Remove(' '); if (strBuffer.Find("EMULE-CLIENT") != -1 || strBuffer.Find("POWERMULE") != -1 ){ m_bGPLEvildoer = true; } ReGetClientSoft(); m_byInfopacketsReceived |= IP_EDONKEYPROTPACK; // check if at least CT_EMULEVERSION was received, all other tags are optional bool bIsMule = (dwEmuleTags & 0x04) == 0x04; if (bIsMule){ m_bEmuleProtocol = true; m_byInfopacketsReceived |= IP_EMULEPROTPACK; } if (thePrefs.GetVerbose() && GetServerIP() == INADDR_NONE) AddDebugLogLine(false, _T("Received invalid server IP %s from %s"), ipstr(GetServerIP()), DbgGetClientInfo()); if( GetKadPort() && Kademlia::CKademlia::isRunning() ) { Kademlia::CKademlia::getUDPListener()->bootstrap(ntohl(GetIP()), GetKadPort()); } return bIsMule; } // returns 'false', if client instance was deleted! bool CUpDownClient::SendHelloPacket(){ if (socket == NULL){ ASSERT(0); return true; } // if IP is filtered, dont greet him but disconnect... SOCKADDR_IN sockAddr = {0}; int nSockAddrLen = sizeof(sockAddr); socket->GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen); if ( theApp.ipfilter->IsFiltered(sockAddr.sin_addr.S_un.S_addr)) { theApp.stat_filteredclients++; if (thePrefs.GetLogFilteredIPs()) AddDebugLogLine(true,GetResString(IDS_IPFILTERED),inet_ntoa(sockAddr.sin_addr),theApp.ipfilter->GetLastHit()); if(Disconnected("IPFilter")){ delete this; return false; } return true; } CSafeMemFile data(128); data.WriteUInt8(16); // size of userhash SendHelloTypePacket(&data); Packet* packet = new Packet(&data); packet->opcode = OP_HELLO; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__Hello", this); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); socket->SendPacket(packet,true); m_bHelloAnswerPending = true; return true; } void CUpDownClient::SendMuleInfoPacket(bool bAnswer){ if (socket == NULL){ ASSERT(0); return; } CSafeMemFile data(128); data.WriteUInt8(theApp.m_uCurVersionShort); data.WriteUInt8(EMULE_PROTOCOL); data.WriteUInt32(7); // nr. of tags CTag tag(ET_COMPRESSION,1); tag.WriteTagToFile(&data); CTag tag2(ET_UDPVER,4); tag2.WriteTagToFile(&data); CTag tag3(ET_UDPPORT,thePrefs.GetUDPPort()); tag3.WriteTagToFile(&data); CTag tag4(ET_SOURCEEXCHANGE,3); tag4.WriteTagToFile(&data); CTag tag5(ET_COMMENTS,1); tag5.WriteTagToFile(&data); CTag tag6(ET_EXTENDEDREQUEST,2); tag6.WriteTagToFile(&data); uint32 dwTagValue = (theApp.clientcredits->CryptoAvailable() ? 3 : 0); if (thePrefs.CanSeeShares() != vsfaNobody) // set 'Preview supported' only if 'View Shared Files' allowed dwTagValue |= 128; CTag tag7(ET_FEATURES, dwTagValue); tag7.WriteTagToFile(&data); Packet* packet = new Packet(&data,OP_EMULEPROT); if (!bAnswer) packet->opcode = OP_EMULEINFO; else packet->opcode = OP_EMULEINFOANSWER; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend(!bAnswer ? "OP__EmuleInfo" : "OP__EmuleInfoAnswer", this); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); socket->SendPacket(packet,true,true); } void CUpDownClient::ProcessMuleInfoPacket(char* pachPacket, uint32 nSize) { bool bDbgInfo = thePrefs.GetDebugClientTCPLevel() > 0; m_strMuleInfo.Empty(); CSafeMemFile data((BYTE*)pachPacket,nSize); m_byCompatibleClient = 0; //The version number part of this packet will soon be useless since it is only able to go to v.99. //Why the version is a uint8 and why it was not done as a tag like the eDonkey hello packet is not known.. //Therefore, sooner or later, we are going to have to switch over to using the eDonkey hello packet to set the version. //No sense making a third value sent for versions.. m_byEmuleVersion = data.ReadUInt8(); if (bDbgInfo) m_strMuleInfo.AppendFormat("EmuleVer=0x%x", (UINT)m_byEmuleVersion); if( m_byEmuleVersion == 0x2B ) m_byEmuleVersion = 0x22; uint8 protversion = data.ReadUInt8(); if (bDbgInfo) m_strMuleInfo.AppendFormat(" ProtVer=%u", (UINT)protversion); //implicitly supported options by older clients if (protversion == EMULE_PROTOCOL) { //in the future do not use version to guess about new features if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x22) m_byUDPVer = 1; if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x21) m_bySourceExchangeVer = 1; if(m_byEmuleVersion == 0x24) m_byAcceptCommentVer = 1; // Shared directories are requested from eMule 0.28+ because eMule 0.27 has a bug in // the OP_ASKSHAREDFILESDIR handler, which does not return the shared files for a // directory which has a trailing backslash. if(m_byEmuleVersion >= 0x28 && !m_bIsML) // MLdonkey currently does not support shared directories m_fSharedDirectories = 1; } else { return; } m_bEmuleProtocol = true; uint32 tagcount = data.ReadUInt32(); if (bDbgInfo) m_strMuleInfo.AppendFormat(" Tags=%u", (UINT)tagcount); for (uint32 i = 0;i < tagcount; i++){ CTag temptag(&data); switch(temptag.tag.specialtag){ case ET_COMPRESSION: // Bits 31- 8: 0 - reserved // Bits 7- 0: data compression version m_byDataCompVer = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" Compr=%u", (UINT)temptag.tag.intvalue); break; case ET_UDPPORT: // Bits 31-16: 0 - reserved // Bits 15- 0: UDP port m_nUDPPort = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" UDPPort=%u", (UINT)temptag.tag.intvalue); break; case ET_UDPVER: // Bits 31- 8: 0 - reserved // Bits 7- 0: UDP protocol version m_byUDPVer = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" UDPVer=%u", (UINT)temptag.tag.intvalue); break; case ET_SOURCEEXCHANGE: // Bits 31- 8: 0 - reserved // Bits 7- 0: source exchange protocol version m_bySourceExchangeVer = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" SrcExch=%u", (UINT)temptag.tag.intvalue); break; case ET_COMMENTS: // Bits 31- 8: 0 - reserved // Bits 7- 0: comments version m_byAcceptCommentVer = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" Commnts=%u", (UINT)temptag.tag.intvalue); break; case ET_EXTENDEDREQUEST: // Bits 31- 8: 0 - reserved // Bits 7- 0: extended requests version m_byExtendedRequestsVer = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" ExtReq=%u", (UINT)temptag.tag.intvalue); break; case ET_COMPATIBLECLIENT: // Bits 31- 8: 0 - reserved // Bits 7- 0: compatible client ID m_byCompatibleClient = temptag.tag.intvalue; if (bDbgInfo) m_strMuleInfo.AppendFormat(" Comptbl=%u", (UINT)temptag.tag.intvalue); break; case ET_FEATURES: // Bits 31- 8: 0 - reserved // Bit 7: Preview // Bit 6- 0: secure identification m_bySupportSecIdent = (temptag.tag.intvalue ) & 3; m_fSupportsPreview = (temptag.tag.intvalue >> 7) & 1; if (bDbgInfo) m_strMuleInfo.AppendFormat(" SecIdent=%u Preview=%u", m_bySupportSecIdent, m_fSupportsPreview); break; case ET_MOD_VERSION: if (temptag.tag.type == 2) m_strModVersion = temptag.tag.stringvalue; else if (temptag.tag.type == 3) m_strModVersion.Format(_T("ModID=%u"), temptag.tag.intvalue); else m_strModVersion = _T("ModID="); if (bDbgInfo) m_strMuleInfo.AppendFormat(" Mod=%s", m_strModVersion); CheckForGPLEvilDoer(); break; default: if (bDbgInfo) m_strMuleInfo.AppendFormat(" ***EmuleUnkTag: 0x%02x=%u", temptag.tag.specialtag, (UINT)temptag.tag.intvalue); } } if( m_byDataCompVer == 0 ){ m_bySourceExchangeVer = 0; m_byExtendedRequestsVer = 0; m_byAcceptCommentVer = 0; m_nUDPPort = 0; } if (bDbgInfo && data.GetPosition() < data.GetLength()){ m_strMuleInfo.AppendFormat(" ***AddData: %u bytes", data.GetLength() - data.GetPosition()); } ReGetClientSoft(); m_byInfopacketsReceived |= IP_EMULEPROTPACK; if (thePrefs.GetVerbose() && GetServerIP() == INADDR_NONE) AddDebugLogLine(false, _T("Received invalid server IP %s from %s"), ipstr(GetServerIP()), DbgGetClientInfo()); } void CUpDownClient::SendHelloAnswer(){ if (socket == NULL){ ASSERT(0); return; } CSafeMemFile data(128); SendHelloTypePacket(&data); Packet* packet = new Packet(&data); packet->opcode = OP_HELLOANSWER; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__HelloAnswer", this); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); socket->SendPacket(packet,true); } void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data) { data->WriteHash16(thePrefs.GetUserHash()); uint32 clientid; // if(theApp.IsFirewalled()) // { //If firewalled, send the actual ID.. clientid = theApp.GetID(); // } // else // { //If NOT firewalled, we must send the ID in the Hybrid format to avoid the ED2K bug //of marking all IP ending with .0 as a LowID!! // clientid = ntohl(theApp.GetID()); // } data->WriteUInt32(clientid); data->WriteUInt16(thePrefs.GetPort()); uint32 tagcount = 5; data->WriteUInt32(tagcount); // eD2K Name // TODO implement multi language website which informs users of the effects of bad mods CTag tagName(CT_NAME, (!m_bGPLEvildoer)?thePrefs.GetUserNick():"Please use a GPL-conform version of eMule" ); tagName.WriteTagToFile(data); // eD2K Version CTag tagVersion(CT_VERSION,EDONKEYVERSION); tagVersion.WriteTagToFile(data); // eMule UDP Ports uint32 kadUDPPort = 0; if(Kademlia::CKademlia::isConnected()) { kadUDPPort = thePrefs.GetUDPPort(); } CTag tagUdpPorts(CT_EMULE_UDPPORTS, (kadUDPPort << 16) | ((uint32)thePrefs.GetUDPPort() ) ); tagUdpPorts.WriteTagToFile(data); // eMule Misc. Options #1 const UINT uUdpVer = 4; const UINT uDataCompVer = 1; const UINT uSupportSecIdent = theApp.clientcredits->CryptoAvailable() ? 3 : 0; const UINT uSourceExchangeVer = 3; const UINT uExtendedRequestsVer = 2; const UINT uAcceptCommentVer = 1; const UINT uNoViewSharedFiles = (thePrefs.CanSeeShares() == vsfaNobody) ? 1 : 0; // for backward compatibility this has to be a 'negative' flag const UINT uMultiPacket = 1; const UINT uSupportPreview = (thePrefs.CanSeeShares() != vsfaNobody) ? 1 : 0; // set 'Preview supported' only if 'View Shared Files' allowed CTag tagMisOptions(CT_EMULE_MISCOPTIONS1, // ( << 4*7) | (uUdpVer << 4*6) | (uDataCompVer << 4*5) | (uSupportSecIdent << 4*4) | (uSourceExchangeVer << 4*3) | (uExtendedRequestsVer << 4*2) | (uAcceptCommentVer << 4*1) | // ( << 1*3) | (uNoViewSharedFiles << 1*2) | (uMultiPacket << 1*1) | (uSupportPreview << 1*0) ); tagMisOptions.WriteTagToFile(data); // eMule Version CTag tagMuleVersion(CT_EMULE_VERSION, //(uCompatibleClientID << 24) | (VERSION_MJR << 17) | (VERSION_MIN << 10) | (VERSION_UPDATE << 7) //| // (RESERVED ) ); tagMuleVersion.WriteTagToFile(data); uint32 dwIP; uint16 nPort; if (theApp.serverconnect->IsConnected()){ dwIP = theApp.serverconnect->GetCurrentServer()->GetIP(); nPort = theApp.serverconnect->GetCurrentServer()->GetPort(); } else{ nPort = 0; dwIP = 0; } data->WriteUInt32(dwIP); data->WriteUInt16(nPort); // data->WriteUInt32(dwIP); //The Hybrid added some bits here, what ARE THEY FOR? } void CUpDownClient::ProcessMuleCommentPacket(char* pachPacket, uint32 nSize){ if( reqfile ){ if( reqfile->IsPartFile()){ int length; if (nSize>(sizeof(m_iRate)+sizeof(length)-1)){ CSafeMemFile data((BYTE*)pachPacket,nSize); m_iRate = data.ReadUInt8(); length = data.ReadUInt32(); reqfile->SetHasRating(true); if (thePrefs.GetLogRatingDescReceived()) AddDebugLogLine(false,GetResString(IDS_RATINGRECV),m_strClientFilename,m_iRate); if ( length > data.GetLength() - data.GetPosition() ){ length = data.GetLength() - data.GetPosition(); } if (length>50) length=50; if (length>0){ data.Read(m_strComment.GetBuffer(length),length); m_strComment.ReleaseBuffer(length); if (thePrefs.GetLogRatingDescReceived()) AddDebugLogLine(false,GetResString(IDS_DESCRIPTIONRECV), m_strClientFilename, m_strComment); reqfile->SetHasComment(true); // test if comment is filtered if (thePrefs.GetCommentFilter().GetLength()>0) { CString resToken; CString strlink=thePrefs.GetCommentFilter(); strlink.MakeLower(); int curPos=0; resToken= strlink.Tokenize("|",curPos); while (resToken != "") { if (m_strComment.MakeLower().Find(resToken)>-1) { m_strComment=""; m_iRate=0; reqfile->SetHasRating(false); reqfile->SetHasComment(false); break; } resToken= strlink.Tokenize("|",curPos); } } } } if (reqfile->HasRating() || reqfile->HasComment()) theApp.emuledlg->transferwnd->downloadlistctrl.UpdateItem(reqfile); } } } bool CUpDownClient::Disconnected(CString strReason, bool bFromSocket){ //If this is a KAD client object, just delete it! ASSERT(theApp.clientlist->IsValidClient(this)); SetKadState(KS_NONE); if (GetUploadState() == US_UPLOADING) { theApp.uploadqueue->RemoveFromUploadQueue(this); } if (GetDownloadState() == DS_DOWNLOADING){ SetDownloadState(DS_ONQUEUE); } else{ // ensure that all possible block requests are removed from the partfile ClearDownloadBlockRequests(); if(GetDownloadState() == DS_CONNECTED){ // client didn't responsed to our request for some reasons (remotely banned?) // or it just doesn't has this file, so try to swap first if (!SwapToAnotherFile(true, true, true, NULL)){ theApp.downloadqueue->RemoveSource(this); //DEBUG_ONLY(AddDebugLogLine(false, "Removed %s from downloadqueue - didn't responsed to filerequests",GetUserName())); } } } // The remote client does not have to answer with OP_HASHSETANSWER *immediatly* // after we've sent OP_HASHSETREQUEST. It may occure that a (buggy) remote client // is sending use another OP_FILESTATUS which would let us change to DL-state to DS_ONQUEUE. if (((GetDownloadState() == DS_REQHASHSET) || m_fHashsetRequesting) && (reqfile)) reqfile->hashsetneeded = true; ASSERT(theApp.clientlist->IsValidClient(this)); //check if this client is needed in any way, if not delete it bool bDelete = true; switch(m_nUploadState){ case US_ONUPLOADQUEUE: bDelete = false; break; }; switch(m_nDownloadState){ case DS_ONQUEUE: case DS_TOOMANYCONNS: case DS_NONEEDEDPARTS: case DS_LOWTOLOWIP: bDelete = false; }; switch(m_nUploadState){ case US_CONNECTING: case US_WAITCALLBACK: case US_ERROR: bDelete = true; }; switch(m_nDownloadState){ case DS_CONNECTING: case DS_WAITCALLBACK: case DS_ERROR: bDelete = true; }; if (GetChatState() != MS_NONE){ bDelete = false; theApp.emuledlg->chatwnd->chatselector.ConnectingResult(this,false); } if (!bFromSocket && socket){ ASSERT (theApp.listensocket->IsValidSocket(socket)); socket->Safe_Delete(); } socket = 0; if (m_iFileListRequested){ AddLogLine(true,GetResString(IDS_SHAREDFILES_FAILED),GetUserName()); m_iFileListRequested = 0; } if (m_Friend) theApp.friendlist->RefreshFriend(m_Friend); theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(this); if (bDelete){ if (thePrefs.GetDebugClientTCPLevel() > 0) Debug("--- Deleted client %s; Reason=%s\n", DbgGetClientInfo(true), strReason); return true; } else{ if (thePrefs.GetDebugClientTCPLevel() > 0) Debug("--- Disconnected client %s; Reason=%s\n", DbgGetClientInfo(true), strReason); m_fHashsetRequesting = 0; SetSentCancelTransfer(0); m_bHelloAnswerPending = false; return false; } } //Returned bool is not if the TryToConnect is successful or not.. //false means the client was deleted! //true means the client was not deleted! bool CUpDownClient::TryToConnect(bool bIgnoreMaxCon) { if (theApp.listensocket->TooManySockets() && !bIgnoreMaxCon && !(socket && socket->IsConnected())) { if(Disconnected("Too many connections")) { delete this; return false; } return true; } if( GetKadState() == KS_QUEUED_FWCHECK ) SetKadState(KS_CONNECTING_FWCHECK); if ( HasLowID() && !theApp.DoCallback(this)) { if (GetDownloadState() == DS_CONNECTING) SetDownloadState(DS_LOWTOLOWIP); else if (GetDownloadState() == DS_REQHASHSET) { SetDownloadState(DS_ONQUEUE); reqfile->hashsetneeded = true; } if (GetUploadState() == US_CONNECTING) { if(Disconnected("LowID->LowID and US_CONNECTING")) { delete this; return false; } } return true; } if (!socket) { socket = new CClientReqSocket(this); if (!socket->Create()) { socket->Safe_Delete(); return true; } } else if (!socket->IsConnected()) { socket->Safe_Delete(); socket = new CClientReqSocket(this); if (!socket->Create()) { socket->Safe_Delete(); return true; } } else { ConnectionEstablished(); return true; } // MOD Note: Do not change this part - Merkur if (HasLowID()) { if (GetDownloadState() == DS_CONNECTING) SetDownloadState(DS_WAITCALLBACK); if (GetUploadState() == US_CONNECTING) { if(Disconnected("LowID and US_CONNECTING")) { delete this; return false; } return true; } if (theApp.serverconnect->IsLocalServer(m_dwServerIP,m_nServerPort)) { Packet* packet = new Packet(OP_CALLBACKREQUEST,4); PokeUInt32(packet->pBuffer, m_nUserIDHybrid); if (thePrefs.GetDebugServerTCPLevel() > 0) DebugSend("OP__CallBackRequest", this); theApp.uploadqueue->AddUpDataOverheadServer(packet->size); theApp.serverconnect->SendPacket(packet); } else { if (GetUploadState() == US_NONE && (!GetRemoteQueueRank() || m_bReaskPending) ) { theApp.downloadqueue->RemoveSource(this); if(Disconnected("LowID and US_NONE and QR=0")) { delete this; return false; } return true; } else { if (GetDownloadState() == DS_WAITCALLBACK) { m_bReaskPending = true; SetDownloadState(DS_ONQUEUE); } } } } // MOD Note - end else { SOCKADDR_IN sockAddr = {0}; sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(GetUserPort()); sockAddr.sin_addr.S_un.S_addr = GetConnectIP(); socket->Connect((SOCKADDR*)&sockAddr, sizeof sockAddr); if (!SendHelloPacket()) return false; // client was deleted! } return true; } void CUpDownClient::ConnectionEstablished() { // ok we have a connection, lets see if we want anything from this client switch(GetKadState()) { case KS_CONNECTING_FWCHECK: SetKadState(KS_CONNECTED_FWCHECK); break; case KS_QUEUED_BUDDY: SetKadState(KS_CONNECTED_BUDDY); break; } if (GetChatState() == MS_CONNECTING || GetChatState() == MS_CHATTING) theApp.emuledlg->chatwnd->chatselector.ConnectingResult(this,true); switch(GetDownloadState()) { case DS_CONNECTING: case DS_WAITCALLBACK: m_bReaskPending = false; SetDownloadState(DS_CONNECTED); SendFileRequest(); } if (m_bReaskPending) { m_bReaskPending = false; if (GetDownloadState() != DS_NONE && GetDownloadState() != DS_DOWNLOADING) { SetDownloadState(DS_CONNECTED); SendFileRequest(); } } switch(GetUploadState()) { case US_CONNECTING: case US_WAITCALLBACK: if (theApp.uploadqueue->IsDownloading(this)) { SetUploadState(US_UPLOADING); if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__AcceptUploadReq", this); Packet* packet = new Packet(OP_ACCEPTUPLOADREQ,0); theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size); socket->SendPacket(packet,true); } } if (m_iFileListRequested == 1) { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend(m_fSharedDirectories ? "OP__AskSharedDirs" : "OP__AskSharedFiles", this); Packet* packet = new Packet(m_fSharedDirectories ? OP_ASKSHAREDDIRS : OP_ASKSHAREDFILES,0); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); socket->SendPacket(packet,true,true); } while (!m_WaitingPackets_list.IsEmpty()) { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("Buffered Packet", this); socket->SendPacket(m_WaitingPackets_list.RemoveHead()); } } void CUpDownClient::ReGetClientSoft() { if (m_pszUsername == NULL){ m_clientSoft = SO_UNKNOWN; return; } int iHashType = GetHashType(); if (iHashType == SO_EMULE){ LPCTSTR pszSoftware; switch(m_byCompatibleClient){ case SO_CDONKEY: m_clientSoft = SO_CDONKEY; pszSoftware = _T("cDonkey"); break; case SO_XMULE: m_clientSoft = SO_XMULE; pszSoftware = _T("xMule"); break; case 3: // aMule m_clientSoft = SO_XMULE; pszSoftware = _T("aMule"); break; case SO_SHAREAZA: m_clientSoft = SO_SHAREAZA; pszSoftware = _T("Shareaza"); break; default: if (m_bIsML || m_byCompatibleClient == SO_MLDONKEY){ m_clientSoft = SO_MLDONKEY; pszSoftware = _T("MLdonkey"); } else if (m_bIsHybrid){ m_clientSoft = SO_EDONKEYHYBRID; pszSoftware = _T("eDonkeyHybrid"); } else if (m_byCompatibleClient != 0){ m_clientSoft = SO_XMULE; pszSoftware = _T("eMule Compat"); } else{ m_clientSoft = SO_EMULE; pszSoftware = _T("eMule"); } } int iLen; TCHAR szSoftware[128]; if (m_byEmuleVersion == 0){ m_nClientVersion = MAKE_CLIENT_VERSION(0, 0, 0); iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("%s"), pszSoftware); } else if (m_byEmuleVersion != 0x99){ UINT nClientMinVersion = (m_byEmuleVersion >> 4)*10 + (m_byEmuleVersion & 0x0f); m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0); iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("%s v0.%u"), pszSoftware, nClientMinVersion); } else{ UINT nClientMajVersion = (m_nClientVersion >> 17) & 0x7f; UINT nClientMinVersion = (m_nClientVersion >> 10) & 0x7f; UINT nClientUpVersion = (m_nClientVersion >> 7) & 0x07; m_nClientVersion = MAKE_CLIENT_VERSION(nClientMajVersion, nClientMinVersion, nClientUpVersion); iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("%s v%u.%u%c"), pszSoftware, nClientMajVersion, nClientMinVersion, _T('a') + nClientUpVersion); } if (iLen > 0){ memcpy(m_strClientSoftware.GetBuffer(iLen), szSoftware, iLen*sizeof(TCHAR)); m_strClientSoftware.ReleaseBuffer(iLen); } return; } if (m_bIsHybrid){ m_clientSoft = SO_EDONKEYHYBRID; // seen: // 105010 50.10 // 10501 50.1 // 1051 51.0 // 501 50.1 UINT nClientMajVersion; UINT nClientMinVersion; UINT nClientUpVersion; if (m_nClientVersion > 100000){ UINT uMaj = m_nClientVersion/100000; nClientMajVersion = uMaj - 1; nClientMinVersion = (m_nClientVersion - uMaj*100000) / 100; nClientUpVersion = m_nClientVersion % 100; } else if (m_nClientVersion > 10000){ UINT uMaj = m_nClientVersion/10000; nClientMajVersion = uMaj - 1; nClientMinVersion = (m_nClientVersion - uMaj*10000) / 10; nClientUpVersion = m_nClientVersion % 10; } else if (m_nClientVersion > 1000){ UINT uMaj = m_nClientVersion/1000; nClientMajVersion = uMaj - 1; nClientMinVersion = m_nClientVersion - uMaj*1000; nClientUpVersion = 0; } else if (m_nClientVersion > 100){ UINT uMin = m_nClientVersion/10; nClientMajVersion = 0; nClientMinVersion = uMin; nClientUpVersion = m_nClientVersion - uMin*10; } else{ nClientMajVersion = 0; nClientMinVersion = m_nClientVersion; nClientUpVersion = 0; } m_nClientVersion = MAKE_CLIENT_VERSION(nClientMajVersion, nClientMinVersion, nClientUpVersion); int iLen; TCHAR szSoftware[128]; if (nClientUpVersion) iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("eDonkeyHybrid v%u.%u.%u"), nClientMajVersion, nClientMinVersion, nClientUpVersion); else iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("eDonkeyHybrid v%u.%u"), nClientMajVersion, nClientMinVersion); if (iLen > 0){ memcpy(m_strClientSoftware.GetBuffer(iLen), szSoftware, iLen*sizeof(TCHAR)); m_strClientSoftware.ReleaseBuffer(iLen); } return; } if (m_bIsML || iHashType == SO_MLDONKEY){ m_clientSoft = SO_MLDONKEY; UINT nClientMinVersion = m_nClientVersion; m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0); TCHAR szSoftware[128]; int iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("MLdonkey v0.%u"), nClientMinVersion); if (iLen > 0){ memcpy(m_strClientSoftware.GetBuffer(iLen), szSoftware, iLen*sizeof(TCHAR)); m_strClientSoftware.ReleaseBuffer(iLen); } return; } if (iHashType == SO_OLDEMULE){ m_clientSoft = SO_OLDEMULE; UINT nClientMinVersion = m_nClientVersion; m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0); TCHAR szSoftware[128]; int iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("Old eMule v0.%u"), nClientMinVersion); if (iLen > 0){ memcpy(m_strClientSoftware.GetBuffer(iLen), szSoftware, iLen*sizeof(TCHAR)); m_strClientSoftware.ReleaseBuffer(iLen); } return; } m_clientSoft = SO_EDONKEY; UINT nClientMinVersion = m_nClientVersion; m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0); TCHAR szSoftware[128]; int iLen = _sntprintf(szSoftware, ARRSIZE(szSoftware), _T("eDonkey v0.%u"), nClientMinVersion); if (iLen > 0){ memcpy(m_strClientSoftware.GetBuffer(iLen), szSoftware, iLen*sizeof(TCHAR)); m_strClientSoftware.ReleaseBuffer(iLen); } } int CUpDownClient::GetHashType() const { if (m_achUserHash[5] == 13 && m_achUserHash[14] == 110) return SO_OLDEMULE; else if (m_achUserHash[5] == 14 && m_achUserHash[14] == 111) return SO_EMULE; else if (m_achUserHash[5] == 'M' && m_achUserHash[14] == 'L') return SO_MLDONKEY; else return SO_UNKNOWN; } void CUpDownClient::SetUserName(LPCSTR pszNewName) { if (m_pszUsername){ delete[] m_pszUsername; m_pszUsername = NULL;// needed, in case 'nstrdup' fires an exception!! } if( pszNewName ) m_pszUsername = nstrdup(pszNewName); } void CUpDownClient::RequestSharedFileList() { if (m_iFileListRequested == 0){ AddLogLine(true,GetResString(IDS_SHAREDFILES_REQUEST),GetUserName()); m_iFileListRequested = 1; TryToConnect(true); } else{ AddLogLine(true,_T("Requesting shared files from user %s (%u) is already in progress"),GetUserName(),GetUserIDHybrid()); } } void CUpDownClient::ProcessSharedFileList(char* pachPacket, uint32 nSize, LPCTSTR pszDirectory){ if (m_iFileListRequested > 0){ m_iFileListRequested--; theApp.searchlist->ProcessSearchanswer(pachPacket,nSize,this,NULL,pszDirectory); } } void CUpDownClient::SetUserHash(const uchar* pucUserHash) { if( pucUserHash == NULL ){ md4clr(m_achUserHash); return; } md4cpy(m_achUserHash, pucUserHash); } void CUpDownClient::SendPublicKeyPacket(){ ///* delete this line later*/ DEBUG_ONLY(AddDebugLogLine(false, "sending public key to '%s'", GetUserName())); // send our public key to the client who requested it if (socket == NULL || credits == NULL || m_SecureIdentState != IS_KEYANDSIGNEEDED){ ASSERT ( false ); return; } if (!theApp.clientcredits->CryptoAvailable()) return; Packet* packet = new Packet(OP_PUBLICKEY,theApp.clientcredits->GetPubKeyLen() + 1,OP_EMULEPROT); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); memcpy(packet->pBuffer+1,theApp.clientcredits->GetPublicKey(), theApp.clientcredits->GetPubKeyLen()); packet->pBuffer[0] = theApp.clientcredits->GetPubKeyLen(); if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__PublicKey", this); socket->SendPacket(packet,true,true); m_SecureIdentState = IS_SIGNATURENEEDED; } void CUpDownClient::SendSignaturePacket(){ // signate the public key of this client and send it if (socket == NULL || credits == NULL || m_SecureIdentState == 0){ ASSERT ( false ); return; } if (!theApp.clientcredits->CryptoAvailable()) return; if (credits->GetSecIDKeyLen() == 0) return; // We don't have his public key yet, will be back here later ///* delete this line later*/ DEBUG_ONLY(AddDebugLogLine(false, "sending signature key to '%s'", GetUserName())); // do we have a challenge value received (actually we should if we are in this function) if (credits->m_dwCryptRndChallengeFrom == 0){ if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "Want to send signature but challenge value is invalid ('%s')", GetUserName()); return; } // v2 // we will use v1 as default, except if only v2 is supported bool bUseV2; if ( (m_bySupportSecIdent&1) == 1 ) bUseV2 = false; else bUseV2 = true; uint8 byChaIPKind = 0; uint32 ChallengeIP = 0; if (bUseV2){ if (theApp.serverconnect->GetClientID() == 0 || theApp.serverconnect->IsLowID()){ // we cannot do not know for sure our public ip, so use the remote clients one ChallengeIP = GetIP(); byChaIPKind = CRYPT_CIP_REMOTECLIENT; } else{ ChallengeIP = theApp.serverconnect->GetClientID(); byChaIPKind = CRYPT_CIP_LOCALCLIENT; } } //end v2 uchar achBuffer[250]; uint8 siglen = theApp.clientcredits->CreateSignature(credits, achBuffer, 250, ChallengeIP, byChaIPKind ); if (siglen == 0){ ASSERT ( false ); return; } Packet* packet = new Packet(OP_SIGNATURE,siglen + 1+ ( (bUseV2)? 1:0 ),OP_EMULEPROT); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); memcpy(packet->pBuffer+1,achBuffer, siglen); packet->pBuffer[0] = siglen; if (bUseV2) packet->pBuffer[1+siglen] = byChaIPKind; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__Signature", this); socket->SendPacket(packet,true,true); m_SecureIdentState = IS_ALLREQUESTSSEND; } void CUpDownClient::ProcessPublicKeyPacket(uchar* pachPacket, uint32 nSize){ theApp.clientlist->AddTrackClient(this); ///* delete this line later*/ DEBUG_ONLY(AddDebugLogLine(false, "recieving public key from '%s'", GetUserName())); if (socket == NULL || credits == NULL || pachPacket[0] != nSize-1 || nSize == 0 || nSize > 250){ ASSERT ( false ); return; } if (!theApp.clientcredits->CryptoAvailable()) return; // the function will handle everything (mulitple key etc) if (credits->SetSecureIdent(pachPacket+1, pachPacket[0])){ // if this client wants a signature, now we can send him one if (m_SecureIdentState == IS_SIGNATURENEEDED){ SendSignaturePacket(); } else if(m_SecureIdentState == IS_KEYANDSIGNEEDED) { // something is wrong if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "Invalid State error: IS_KEYANDSIGNEEDED in ProcessPublicKeyPacket"); } } else { if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "Failed to use new received public key"); } } void CUpDownClient::ProcessSignaturePacket(uchar* pachPacket, uint32 nSize){ ///* delete this line later*/ DEBUG_ONLY(AddDebugLogLine(false, "receiving signature from '%s'", GetUserName())); // here we spread the good guys from the bad ones ;) if (socket == NULL || credits == NULL || nSize == 0 || nSize > 250){ ASSERT ( false ); return; } uint8 byChaIPKind; if (pachPacket[0] == nSize-1) byChaIPKind = 0; else if (pachPacket[0] == nSize-2 && (m_bySupportSecIdent & 2) > 0) //v2 byChaIPKind = pachPacket[nSize-1]; else{ ASSERT ( false ); return; } if (!theApp.clientcredits->CryptoAvailable()) return; // we accept only one signature per IP, to avoid floods which need a lot cpu time for cryptfunctions if (m_dwLastSignatureIP == GetIP()) { if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "received multiple signatures from one client"); return; } // also make sure this client has a public key if (credits->GetSecIDKeyLen() == 0) { if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "received signature for client without public key"); return; } // and one more check: did we ask for a signature and sent a challange packet? if (credits->m_dwCryptRndChallengeFor == 0) { if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "received signature for client with invalid challenge value ('%s')", GetUserName()); return; } if (theApp.clientcredits->VerifyIdent(credits, pachPacket+1, pachPacket[0], GetIP(), byChaIPKind ) ){ // result is saved in function abouve //if (thePrefs.GetLogSecureIdent()) // AddDebugLogLine(false, "'%s' has passed the secure identification, V2 State: %i", GetUserName(), byChaIPKind); } else { if (thePrefs.GetLogSecureIdent()) AddDebugLogLine(false, "'%s' has failed the secure identification, V2 State: %i", GetUserName(), byChaIPKind); } m_dwLastSignatureIP = GetIP(); } void CUpDownClient::SendSecIdentStatePacket(){ // check if we need public key and signature uint8 nValue = 0; if (credits){ if (theApp.clientcredits->CryptoAvailable()){ if (credits->GetSecIDKeyLen() == 0) nValue = IS_KEYANDSIGNEEDED; else if (m_dwLastSignatureIP != GetIP()) nValue = IS_SIGNATURENEEDED; } if (nValue == 0){ //if (thePrefs.GetLogSecureIdent()) // DEBUG_ONLY(AddDebugLogLine(false, "Not sending SecIdentState Packet, because State is Zero")); return; } // crypt: send random data to sign uint32 dwRandom = rand()+1; credits->m_dwCryptRndChallengeFor = dwRandom; Packet* packet = new Packet(OP_SECIDENTSTATE,5,OP_EMULEPROT); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); packet->pBuffer[0] = nValue; PokeUInt32(packet->pBuffer+1, dwRandom); if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__SecIdentState", this); socket->SendPacket(packet,true,true); } else ASSERT ( false ); } void CUpDownClient::ProcessSecIdentStatePacket(uchar* pachPacket, uint32 nSize){ if (nSize != 5) return; if (!credits){ ASSERT ( false ); return; } switch(pachPacket[0]){ case 0: m_SecureIdentState = IS_UNAVAILABLE; break; case 1: m_SecureIdentState = IS_SIGNATURENEEDED; break; case 2: m_SecureIdentState = IS_KEYANDSIGNEEDED; break; } credits->m_dwCryptRndChallengeFrom = PeekUInt32(pachPacket+1); } void CUpDownClient::InfoPacketsReceived(){ // indicates that both Information Packets has been received // needed for actions, which process data from both packets ASSERT ( m_byInfopacketsReceived == IP_BOTH ); m_byInfopacketsReceived = IP_NONE; if (m_bySupportSecIdent){ SendSecIdentStatePacket(); } } void CUpDownClient::ResetFileStatusInfo() { if (m_abyPartStatus){ delete[] m_abyPartStatus; m_abyPartStatus = NULL; } m_nPartCount = 0; m_strClientFilename.Empty(); m_bCompleteSource = false; m_dwLastAskedTime = 0; m_iRate = 0; m_strComment.Empty(); } bool CUpDownClient::IsBanned() const { return ( (theApp.clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING); } void CUpDownClient::SendPreviewRequest(const CAbstractFile* pForFile) { if (m_fPreviewReqPending == 0){ m_fPreviewReqPending = 1; Packet* packet = new Packet(OP_REQUESTPREVIEW,16,OP_EMULEPROT); md4cpy(packet->pBuffer,pForFile->GetFileHash()); theApp.uploadqueue->AddUpDataOverheadOther(packet->size); SafeSendPacket(packet); } else{ //to res table - later AddLogLine(true, GetResString(IDS_ERR_PREVIEWALREADY)); } } void CUpDownClient::SendPreviewAnswer(const CKnownFile* pForFile, CxImage** imgFrames, uint8 nCount) { m_fPreviewAnsPending = 0; CSafeMemFile data(1024); if (pForFile){ data.WriteHash16(pForFile->GetFileHash()); } else{ static const uchar _aucZeroHash[16] = {0}; data.WriteHash16(_aucZeroHash); } data.WriteUInt8(nCount); for (int i = 0; i != nCount; i++){ if (imgFrames == NULL){ ASSERT ( false ); return; } CxImage* cur_frame = imgFrames[i]; if (cur_frame == NULL){ ASSERT ( false ); return; } BYTE* abyResultBuffer = NULL; long nResultSize = 0; if (!cur_frame->Encode(abyResultBuffer, nResultSize, CXIMAGE_FORMAT_PNG)){ ASSERT ( false ); return; } data.WriteUInt32(nResultSize); data.Write(abyResultBuffer, nResultSize); free(abyResultBuffer); } Packet* packet = new Packet(&data, OP_EMULEPROT); packet->opcode = OP_PREVIEWANSWER; theApp.uploadqueue->AddUpDataOverheadOther(packet->size); SafeSendPacket(packet); } void CUpDownClient::ProcessPreviewReq(char* pachPacket, uint32 nSize){ if (nSize < 16) throw GetResString(IDS_ERR_WRONGPACKAGESIZE); if (m_fPreviewAnsPending || thePrefs.CanSeeShares()==vsfaNobody || (thePrefs.CanSeeShares()==vsfaFriends && !IsFriend())) return; m_fPreviewAnsPending = 1; CKnownFile* previewFile = theApp.sharedfiles->GetFileByID((uchar*)pachPacket); if (previewFile == NULL){ SendPreviewAnswer(NULL, NULL, 0); } else{ previewFile->GrabImage(4,0,true,450,this); } } void CUpDownClient::ProcessPreviewAnswer(char* pachPacket, uint32 nSize){ if (m_fPreviewReqPending == 0) return; m_fPreviewReqPending = 0; CSafeMemFile data((BYTE*)pachPacket,nSize); uchar Hash[16]; data.ReadHash16(Hash); uint8 nCount = data.ReadUInt8(); if (nCount == 0){ // to res table -later AddLogLine(true, GetResString(IDS_ERR_PREVIEWFAILED),GetUserName()); return; } CSearchFile* sfile = theApp.searchlist->GetSearchFileByHash(Hash); if (sfile == NULL){ //already deleted return; } BYTE* pBuffer = NULL; try{ for (int i = 0; i != nCount; i++){ uint32 nImgSize = data.ReadUInt32(); if (nImgSize > nSize) throw CString(_T("CUpDownClient::ProcessPreviewAnswer - Provided image size exceeds limit")); pBuffer = new BYTE[nImgSize]; data.Read(pBuffer, nImgSize); CxImage* image = new CxImage(pBuffer, nImgSize, CXIMAGE_FORMAT_PNG); delete[] pBuffer; pBuffer = NULL; if (image->IsValid()){ sfile->AddPreviewImg(image); } } } catch(...){ if (pBuffer != NULL) delete[] pBuffer; throw; } (new PreviewDlg())->SetFile(sfile); } // sends a packet, if needed it will establish a connection before // options used: ignore max connections, control packet, delete packet // !if the functions returns false that client object was deleted because the connection try failed and the object wasn't needed anymore. bool CUpDownClient::SafeSendPacket(Packet* packet){ if (socket && socket->IsConnected()){ socket->SendPacket(packet); return true; } else{ m_WaitingPackets_list.AddTail(packet); return TryToConnect(true); } } bool CUpDownClient::HasLowID() const { return IsLowIDHybrid(m_nUserIDHybrid); } #ifdef _DEBUG void CUpDownClient::AssertValid() const { CObject::AssertValid(); CHECK_OBJ(socket); CHECK_PTR(credits); CHECK_PTR(m_Friend); CHECK_OBJ(reqfile); (void)m_abyUpPartStatus; m_OtherRequests_list.AssertValid(); m_OtherNoNeeded_list.AssertValid(); (void)m_lastPartAsked; CHECK_BOOL(m_bAddNextConnect); (void)m_cMessagesReceived; (void)m_cMessagesSent; (void)m_dwUserIP; (void)m_dwServerIP; (void)m_nUserIDHybrid; (void)m_nUserPort; (void)m_nServerPort; (void)m_nClientVersion; (void)m_nUpDatarate; (void)dataratems; (void)m_byEmuleVersion; (void)m_byDataCompVer; CHECK_BOOL(m_bEmuleProtocol); CHECK_BOOL(m_bIsHybrid); (void)m_pszUsername; (void)m_achUserHash; (void)m_nUDPPort; (void)m_nKadPort; (void)m_byUDPVer; (void)m_bySourceExchangeVer; (void)m_byAcceptCommentVer; (void)m_byExtendedRequestsVer; CHECK_BOOL(m_bFriendSlot); CHECK_BOOL(m_bCommentDirty); CHECK_BOOL(m_bIsML); ASSERT( m_clientSoft >= SO_EMULE && m_clientSoft <= SO_SHAREAZA || m_clientSoft == SO_MLDONKEY || m_clientSoft >= SO_EDONKEYHYBRID && m_clientSoft <= SO_UNKNOWN ); (void)m_strClientSoftware; (void)m_dwLastSourceRequest; (void)m_dwLastSourceAnswer; (void)m_dwLastAskedForSources; (void)m_iFileListRequested; (void)m_byCompatibleClient; m_WaitingPackets_list.AssertValid(); m_DontSwap_list.AssertValid(); (void)m_lastRefreshedDLDisplay; ASSERT( m_SecureIdentState >= IS_UNAVAILABLE && m_SecureIdentState <= IS_KEYANDSIGNEEDED ); (void)m_dwLastSignatureIP; ASSERT( (m_byInfopacketsReceived & ~IP_BOTH) == 0 ); (void)m_bySupportSecIdent; (void)m_nTransferedUp; ASSERT( m_nUploadState >= US_UPLOADING && m_nUploadState <= US_NONE ); (void)m_dwUploadTime; (void)m_cAsked; (void)m_dwLastUpRequest; (void)m_nCurSessionUp; (void)m_nCurQueueSessionPayloadUp; (void)m_addedPayloadQueueSession; (void)m_nUpPartCount; (void)m_nUpCompleteSourcesCount; (void)s_UpStatusBar; (void)requpfileid; (void)m_lastRefreshedULDisplay; m_AvarageUDR_list.AssertValid(); m_BlockRequests_queue.AssertValid(); m_DoneBlocks_list.AssertValid(); m_RequestedFiles_list.AssertValid(); ASSERT( m_nDownloadState >= DS_DOWNLOADING && m_nDownloadState <= DS_NONE ); (void)m_cDownAsked; (void)m_abyPartStatus; (void)m_dwLastAskedTime; (void)m_strClientFilename; (void)m_nTransferedDown; (void)m_dwDownStartTime; (void)m_nLastBlockOffset; (void)m_nDownDatarate; (void)m_nDownDataRateMS; (void)m_nSumForAvgDownDataRate; (void)m_cShowDR; (void)m_nRemoteQueueRank; (void)m_dwLastBlockReceived; (void)m_nPartCount; ASSERT( m_nSourceFrom >= SF_SERVER && m_nSourceFrom <= SF_PASSIVE ); CHECK_BOOL(m_bRemoteQueueFull); CHECK_BOOL(m_bCompleteSource); CHECK_BOOL(m_bReaskPending); CHECK_BOOL(m_bUDPPending); CHECK_BOOL(m_bTransferredDownMini); ASSERT( m_nKadState >= KS_NONE && m_nKadState <= KS_CONNECTED_BUDDY ); m_AvarageDDR_list.AssertValid(); (void)sumavgUDR; m_PendingBlocks_list.AssertValid(); m_DownloadBlocks_list.AssertValid(); (void)s_StatusBar; ASSERT( m_nChatstate >= MS_NONE && m_nChatstate <= MS_UNABLETOCONNECT ); (void)m_strComment; (void)m_iRate; #undef CHECK_PTR #undef CHECK_BOOL } #endif #ifdef _DEBUG void CUpDownClient::Dump(CDumpContext& dc) const { CObject::Dump(dc); } #endif LPCTSTR CUpDownClient::DbgGetDownloadState() const { const static LPCTSTR apszState[] = { _T("Downloading"), _T("OnQueue"), _T("Connected"), _T("Connecting"), _T("WaitCallback"), _T("ReqHashSet"), _T("NoNeededParts"), _T("TooManyConns"), _T("LowToLowIp"), _T("Banned"), _T("Error"), _T("None") }; if (GetDownloadState() >= ARRSIZE(apszState)) return _T("*Unknown*"); return apszState[GetDownloadState()]; } LPCTSTR CUpDownClient::DbgGetUploadState() const { const static LPCTSTR apszState[] = { _T("Uploading"), _T("OnUploadQueue"), _T("WaitCallback"), _T("Connecting"), _T("Pending"), _T("LowToLowIp"), _T("Banned"), _T("Error"), _T("None") }; if (GetUploadState() >= ARRSIZE(apszState)) return _T("*Unknown*"); return apszState[GetUploadState()]; } CString CUpDownClient::DbgGetFullClientSoftVer() const { if (GetClientModVer().IsEmpty()) return GetClientSoftVer(); return GetClientSoftVer() + _T(" [") + GetClientModVer() + _T(']'); } CString CUpDownClient::DbgGetClientInfo(bool bFormatIP) const { CString str; if (this != NULL) { try{ str.Format( bFormatIP ? _T("%-15s '%s' (%s,%s/%s)") : _T("%s '%s' (%s,%s/%s)"), ipstr(GetConnectIP()), GetUserName(), DbgGetFullClientSoftVer(), DbgGetDownloadState(), DbgGetUploadState()); } catch(...){ str.Format(_T("%08x - Invalid client instance"), this); } } return str; } bool CUpDownClient::CheckHandshakeFinished(UINT protocol, UINT opcode) const { if (m_bHelloAnswerPending){ //throw CString(_T("Handshake not finished")); // -> disconnect client // this triggers way too often.. need more time to look at this -> only create a warning if (thePrefs.GetVerbose()) AddDebugLogLine(false, _T("Handshake not finished - while processing packet: %s; %s"), DbgGetClientTCPOpcode(protocol, opcode), DbgGetClientInfo()); return false; } return true; } void CUpDownClient::CheckForGPLEvilDoer(){ // check for known major gpl breaker if (m_strModVersion.Trim().MakeUpper().Find("LH") == 0 || m_strModVersion.Trim().MakeUpper().Find("LIO") == 0){ m_bGPLEvildoer = true; } }