From cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c Mon Sep 17 00:00:00 2001 From: watcherhd Date: Thu, 21 Apr 2011 14:14:52 +0000 Subject: svn.miranda.im is moving to a new home! git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@7 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- icqj_mod/icq_direct.c | 1367 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1367 insertions(+) create mode 100644 icqj_mod/icq_direct.c (limited to 'icqj_mod/icq_direct.c') diff --git a/icqj_mod/icq_direct.c b/icqj_mod/icq_direct.c new file mode 100644 index 0000000..1adce2a --- /dev/null +++ b/icqj_mod/icq_direct.c @@ -0,0 +1,1367 @@ +// ---------------------------------------------------------------------------80 +// ICQ plugin for Miranda Instant Messenger +// ________________________________________ +// +// Copyright © 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede +// Copyright © 2001,2002 Jon Keating, Richard Hughes +// Copyright © 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater +// Copyright © 2004,2005,2006 Joe Kucera +// +// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// ----------------------------------------------------------------------------- +// +// File name : $Source: /cvsroot/miranda/miranda/protocols/IcqOscarJ/icq_direct.c,v $ +// Revision : $Revision: 2955 $ +// Last change on : $Date: 2006-05-24 23:04:25 +0200 (Wed, 24 May 2006) $ +// Last change by : $Author: jokusoftware $ +// +// DESCRIPTION: +// +// Describe me here please... +// +// ----------------------------------------------------------------------------- + +#include "icqoscar.h" + + + +typedef struct directthreadstartinfo_t +{ + int type; // Only valid for outgoing connections + int incoming; // 1=incoming, 0=outgoing + HANDLE hConnection; // only valid for incoming connections, handle to the connection + HANDLE hContact; // Only valid for outgoing connections + void* pvExtra; // Only valid for outgoing connections +} directthreadstartinfo; + +static unsigned char client_check_data[] = { + "As part of this software beta version Mirabilis is " + "granting a limited access to the ICQ network, " + "servers, directories, listings, information and databases (\"" + "ICQ Services and Information\"). The " + "ICQ Service and Information may databases (\"" + "ICQ Services and Information\"). The " + "ICQ Service and Information may\0" +}; + +static directconnect** directConnList = NULL; +static int directConnSize = 0; +static int directConnCount = 0; +static int mutexesInited = 0; +static CRITICAL_SECTION directConnListMutex; +static CRITICAL_SECTION expectedFileRecvMutex; +static int expectedFileRecvCount = 0; +static filetransfer** expectedFileRecv = NULL; + +extern WORD wListenPort; +extern DWORD dwLocalDirectConnCookie; + +static void handleDirectPacket(directconnect* dc, PBYTE buf, WORD wLen); +static DWORD __stdcall icq_directThread(directthreadstartinfo* dtsi); +static void sendPeerInit_v78(directconnect* dc); +static int DecryptDirectPacket(directconnect* dc, PBYTE buf, WORD wLen); +static void sendPeerInitAck(directconnect* dc); +static void sendPeerMsgInit(directconnect* dc, DWORD dwSeq); +static void sendPeerFileInit(directconnect* dc); + + +void InitDirectConns(void) +{ + if (!mutexesInited) + { + mutexesInited = 1; + InitializeCriticalSection(&directConnListMutex); + InitializeCriticalSection(&expectedFileRecvMutex); + } + directConnCount = 0; + directConnSize = 0; +} + + + +void UninitDirectConns(void) +{ + CloseContactDirectConns(NULL); + + for(;;) + { + if (!directConnCount) + break; + + Sleep(10); /* yeah, ugly */ + } + + DeleteCriticalSection(&directConnListMutex); + DeleteCriticalSection(&expectedFileRecvMutex); + + SAFE_FREE((void**)&directConnList); +} + + + +void CloseContactDirectConns(HANDLE hContact) +{ + int i; + + EnterCriticalSection(&directConnListMutex); + + for (i = 0; i < directConnCount; i++) + { + if (!hContact || directConnList[i]->hContact == hContact) + { + HANDLE hConnection = directConnList[i]->hConnection; + int sck = CallService(MS_NETLIB_GETSOCKET, (WPARAM)hConnection, 0); + + directConnList[i]->hConnection = NULL; // do not allow reuse + if (sck!=INVALID_SOCKET) shutdown(sck, 2); // close gracefully + Netlib_CloseHandle(hConnection); + } + } + + LeaveCriticalSection(&directConnListMutex); +} + + + +static void ResizeDirectList(int nSize) +{ + if ((directConnSize < nSize) || ((directConnSize > nSize + 6) && nSize)) + { + if (directConnSize < nSize) + directConnSize += 4; + else + directConnSize -= 4; + + directConnList = (directconnect**)realloc(directConnList, sizeof(directconnect*) * directConnSize); + } +} + + + +static void AddDirectConnToList(directconnect* dc) +{ + EnterCriticalSection(&directConnListMutex); + + ResizeDirectList(directConnCount + 1); + directConnList[directConnCount] = dc; + directConnCount++; + + LeaveCriticalSection(&directConnListMutex); +} + + + +static RemoveDirectConnFromList(directconnect* dc) +{ + int i; + + EnterCriticalSection(&directConnListMutex); + + for (i = 0; i < directConnCount; i++) + { + if (directConnList[i] == dc) + { + directConnCount--; + directConnList[i] = directConnList[directConnCount]; + ResizeDirectList(directConnCount); + break; + } + } + + LeaveCriticalSection(&directConnListMutex); +} + + + +directconnect* FindFileTransferDC(filetransfer* ft) +{ + int i; + directconnect* dc = NULL; + + EnterCriticalSection(&directConnListMutex); + + for (i = 0; i < directConnCount; i++) + { + if (directConnList[i] && directConnList[i]->ft == ft) + { + dc = directConnList[i]; + break; + } + } + + LeaveCriticalSection(&directConnListMutex); + return dc; +} + + + +void AddExpectedFileRecv(filetransfer* ft) +{ + EnterCriticalSection(&expectedFileRecvMutex); + + expectedFileRecv = (filetransfer** )realloc(expectedFileRecv, sizeof(filetransfer *) * (expectedFileRecvCount + 1)); + expectedFileRecv[expectedFileRecvCount++] = ft; + + LeaveCriticalSection(&expectedFileRecvMutex); +} + + + +filetransfer *FindExpectedFileRecv(DWORD dwUin, DWORD dwTotalSize) +{ + int i; + filetransfer* pFt = NULL; + + + EnterCriticalSection(&expectedFileRecvMutex); + + for (i = 0; i < expectedFileRecvCount; i++) + { + if (expectedFileRecv[i]->dwUin == dwUin && expectedFileRecv[i]->dwTotalSize == dwTotalSize) + { + pFt = expectedFileRecv[i]; + expectedFileRecvCount--; + memmove(expectedFileRecv + i, expectedFileRecv + i + 1, sizeof(filetransfer*) * (expectedFileRecvCount - i)); + expectedFileRecv = (filetransfer**)realloc(expectedFileRecv, sizeof(filetransfer*) * expectedFileRecvCount); + + // Filereceive found, exit loop + break; + } + } + + LeaveCriticalSection(&expectedFileRecvMutex); + + return pFt; +} + + + +int sendDirectPacket(directconnect* dc, icq_packet* pkt) +{ + int nResult; + + nResult = Netlib_Send(dc->hConnection, (const char*)pkt->pData, pkt->wLen + 2, 0); + + if (nResult == SOCKET_ERROR) + { + NetLog_Direct("Direct %p socket error: %d, closing", dc->hConnection, GetLastError()); + CloseDirectConnection(dc); + } + + SAFE_FREE(&pkt->pData); + + return nResult; +} + + + +directthreadstartinfo* CreateDTSI(HANDLE hContact, HANDLE hConnection, int type) +{ + directthreadstartinfo* dtsi; + + dtsi = (directthreadstartinfo*)SAFE_MALLOC(sizeof(directthreadstartinfo)); + dtsi->hContact = hContact; + dtsi->hConnection = hConnection; + if (type == -1) + { + dtsi->incoming = 1; + } + else + { + dtsi->type = type; + } + + return dtsi; +} + + + +// Check if we have an open and initialized DC with type +// 'type' to the specified contact +BOOL IsDirectConnectionOpen(HANDLE hContact, int type) +{ + int i; + BOOL bIsOpen = FALSE, bIsCreated = FALSE; + + + EnterCriticalSection(&directConnListMutex); + + for (i = 0; i < directConnCount; i++) + { + if (directConnList[i] && (directConnList[i]->type == type)) + { + if (directConnList[i]->hContact == hContact) + if (directConnList[i]->initialised) + { + // Connection is OK + bIsOpen = TRUE; + // we are going to use the conn, so prevent timeout + directConnList[i]->packetPending = 1; + break; + } + else + bIsCreated = TRUE; // we found pending connection + } + } + + LeaveCriticalSection(&directConnListMutex); + + if (!bIsCreated && !bIsOpen && type == DIRECTCONN_STANDARD && gbDCMsgEnabled == 2) + { // do not try to open DC to offline contact + if (ICQGetContactStatus(hContact) == ID_STATUS_OFFLINE) return FALSE; + + // Create a new connection + OpenDirectConnection(hContact, DIRECTCONN_STANDARD, NULL); + } + + return bIsOpen; +} + + + +// This function is called from the Netlib when someone is connecting to +// one of our incomming DC ports +void icq_newConnectionReceived(HANDLE hNewConnection, DWORD dwRemoteIP) +{ + pthread_t tid; + + // Start a new thread for the incomming connection + tid.hThread = (HANDLE)forkthreadex(NULL, 0, icq_directThread, CreateDTSI(NULL, hNewConnection, -1), 0, &tid.dwThreadId); + CloseHandle(tid.hThread); +} + + + +// Opens direct connection of specified type to specified contact +void OpenDirectConnection(HANDLE hContact, int type, void* pvExtra) +{ + pthread_t tid; + directthreadstartinfo* dtsi; + + // Create a new connection + dtsi = CreateDTSI(hContact, NULL, type); + dtsi->pvExtra = pvExtra; + + tid.hThread = (HANDLE)forkthreadex(NULL, 0, icq_directThread, dtsi, 0, &tid.dwThreadId); + CloseHandle(tid.hThread); +} + + + +// Safely close NetLib connection - do not corrupt direct connection list +void CloseDirectConnection(directconnect *dc) +{ + EnterCriticalSection(&directConnListMutex); + + if (dc->hConnection) + { + Netlib_CloseHandle(dc->hConnection); +#ifdef _DEBUG + NetLog_Direct("Direct conn closed (%p)", dc->hConnection); +#endif + dc->hConnection = NULL; + } + + LeaveCriticalSection(&directConnListMutex); +} + + + +// This should be called only if connection already exists +int SendDirectMessage(HANDLE hContact, icq_packet *pkt) +{ + int i; + + EnterCriticalSection(&directConnListMutex); + + for (i = 0; i < directConnCount; i++) + { + if (directConnList[i] == NULL) + continue; + + if (directConnList[i]->hContact == hContact) + { + if (directConnList[i]->initialised) + { + // This connection can be reused, send packet and exit + NetLog_Direct("Sending direct message"); + + if (pkt->pData[2] == 2) + EncryptDirectPacket(directConnList[i], pkt); + + sendDirectPacket(directConnList[i], pkt); + directConnList[i]->packetPending = 0; // packet done + + LeaveCriticalSection(&directConnListMutex); + + return TRUE; // Success + } + break; // connection not ready, use server instead + } + } + + LeaveCriticalSection(&directConnListMutex); + + return FALSE; // connection pending, we failed, use server instead +} + + +// Called from icq_newConnectionReceived when a new incomming dc is done +// Called from OpenDirectConnection when a new outgoing dc is done +// Called from SendDirectMessage when a new outgoing dc is done +static DWORD __stdcall icq_directThread(directthreadstartinfo *dtsi) +{ + directconnect dc = {0}; + NETLIBPACKETRECVER packetRecv={0}; + HANDLE hPacketRecver; + BOOL bFirstPacket = TRUE; + + + srand(time(NULL)); + AddDirectConnToList(&dc); + + // Initialize DC struct + dc.hContact = dtsi->hContact; + dc.dwThreadId = GetCurrentThreadId(); + dc.incoming = dtsi->incoming; + dc.hConnection = dtsi->hConnection; + dc.ft = NULL; + + if (!dc.incoming) + { + dc.type = dtsi->type; + dc.dwRemoteExternalIP = ICQGetContactSettingDword(dtsi->hContact, "IP", 0); + dc.dwRemoteInternalIP = ICQGetContactSettingDword(dtsi->hContact, "RealIP", 0); + dc.dwRemotePort = ICQGetContactSettingWord(dtsi->hContact, "UserPort", 0); + dc.dwRemoteUin = ICQGetContactSettingUIN(dtsi->hContact); + dc.wVersion = ICQGetContactSettingWord(dtsi->hContact, "Version", 0); + dc.dwConnCookie = ICQGetContactSettingDword(dtsi->hContact, "DirectCookie", 0); + + if (!dc.wVersion) + dc.wVersion = ICQGetContactSettingWord(dtsi->hContact, "OldVersion", 0); + + if (!dc.dwRemoteExternalIP && !dc.dwRemoteInternalIP) + { // we do not have any ip, do not try to connect + RemoveDirectConnFromList(&dc); + SAFE_FREE(&dtsi); + return 0; + } + + dc.dwReqId = 0; + + if (dc.type == DIRECTCONN_STANDARD) + { + // do nothing - some specific init for msg sessions + } + else if (dc.type == DIRECTCONN_FILE) + { + dc.ft = (filetransfer*)dtsi->pvExtra; + dc.dwRemotePort = dc.ft->dwRemotePort; + } + else if (dc.type == DIRECTCONN_REVERSE) + { + dc.dwReqId = (DWORD)dtsi->pvExtra; + } + } + else + { + dc.type = DIRECTCONN_STANDARD; + } + + SAFE_FREE(&dtsi); + + // Load local IP information + dc.dwLocalExternalIP = ICQGetContactSettingDword(NULL, "IP", 0); + dc.dwLocalInternalIP = ICQGetContactSettingDword(NULL, "RealIP", 0); + + // Create outgoing DC + if (!dc.incoming) + { + NETLIBOPENCONNECTION nloc = {0}; + IN_ADDR addr; + + + nloc.cbSize = sizeof(nloc); + nloc.flags = 0; + if (dc.dwRemoteExternalIP == dc.dwLocalExternalIP) + addr.S_un.S_addr = htonl(dc.dwRemoteInternalIP); + else + addr.S_un.S_addr = htonl(dc.dwRemoteExternalIP); + + if (!addr.S_un.S_addr) + { // IP to connect to is empty, go away + RemoveDirectConnFromList(&dc); + return 0; + } + nloc.szHost = inet_ntoa(addr); + nloc.wPort = (WORD)dc.dwRemotePort; + NetLog_Direct("%sConnecting to %s:%u", dc.type==DIRECTCONN_REVERSE?"Reverse ":"", nloc.szHost, nloc.wPort); + + dc.hConnection = NetLib_OpenConnection(ghDirectNetlibUser, &nloc); + if (dc.hConnection == NULL) + { + if (dc.type != DIRECTCONN_REVERSE) + { // try reverse connect + reverse_cookie *pCookie = (reverse_cookie*)SAFE_MALLOC(sizeof(reverse_cookie)); + DWORD dwCookie; + + NetLog_Direct("connect() failed (%d), trying reverse.", GetLastError()); + + if (pCookie) + { // ini cookie + pCookie->pMessage.bMessageType = MTYPE_REVERSE_REQUEST; + pCookie->pMessage.dwMsgID1 = time(NULL); + pCookie->pMessage.dwMsgID2 = RandRange(0, 0x00FF); + pCookie->hContact = dc.hContact; + pCookie->dwUin = dc.dwRemoteUin; + pCookie->type = dc.type; + pCookie->ft = dc.ft; + dwCookie = AllocateCookie(CKT_REVERSEDIRECT, 0, dc.dwRemoteUin, pCookie); + icq_sendReverseReq(&dc, dwCookie, (message_cookie_data*)pCookie); + RemoveDirectConnFromList(&dc); + + return 0; + } + else + NetLog_Direct("Reverse failed (%s)", "malloc failed"); + } + else // we failed reverse connection + { + reverse_cookie *pCookie; + DWORD dwUin; + + if (FindCookie(dc.dwReqId, &dwUin, &pCookie)) + { // release cookie + icq_sendReverseFailed(&dc, pCookie->pMessage.dwMsgID1, pCookie->pMessage.dwMsgID2, dc.dwReqId); + ReleaseCookie(dc.dwReqId); + } + } + NetLog_Direct("connect() failed (%d)", GetLastError()); + RemoveDirectConnFromList(&dc); + if (dc.type == DIRECTCONN_FILE) + ICQBroadcastAck(dc.ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, dc.ft, 0); + + return 0; + } + + if (dc.type == DIRECTCONN_FILE) + dc.ft->hConnection = dc.hConnection; + + if (dc.wVersion > 6) + { + sendPeerInit_v78(&dc); + } + else + { + NetLog_Direct("Error: Unsupported direct protocol: %d, closing.", dc.wVersion); + CloseDirectConnection(&dc); + RemoveDirectConnFromList(&dc); + + return 0; + } + } + + hPacketRecver = (HANDLE)CallService(MS_NETLIB_CREATEPACKETRECVER, (WPARAM)dc.hConnection, 8192); + packetRecv.cbSize = sizeof(packetRecv); + packetRecv.bytesUsed = 0; + + // Packet receiving loop + + while (dc.hConnection) + { + int recvResult; + + packetRecv.dwTimeout = dc.wantIdleTime ? 0 : 600000; + + recvResult = CallService(MS_NETLIB_GETMOREPACKETS, (WPARAM)hPacketRecver, (LPARAM)&packetRecv); + if (recvResult == 0) + { + NetLog_Direct("Clean closure of direct socket (%p)", dc.hConnection); + break; + } + + if (recvResult == SOCKET_ERROR) + { + if (GetLastError() == ERROR_TIMEOUT) + { // TODO: this will not work on some systems + if (dc.wantIdleTime) + { + switch (dc.type) + { + case DIRECTCONN_FILE: + handleFileTransferIdle(&dc); + break; + } + } + else if (dc.packetPending) + { // do we expect packet soon? + NetLog_Direct("Keeping connection, packet pending."); + } + else + { + NetLog_Direct("Connection inactive for 10 minutes, closing."); + break; + } + } + else + { + NetLog_Direct("Abortive closure of direct socket (%p) (%d)", dc.hConnection, GetLastError()); + break; + } + } + + if (dc.type == DIRECTCONN_CLOSING) + packetRecv.bytesUsed = packetRecv.bytesAvailable; + else + { + int i; + + for (i = 0; i + 2 <= packetRecv.bytesAvailable;) + { + WORD wLen = *(WORD*)(packetRecv.buffer + i); + + if (bFirstPacket) + { + if (wLen > 64) + { // roughly check first packet size + NetLog_Direct("Error: Overflowed packet, closing connection."); + CloseDirectConnection(&dc); + break; + } + bFirstPacket = FALSE; + } + + if (wLen + 2 + i > packetRecv.bytesAvailable) + break; + + if (dc.type == DIRECTCONN_STANDARD && wLen && packetRecv.buffer[i + 2] == 2) + { + if (!DecryptDirectPacket(&dc, packetRecv.buffer + i + 3, (WORD)(wLen - 1))) + { + NetLog_Direct("Error: Corrupted packet encryption, ignoring packet"); + i += wLen + 2; + continue; + } + } +#ifdef _DEBUG + NetLog_Direct("New direct package"); +#endif + if (dc.type == DIRECTCONN_FILE && dc.initialised) + handleFileTransferPacket(&dc, packetRecv.buffer + i + 2, wLen); + else + handleDirectPacket(&dc, packetRecv.buffer + i + 2, wLen); + + i += wLen + 2; + } + packetRecv.bytesUsed = i; + } + } + + // End of packet receiving loop + + Netlib_CloseHandle(hPacketRecver); + CloseDirectConnection(&dc); + + if (dc.ft) + { + if (dc.ft->fileId != -1) + { + _close(dc.ft->fileId); + ICQBroadcastAck(dc.ft->hContact, ACKTYPE_FILE, dc.ft->dwBytesDone==dc.ft->dwTotalSize ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, dc.ft, 0); + } + else if (dc.ft->hConnection) + ICQBroadcastAck(dc.ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, dc.ft, 0); + + SAFE_FREE(&dc.ft->szFilename); + SAFE_FREE(&dc.ft->szDescription); + SAFE_FREE(&dc.ft->szSavePath); + SAFE_FREE(&dc.ft->szThisFile); + SAFE_FREE(&dc.ft->szThisSubdir); + if (dc.ft->files) + { + int i; + + for (i = 0; i < (int)dc.ft->dwFileCount; i++) + SAFE_FREE(&dc.ft->files[i]); + SAFE_FREE((char**)&dc.ft->files); + } + SAFE_FREE(&dc.ft); + _chdir("\\"); /* so we don't leave a subdir handle open so it can't be deleted */ + } + + RemoveDirectConnFromList(&dc); + + return 0; +} + + + +static void handleDirectPacket(directconnect* dc, PBYTE buf, WORD wLen) +{ + if (wLen < 1) + return; + + switch (buf[0]) + { + case PEER_FILE_INIT: // first packet of a file transfer +#ifdef _DEBUG + NetLog_Direct("Received PEER_FILE_INIT from %u",dc->dwRemoteUin); +#endif + if (dc->handshake) + handleFileTransferPacket(dc, buf, wLen); + else + NetLog_Direct("Received %s on uninitialised DC, ignoring.", "PEER_FILE_INIT"); + + break; + + case PEER_INIT_ACK: // This is sent as a response to our PEER_INIT packet + if (wLen != 4) + { + NetLog_Direct("Error: Received malformed PEER_INITACK from %u", dc->dwRemoteUin); + break; + } +#ifdef _DEBUG + NetLog_Direct("Received PEER_INITACK from %u on %s DC", dc->dwRemoteUin, dc->incoming?"incoming":"outgoing"); +#endif + if (dc->incoming) dc->handshake = 1; + + if (dc->incoming && dc->type == DIRECTCONN_REVERSE) + { + reverse_cookie* pCookie; + DWORD dwCookieUin; + + dc->incoming = 0; + + if (FindCookie(dc->dwReqId, &dwCookieUin, &pCookie) && pCookie) + { // valid reverse DC, check and init session + FreeCookie(dc->dwReqId); + if (pCookie->dwUin == dc->dwRemoteUin) + { // valid connection + dc->type = pCookie->type; + dc->ft = pCookie->ft; + dc->hContact = pCookie->hContact; + if (dc->type == DIRECTCONN_STANDARD) + { // init message session + sendPeerMsgInit(dc, 0); + } + else if (dc->type == DIRECTCONN_FILE) + { // init file session + sendPeerFileInit(dc); + dc->initialised = 1; + } + SAFE_FREE(&pCookie); + break; + } + else + { + SAFE_FREE(&pCookie); + NetLog_Direct("Error: Invalid connection (UINs does not match)."); + CloseDirectConnection(dc); + return; + } + } + else + { + NetLog_Direct("Error: Received unexpected reverse DC, closing."); + CloseDirectConnection(dc); + return; + } + } + break; + + case PEER_INIT: /* connect packet */ +#ifdef _DEBUG + NetLog_Direct("Received PEER_INIT"); +#endif + buf++; + + if (wLen < 3) + return; + + unpackLEWord(&buf, &dc->wVersion); + + if (dc->wVersion > 6) + { // we support only versions 7 and up + WORD wSecondLen; + DWORD dwUin; + DWORD dwPort; + DWORD dwCookie; + HANDLE hContact; + + if (wLen != 0x30) + { + NetLog_Direct("Error: Received malformed PEER_INIT"); + return; + } + + unpackLEWord(&buf, &wSecondLen); + if (wSecondLen && wSecondLen != 0x2b) + { // OMG? GnomeICU sets this to zero + NetLog_Direct("Error: Received malformed PEER_INIT"); + return; + } + + unpackLEDWord(&buf, &dwUin); + if (dwUin != dwLocalUIN) + { + NetLog_Direct("Error: Received PEER_INIT targeted to %u", dwUin); + CloseDirectConnection(dc); + return; + } + + buf += 2; /* 00 00 */ + unpackLEDWord(&buf, &dc->dwRemotePort); + unpackLEDWord(&buf, &dc->dwRemoteUin); + unpackDWord(&buf, &dc->dwRemoteExternalIP); + unpackDWord(&buf, &dc->dwRemoteInternalIP); + buf ++; /* 04: accept direct connections */ + unpackLEDWord(&buf, &dwPort); + if (dwPort != dc->dwRemotePort) + { + NetLog_Direct("Error: Received malformed PEER_INIT (invalid port)"); + return; + } + unpackLEDWord(&buf, &dwCookie); + + buf += 8; // Unknown stuff + unpackLEDWord(&buf, &dc->dwReqId); + + if (dc->dwRemoteUin || !dc->dwReqId) + { // OMG! Licq sends on reverse connection empty uin + hContact = HContactFromUIN(dc->dwRemoteUin, NULL); + if (hContact == INVALID_HANDLE_VALUE) + { + NetLog_Direct("Error: Received PEER_INIT from %u not on my list", dwUin); + CloseDirectConnection(dc); + return; /* don't allow direct connection with people not on my clist */ + } + + if (dwCookie != ICQGetContactSettingDword(hContact, "DirectCookie", 0)) + { + NetLog_Direct("Error: Received PEER_INIT with broken cookie"); + CloseDirectConnection(dc); + return; + } + } + + if (dc->incoming && dc->dwReqId) + { // this is reverse connection + dc->type = DIRECTCONN_REVERSE; + if (!dc->dwRemoteUin) + { // we need to load cookie (licq) + reverse_cookie* pCookie; + DWORD dwCookieUin; + + if (FindCookie(dc->dwReqId, &dwCookieUin, &pCookie) && pCookie) + { // valid reverse DC, check and init session + dc->dwRemoteUin = pCookie->dwUin; + dc->hContact = pCookie->hContact; + } + else + { + NetLog_Direct("Error: Received unexpected reverse DC, closing."); + CloseDirectConnection(dc); + return; + } + } + } + + sendPeerInitAck(dc); // ack good PEER_INIT packet + + if (dc->incoming) + { // store good IP info + dc->hContact = hContact; + dc->dwConnCookie = dwCookie; + ICQWriteContactSettingDword(dc->hContact, "IP", dc->dwRemoteExternalIP); + ICQWriteContactSettingDword(dc->hContact, "RealIP", dc->dwRemoteInternalIP); + sendPeerInit_v78(dc); // reply with our PEER_INIT + } + else // outgoing + { + dc->handshake = 1; + + if (dc->type == DIRECTCONN_REVERSE) + { + dc->incoming = 1; // this is incoming reverse connection + dc->type = DIRECTCONN_STANDARD; // we still do not know type + } + else if (dc->type == DIRECTCONN_STANDARD) + { // send PEER_MSGINIT + sendPeerMsgInit(dc, 0); + } + else if (dc->type == DIRECTCONN_FILE) + { + sendPeerFileInit(dc); + dc->initialised = 1; + } + } + } + else + { + NetLog_Direct("Unsupported direct protocol: %d, closing connection", dc->wVersion); + CloseDirectConnection(dc); + } + break; + + case PEER_MSG: /* messaging packets */ +#ifdef _DEBUG + NetLog_Direct("Received PEER_MSG from %u", dc->dwRemoteUin); +#endif + if (dc->initialised) + handleDirectMessage(dc, buf + 1, (WORD)(wLen - 1)); + else + NetLog_Direct("Received %s on uninitialised DC, ignoring.", "PEER_MSG"); + + break; + + case PEER_MSG_INIT: /* init message connection */ + { // it is sent by both contains GUID of message channel + DWORD q1,q2,q3,q4; + + if (!gbDCMsgEnabled) + { // DC messaging disabled, close connection + NetLog_Direct("Messaging DC requested, denied"); + CloseDirectConnection(dc); + break; + } + +#ifdef _DEBUG + NetLog_Direct("Received PEER_MSG_INIT from %u",dc->dwRemoteUin); +#endif + buf++; + if (wLen != 0x21) + break; + + if (!dc->handshake) + { + NetLog_Direct("Received %s on unitialised DC, ignoring.", "PEER_MSG_INIT"); + break; + } + + buf += 4; /* always 10 */ + buf += 4; /* some id */ + buf += 4; /* sequence - always 0 on incoming */ + unpackDWord(&buf, &q1); // session type GUID + unpackDWord(&buf, &q2); + if (!dc->incoming) + { // skip marker on sequence 1 + buf += 4; + } + unpackDWord(&buf, &q3); + unpackDWord(&buf, &q4); + if (!CompareGUIDs(q1,q2,q3,q4,PSIG_MESSAGE)) + { // This is not for normal messages, useless so kill. + if (CompareGUIDs(q1,q2,q3,q4,PSIG_STATUS_PLUGIN)) + { + NetLog_Direct("Status Manager Plugin connections not supported, closing."); + } + else if (CompareGUIDs(q1,q2,q3,q4,PSIG_INFO_PLUGIN)) + { + NetLog_Direct("Info Manager Plugin connection not supported, closing."); + } + else + { + NetLog_Direct("Unknown connection type init, closing."); + } + CloseDirectConnection(dc); + break; + } + + if (dc->incoming) + { // reply with our PEER_MSG_INIT + sendPeerMsgInit(dc, 1); + } + else + { // connection initialized, ready to send message packet + } + NetLog_Direct("Direct message session ready."); + dc->initialised = 1; + } + break; + + default: + NetLog_Direct("Unknown direct packet ignored."); + break; + } +} + + + +void EncryptDirectPacket(directconnect* dc, icq_packet* p) +{ + unsigned long B1; + unsigned long M1; + unsigned long check; + unsigned int i; + unsigned char X1; + unsigned char X2; + unsigned char X3; + unsigned char* buf = (unsigned char*)(p->pData + 3); + unsigned char bak[6]; + unsigned long offset; + unsigned long key; + unsigned long hex; + unsigned long size = p->wLen - 1; + + + if (dc->wVersion < 4) + return; // no encryption necessary. + + + switch (dc->wVersion) + { + case 4: + case 5: + offset = 6; + break; + + case 6: + case 7: + case 8: + case 9: + case 10: + default: + offset = 0; + } + + // calculate verification data + M1 = (rand() % ((size < 255 ? size : 255)-10))+10; + X1 = buf[M1] ^ 0xFF; + X2 = rand() % 220; + X3 = client_check_data[X2] ^ 0xFF; + if (offset) + { + memcpy(bak, buf, sizeof(bak)); + B1 = (buf[offset+4]<<24) | (buf[offset+6]<<16) | (buf[2]<<8) | buf[0]; + } + else + { + B1 = (buf[4]<<24) | (buf[6]<<16) | (buf[4]<<8) | (buf[6]); + } + + // calculate checkcode + check = (M1<<24) | (X1<<16) | (X2<<8) | X3; + check ^= B1; + + // main XOR key + key = 0x67657268 * size + check; + + // XORing the actual data + for (i = 0; i<(size+3)/4; i+=4) + { + hex = key + client_check_data[i&0xFF]; + *(PDWORD)(buf + i) ^= hex; + } + + // in TCPv4 are the first 6 bytes unencrypted + // so restore them + if (offset) + memcpy(buf, bak, sizeof(bak)); + + // storing the checkcode + *(PDWORD)(buf + offset) = check; +} + + + +static int DecryptDirectPacket(directconnect* dc, PBYTE buf, WORD wLen) +{ + unsigned long hex; + unsigned long key; + unsigned long B1; + unsigned long M1; + unsigned long check; + unsigned int i; + unsigned char X1; + unsigned char X2; + unsigned char X3; + unsigned char bak[6]; + unsigned long size = wLen; + unsigned long offset; + + + if (dc->wVersion < 4) + return 1; // no decryption necessary. + + if (size < 4) + return 1; + + if (dc->wVersion < 4) + return 1; + + if (dc->wVersion == 4 || dc->wVersion == 5) + { + offset = 6; + } + else + { + offset = 0; + } + + // backup the first 6 bytes + if (offset) + memcpy(bak, buf, sizeof(bak)); + + // retrieve checkcode + check = *(PDWORD)(buf+offset); + + // main XOR key + key = 0x67657268 * size + check; + + for (i=4; i<(size+3)/4; i+=4) + { + hex = key + client_check_data[i&0xFF]; + *(PDWORD)(buf + i) ^= hex; + } + + // retrive validate data + if (offset) + { + // in TCPv4 are the first 6 bytes unencrypted + // so restore them + memcpy(buf, bak, sizeof(bak)); + B1 = (buf[offset+4]<<24) | (buf[offset+6]<<16) | (buf[2]<<8) | buf[0]; + } + else + { + B1 = (buf[4]<<24) | (buf[6]<<16) | (buf[4]<<8) | (buf[6]<<0); + } + + // special decryption + B1 ^= check; + + // validate packet + M1 = (B1>>24) & 0xFF; + if (M1 < 10 || M1 >= size) + { + return 0; + } + + X1 = buf[M1] ^ 0xFF; + if(((B1 >> 16) & 0xFF) != X1) + { + return 0; + } + + X2 = (BYTE)((B1 >> 8) & 0xFF); + if (X2 < 220) + { + X3 = client_check_data[X2] ^ 0xFF; + if ((B1 & 0xFF) != X3) + { + return 0; + } + } +#ifdef _DEBUG + { // log decrypted data + char szTitleLine[128]; + char* szBuf; + int titleLineLen; + int line; + int col; + int colsInLine; + char* pszBuf; + + + titleLineLen = null_snprintf(szTitleLine, 128, "DECRYPTED\n"); + szBuf = (char*)_alloca(titleLineLen + ((wLen+15)>>4) * 76 + 1); + CopyMemory(szBuf, szTitleLine, titleLineLen); + pszBuf = szBuf + titleLineLen; + + for (line = 0; ; line += 16) + { + colsInLine = min(16, wLen - line); + pszBuf += wsprintf(pszBuf, "%08X: ", line); + + for (col = 0; colwVersion); // Version + packLEWord(&packet, 43); // Data length + packLEDWord(&packet, dc->dwRemoteUin); // UIN of remote user + packWord(&packet, 0); // Unknown + packLEDWord(&packet, wListenPort); // Our port + packLEDWord(&packet, dwLocalUIN); // Our UIN + packDWord(&packet, dc->dwLocalExternalIP); // Our external IP + packDWord(&packet, dc->dwLocalInternalIP); // Our internal IP + packByte(&packet, DC_TYPE); // TCP connection flags + packLEDWord(&packet, wListenPort); // Our port + packLEDWord(&packet, dc->dwConnCookie); // DC cookie + packLEDWord(&packet, WEBFRONTPORT); // Unknown + packLEDWord(&packet, CLIENTFEATURES); // Unknown + if (dc->type == DIRECTCONN_REVERSE) + packLEDWord(&packet, dc->dwReqId); // Reverse Request Cookie + else + packDWord(&packet, 0); // Unknown + + sendDirectPacket(dc, &packet); +#ifdef _DEBUG + NetLog_Direct("Sent PEER_INIT to %u on %s DC", dc->dwRemoteUin, dc->incoming?"incoming":"outgoing"); +#endif +} + + + +// Sends a PEER_INIT packet through a DC +// ----------------------------------------------------------------------- +// This is sent to acknowledge a PEER_INIT packet. +static void sendPeerInitAck(directconnect* dc) +{ + icq_packet packet; + + directPacketInit(&packet, 4); // Packet length + packLEDWord(&packet, PEER_INIT_ACK); // + + sendDirectPacket(dc, &packet); +#ifdef _DEBUG + NetLog_Direct("Sent PEER_INIT_ACK to %u on %s DC", dc->dwRemoteUin, dc->incoming?"incoming":"outgoing"); +#endif +} + + + +// Sends a PEER_MSG_INIT packet through a DC +// ----------------------------------------------------------------------- +// This packet starts message session. +static void sendPeerMsgInit(directconnect* dc, DWORD dwSeq) +{ + icq_packet packet; + + directPacketInit(&packet, 33); + packByte(&packet, PEER_MSG_INIT); + packLEDWord(&packet, 10); // unknown + packLEDWord(&packet, 1); // message connection + packLEDWord(&packet, dwSeq); // sequence is 0,1 + if (!dwSeq) + { + packGUID(&packet, PSIG_MESSAGE); // message type GUID + packLEWord(&packet, 1); // delimiter + packLEWord(&packet, 4); + } + else + { + packDWord(&packet, 0); // first part of Message GUID + packDWord(&packet, 0); + packLEWord(&packet, 1); // delimiter + packLEWord(&packet, 4); + packDWord(&packet, 0); // second part of Message GUID + packDWord(&packet, 0); + } + sendDirectPacket(dc, &packet); +#ifdef _DEBUG + NetLog_Direct("Sent PEER_MSG_INIT to %u on %s DC", dc->dwRemoteUin, dc->incoming?"incoming":"outgoing"); +#endif +} + + + +// Sends a PEER_FILE_INIT packet through a DC +// ----------------------------------------------------------------------- +// This packet configures file-transfer session. +static void sendPeerFileInit(directconnect* dc) +{ + icq_packet packet; + DBVARIANT dbv; + char* szNick; + int nNickLen; + + dbv.type = DBVT_DELETED; + if (ICQGetContactSetting(NULL, "Nick", &dbv)) + szNick = ""; + else + szNick = dbv.pszVal; + nNickLen = strlennull(szNick); + + directPacketInit(&packet, (WORD)(20 + nNickLen)); + packByte(&packet, PEER_FILE_INIT); /* packet type */ + packLEDWord(&packet, 0); /* unknown */ + packLEDWord(&packet, dc->ft->dwFileCount); + packLEDWord(&packet, dc->ft->dwTotalSize); + packLEDWord(&packet, dc->ft->dwTransferSpeed); + packLEWord(&packet, (WORD)(nNickLen + 1)); + packBuffer(&packet, szNick, (WORD)(nNickLen + 1)); + sendDirectPacket(dc, &packet); +#ifdef _DEBUG + NetLog_Direct("Sent PEER_FILE_INIT to %u on %s DC", dc->dwRemoteUin, dc->incoming?"incoming":"outgoing"); +#endif + ICQFreeVariant(&dbv); +} -- cgit v1.2.3