filetransfer* CMsnProto::p2p_getSessionByCallID(const char* CallID, const char* wlid) { if (CallID == NULL) return NULL; EnterCriticalSection(&sessionLock); filetransfer* ft = NULL; char* szEmail = NULL; for (int i=0; i < sessionList.getCount(); i++) { filetransfer* FT = &sessionList[i]; if (FT->p2p_callID && !_stricmp(FT->p2p_callID, CallID)) { if (_stricmp(FT->p2p_dest, wlid)) { if (!szEmail) parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL); if (_stricmp(FT->p2p_dest, szEmail)) continue; } ft = FT; break; } } LeaveCriticalSection(&sessionLock); if (ft == NULL) MSN_DebugLog("Ignoring unknown session call id %s", CallID); return ft; }
ThreadData::~ThreadData() { if ( s != NULL ) { MSN_DebugLog( "Closing connection handle %08X", s ); Netlib_CloseHandle( s ); } if ( mIncomingBoundPort != NULL ) { Netlib_CloseHandle( mIncomingBoundPort ); } if ( mMsnFtp != NULL ) { delete mMsnFtp; mMsnFtp = NULL; } if ( hWaitEvent != INVALID_HANDLE_VALUE ) CloseHandle( hWaitEvent ); p2p_clearDormantSessions(); free( mJoinedContacts ); while (mFirstQueueItem != NULL) { TQueueItem* QI = mFirstQueueItem; mFirstQueueItem = mFirstQueueItem->next; free(QI); } }
void CMsnProto::DecryptEchoPacket(UDPProbePkt& pkt) { pkt.clientPort ^= 0x3141; pkt.discardPort ^= 0x3141; pkt.testPort ^= 0x3141; pkt.clientIP ^= 0x31413141; pkt.testIP ^= 0x31413141; IN_ADDR addr; MSN_DebugLog("Echo packet: version: 0x%x service code: 0x%x transaction ID: 0x%x", pkt.version, pkt.serviceCode, pkt.trId); addr.S_un.S_addr = pkt.clientIP; MSN_DebugLog("Echo packet: client port: %u client addr: %s", pkt.clientPort, inet_ntoa(addr)); addr.S_un.S_addr = pkt.testIP; MSN_DebugLog("Echo packet: discard port: %u test port: %u test addr: %s", pkt.discardPort, pkt.testPort, inet_ntoa(addr)); }
int __cdecl CMsnProto::SetStatus(int iNewStatus) { if (m_iDesiredStatus == iNewStatus) return 0; m_iDesiredStatus = iNewStatus; MSN_DebugLog("PS_SETSTATUS(%d,0)", iNewStatus); if (m_iDesiredStatus == ID_STATUS_OFFLINE) { if (msnNsThread) msnNsThread->sendTerminate(); } else if (!msnLoggedIn && m_iStatus == ID_STATUS_OFFLINE) { char szPassword[100]; int ps = getStaticString(NULL, "Password", szPassword, sizeof(szPassword)); if (ps != 0 || *szPassword == 0) { SendBroadcast(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD); m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; return 0; } if (*MyOptions.szEmail == 0) { SendBroadcast(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID); m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; return 0; } sessionList.destroy(); dcList.destroy(); usingGateway = false; int oldMode = m_iStatus; m_iStatus = ID_STATUS_CONNECTING; SendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldMode, m_iStatus); ThreadData* newThread = new ThreadData; newThread->mType = SERVER_NOTIFICATION; newThread->mIsMainThread = true; newThread->startThread(&CMsnProto::MSNServerThread, this); } else if (m_iStatus > ID_STATUS_OFFLINE) MSN_SetServerStatus(m_iDesiredStatus); return 0; }
void ThreadData::applyGatewayData( HANDLE hConn, bool isPoll ) { char szHttpPostUrl[300]; getGatewayUrl( szHttpPostUrl, sizeof( szHttpPostUrl ), isPoll ); MSN_DebugLog( "applying '%s' to %08X [%d]", szHttpPostUrl, this, GetCurrentThreadId() ); NETLIBHTTPPROXYINFO nlhpi = {0}; nlhpi.cbSize = sizeof(nlhpi); nlhpi.flags = NLHPIF_HTTP11; nlhpi.szHttpGetUrl = NULL; nlhpi.szHttpPostUrl = szHttpPostUrl; nlhpi.firstPostSequence = 1; MSN_CallService( MS_NETLIB_SETHTTPPROXYINFO, (WPARAM)hConn, (LPARAM)&nlhpi); }
void __cdecl msn_keepAliveThread( void* ) { msnPingTimeout = msnPingTimeoutCurrent; while( TRUE ) { while ( --msnPingTimeout > 0 ) { if ( ::WaitForSingleObject( hKeepAliveThreadEvt, 1000 ) != WAIT_TIMEOUT ) { ::CloseHandle( hKeepAliveThreadEvt ); hKeepAliveThreadEvt = NULL; MSN_DebugLog( "Closing keep-alive thread" ); return; } } msnPingTimeout = msnPingTimeoutCurrent = 45; /* * if proxy is not used, every connection uses select() to send PNG */ if ( msnLoggedIn && !MyOptions.UseGateway ) if ( MSN_GetByte( "KeepAlive", 0 )) msnNsThread->send( "PNG\r\n", 5 ); } }
filetransfer* CMsnProto::p2p_getSessionByUniqueID(unsigned id) { if (id == 0) return NULL; filetransfer* ft = NULL; EnterCriticalSection(&sessionLock); for (int i=0; i < sessionList.getCount(); i++) { filetransfer* FT = &sessionList[i]; if (FT->p2p_acksessid == id) { ft = FT; break; } } LeaveCriticalSection(&sessionLock); if (ft == NULL) MSN_DebugLog("Ignoring unknown unique id %08x", id); return ft; }
void CMsnProto::MSNatDetect(void) { unsigned i; PHOSTENT host = gethostbyname("echo.edge.messenger.live.com"); if (host == NULL) { MSN_DebugLog("P2PNAT could not find echo server \"echo.edge.messenger.live.com\""); return; } SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = _htons(7001); addr.sin_addr = *( PIN_ADDR )host->h_addr_list[0]; MSN_DebugLog("P2PNAT Detected echo server IP %d.%d.%d.%d", addr.sin_addr.S_un.S_un_b.s_b1, addr.sin_addr.S_un.S_un_b.s_b2, addr.sin_addr.S_un.S_un_b.s_b3, addr.sin_addr.S_un.S_un_b.s_b4); SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); connect(s, (SOCKADDR*)&addr, sizeof(addr)); UDPProbePkt pkt = { 0 }; UDPProbePkt pkt2; // Detect My IP pkt.version = 2; pkt.serviceCode = 4; send(s, (char*)&pkt, sizeof(pkt), 0); SOCKADDR_IN myaddr; int szname = sizeof(myaddr); getsockname(s, (SOCKADDR*)&myaddr, &szname); MyConnection.intIP = myaddr.sin_addr.S_un.S_addr; MSN_DebugLog("P2PNAT Detected IP facing internet %d.%d.%d.%d", myaddr.sin_addr.S_un.S_un_b.s_b1, myaddr.sin_addr.S_un.S_un_b.s_b2, myaddr.sin_addr.S_un.S_un_b.s_b3, myaddr.sin_addr.S_un.S_un_b.s_b4); SOCKET s1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); pkt.version = 2; pkt.serviceCode = 1; pkt.clientPort = 0x3141; pkt.clientIP = 0x31413141; UDPProbePkt rpkt = {0}; // NAT detection for (i=0; i<4; ++i) { if (Miranda_Terminated()) break; // Send echo request to server 1 MSN_DebugLog("P2PNAT Request 1 attempt %d sent", i); sendto(s1, (char*)&pkt, sizeof(pkt), 0, (SOCKADDR*)&addr, sizeof(addr)); fd_set fd; FD_ZERO(&fd); FD_SET(s1, &fd); TIMEVAL tv = {0, 200000 * (1 << i) }; if (select(1, &fd, NULL, NULL, &tv) == 1) { MSN_DebugLog("P2PNAT Request 1 attempt %d response", i); recv(s1, (char*)&rpkt, sizeof(rpkt), 0); pkt2 = rpkt; DecryptEchoPacket(rpkt); break; } else MSN_DebugLog("P2PNAT Request 1 attempt %d timeout", i); } closesocket(s); // Server did not respond if (i >= 4) { MyConnection.udpConType = conFirewall; closesocket(s1); return; } MyConnection.extIP = rpkt.clientIP; // Check if NAT not found if (MyConnection.extIP == MyConnection.intIP) { if (msnExternalIP != NULL && inet_addr(msnExternalIP) != MyConnection.extIP) MyConnection.udpConType = conISALike; else MyConnection.udpConType = conDirect; closesocket(s1); return; } // Detect UPnP NAT NETLIBBIND nlb = {0}; nlb.cbSize = sizeof(nlb); nlb.pfnNewConnectionV2 = MSN_ConnectionProc; nlb.pExtra = this; HANDLE sb = (HANDLE) MSN_CallService(MS_NETLIB_BINDPORT, (WPARAM)hNetlibUser, (LPARAM)&nlb); if ( sb != NULL ) { MyConnection.upnpNAT = htonl(nlb.dwExternalIP) == MyConnection.extIP; Sleep(100); Netlib_CloseHandle(sb); } DiscardExtraPackets(s1); // Start IP Restricted NAT detection UDPProbePkt rpkt2 = {0}; pkt2.serviceCode = 3; SOCKADDR_IN addr2 = addr; addr2.sin_addr.S_un.S_addr = rpkt.testIP; addr2.sin_port = rpkt.discardPort; for (i=0; i<4; ++i) { if (Miranda_Terminated()) break; MSN_DebugLog("P2PNAT Request 2 attempt %d sent", i); // Remove IP restriction for server 2 sendto(s1, NULL, 0, 0, (SOCKADDR*)&addr2, sizeof(addr2)); // Send echo request to server 1 for server 2 sendto(s1, (char*)&pkt2, sizeof(pkt2), 0, (SOCKADDR*)&addr, sizeof(addr)); fd_set fd; FD_ZERO(&fd); FD_SET(s1, &fd); TIMEVAL tv = {0, 200000 * (1 << i) }; if (select(1, &fd, NULL, NULL, &tv) == 1) { MSN_DebugLog("P2PNAT Request 2 attempt %d response", i); recv(s1, (char*)&rpkt2, sizeof(rpkt2), 0); DecryptEchoPacket(rpkt2); break; } else MSN_DebugLog("P2PNAT Request 2 attempt %d timeout", i); } // Response recieved so it's an IP Restricted NAT (Restricted Cone NAT) // (MSN does not detect Full Cone NAT and consider it as IP Restricted NAT) if (i < 4) { MyConnection.udpConType = conIPRestrictNAT; closesocket(s1); return; } DiscardExtraPackets(s1); // Symmetric NAT detection addr2.sin_port = rpkt.testPort; for (i=0; i<4; ++i) { if (Miranda_Terminated()) break; MSN_DebugLog("P2PNAT Request 3 attempt %d sent", i); // Send echo request to server 1 sendto(s1, (char*)&pkt, sizeof(pkt), 0, (SOCKADDR*)&addr2, sizeof(addr2)); fd_set fd; FD_ZERO(&fd); FD_SET(s1, &fd); TIMEVAL tv = {1 << i, 0 }; if ( select(1, &fd, NULL, NULL, &tv) == 1 ) { MSN_DebugLog("P2PNAT Request 3 attempt %d response", i); recv(s1, (char*)&rpkt2, sizeof(rpkt2), 0); DecryptEchoPacket(rpkt2); break; } else MSN_DebugLog("P2PNAT Request 3 attempt %d timeout", i); } if (i < 4) { // If ports different it's symmetric NAT MyConnection.udpConType = rpkt.clientPort == rpkt2.clientPort ? conPortRestrictNAT : conSymmetricNAT; } closesocket(s1); }
void CMsnProto::MSNConnDetectThread( void* ) { char parBuf[512] = ""; memset(&MyConnection, 0, sizeof(MyConnection)); MyConnection.icf = IsIcfEnabled(); bool portsMapped = getByte("NLSpecifyIncomingPorts", 0) != 0; unsigned gethst = getByte("AutoGetHost", 1); switch (gethst) { case 0: MSN_DebugLog("P2PNAT User overwrote IP connection is guessed by user settings only"); // User specified host by himself so check if it matches MSN information // if it does, move to connection type autodetection, // if it does not, guess connection type from available info getStaticString(NULL, "YourHost", parBuf, sizeof(parBuf)); if (msnExternalIP == NULL || strcmp(msnExternalIP, parBuf) != 0) { MyConnection.extIP = inet_addr(parBuf); if (MyConnection.extIP == INADDR_NONE) { PHOSTENT myhost = gethostbyname(parBuf); if (myhost != NULL) MyConnection.extIP = ((PIN_ADDR)myhost->h_addr)->S_un.S_addr; else setByte("AutoGetHost", 1); } if (MyConnection.extIP != INADDR_NONE) { MyConnection.intIP = MyConnection.extIP; MyConnection.udpConType = MyConnection.extIP ? (ConEnum)portsMapped : conUnknown; MyConnection.CalculateWeight(); return; } else MyConnection.extIP = 0; } break; case 1: if (msnExternalIP != NULL) MyConnection.extIP = inet_addr(msnExternalIP); else { gethostname(parBuf, sizeof(parBuf)); PHOSTENT myhost = gethostbyname(parBuf); if (myhost != NULL) MyConnection.extIP = ((PIN_ADDR)myhost->h_addr)->S_un.S_addr; } MyConnection.intIP = MyConnection.extIP; break; case 2: MyConnection.udpConType = conUnknown; MyConnection.CalculateWeight(); return; } if (getByte( "NLSpecifyOutgoingPorts", 0)) { // User specified outgoing ports so the connection must be firewalled // do not autodetect and guess connection type from available info MyConnection.intIP = MyConnection.extIP; MyConnection.udpConType = (ConEnum)portsMapped; MyConnection.upnpNAT = false; MyConnection.CalculateWeight(); return; } MSNatDetect(); // If user mapped incoming ports consider direct connection if (portsMapped) { MSN_DebugLog("P2PNAT User manually mapped ports for incoming connection"); switch(MyConnection.udpConType) { case conUnknown: case conFirewall: case conISALike: MyConnection.udpConType = conDirect; break; case conUnknownNAT: case conPortRestrictNAT: case conIPRestrictNAT: case conSymmetricNAT: MyConnection.upnpNAT = true; break; } } MSN_DebugLog("P2PNAT Connection %s found UPnP: %d ICF: %d", conStr[MyConnection.udpConType], MyConnection.upnpNAT, MyConnection.icf); MyConnection.CalculateWeight(); }
void __cdecl MSNServerThread( ThreadData* info ) { if ( !sttRedirectorWasChecked ) { sttRedirectorWasChecked = true; MSN_StartThread(( pThreadFunc )msn_RedirectorThread, NULL ); } NETLIBOPENCONNECTION tConn = { 0 }; tConn.cbSize = sizeof( tConn ); tConn.flags = NLOCF_V2; char* tPortDelim = strrchr( info->mServer, ':' ); if ( tPortDelim != NULL ) *tPortDelim = '\0'; if ( MyOptions.UseGateway && !MyOptions.UseProxy ) { tConn.szHost = MSN_DEFAULT_GATEWAY; tConn.wPort = 80; } else { tConn.szHost = info->mServer; tConn.wPort = MSN_DEFAULT_PORT; if ( tPortDelim != NULL ) { int tPortNumber; if ( sscanf( tPortDelim+1, "%d", &tPortNumber ) == 1 ) tConn.wPort = ( WORD )tPortNumber; } } MSN_DebugLog( "Thread started: server='%s', type=%d", tConn.szHost, info->mType ); info->s = ( HANDLE )MSN_CallService( MS_NETLIB_OPENCONNECTION, ( WPARAM )hNetlibUser, ( LPARAM )&tConn ); if ( info->s == NULL ) { MSN_DebugLog( "Connection Failed (%d)", WSAGetLastError() ); switch ( info->mType ) { case SERVER_NOTIFICATION: case SERVER_DISPATCH: MSN_SendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NOSERVER ); MSN_GoOffline(); break; } return; } if ( MyOptions.UseGateway ) MSN_CallService( MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM( info->s ), 2 ); MSN_DebugLog( "Connected with handle=%08X", info->s ); if ( info->mType == SERVER_DISPATCH || info->mType == SERVER_NOTIFICATION ) { if ( MyOptions.UseMSNP11 ) info->sendPacket( "VER", "MSNP11 MSNP10 CVR0" ); else info->sendPacket( "VER", "MSNP10 MSNP9 CVR0" ); } else if ( info->mType == SERVER_SWITCHBOARD ) { char tEmail[ MSN_MAX_EMAIL_LEN ]; MSN_GetStaticString( "e-mail", NULL, tEmail, sizeof( tEmail )); info->sendPacket( info->mCaller ? "USR" : "ANS", "%s %s", tEmail, info->mCookie ); } else if ( info->mType == SERVER_FILETRANS && info->mCaller == 0 ) { info->send( "VER MSNFTP\r\n", 12 ); } if ( info->mIsMainThread ) { MSN_EnableMenuItems( TRUE ); msnPingTimeout = msnPingTimeoutCurrent; msnNsThread = info; if (hKeepAliveThreadEvt == NULL) { hKeepAliveThreadEvt = ::CreateEvent( NULL, TRUE, FALSE, NULL ); MSN_StartThread(( pThreadFunc )msn_keepAliveThread, NULL ); } } MSN_DebugLog( "Entering main recv loop" ); info->mBytesInData = 0; while ( TRUE ) { int handlerResult; int recvResult = info->recv( info->mData + info->mBytesInData, sizeof( info->mData ) - info->mBytesInData ); if ( recvResult == SOCKET_ERROR ) { MSN_DebugLog( "Connection %08p [%d] was abortively closed", info->s, GetCurrentThreadId()); break; } if ( !recvResult ) { MSN_DebugLog( "Connection %08p [%d] was gracefully closed", info->s, GetCurrentThreadId()); break; } info->mBytesInData += recvResult; if ( info->mCaller == 1 && info->mType == SERVER_FILETRANS ) { handlerResult = MSN_HandleMSNFTP( info, info->mData ); if ( handlerResult ) break; } else { while( TRUE ) { char* peol = strchr(info->mData,'\r'); if ( peol == NULL ) break; if ( info->mBytesInData < peol-info->mData+2 ) break; //wait for full line end char msg[ sizeof(info->mData) ]; memcpy( msg, info->mData, peol-info->mData ); msg[ peol-info->mData ] = 0; if ( *++peol != '\n' ) MSN_DebugLog( "Dodgy line ending to command: ignoring" ); else peol++; info->mBytesInData -= peol - info->mData; memmove( info->mData, peol, info->mBytesInData ); MSN_DebugLog( "RECV:%s", msg ); if ( !isalnum( msg[0] ) || !isalnum(msg[1]) || !isalnum(msg[2]) || (msg[3] && msg[3]!=' ')) { MSN_DebugLog( "Invalid command name" ); continue; } if ( info->mType != SERVER_FILETRANS ) { if ( isdigit(msg[0]) && isdigit(msg[1]) && isdigit(msg[2])) //all error messages handlerResult = MSN_HandleErrors( info, msg ); else handlerResult = MSN_HandleCommands( info, msg ); } else handlerResult = MSN_HandleMSNFTP( info, msg ); if ( handlerResult ) goto LBL_Exit; } } if ( info->mBytesInData == sizeof( info->mData )) { MSN_DebugLog( "sizeof(data) is too small: the longest line won't fit" ); break; } } LBL_Exit: if ( info->mIsMainThread ) { MSN_GoOffline(); msnNsThread = NULL; if ( hKeepAliveThreadEvt ) SetEvent( hKeepAliveThreadEvt ); } MSN_DebugLog( "Thread [%d] ending now", GetCurrentThreadId() ); }