From 6860b3202f3c8218288fad2778529ba76463015c Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sat, 13 Oct 2012 09:07:09 +0000 Subject: MSN: folders restructurization git-svn-id: http://svn.miranda-ng.org/main/trunk@1901 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/MSN/src/msn_natdetect.cpp | 494 ++++++++++++++++++++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 protocols/MSN/src/msn_natdetect.cpp (limited to 'protocols/MSN/src/msn_natdetect.cpp') diff --git a/protocols/MSN/src/msn_natdetect.cpp b/protocols/MSN/src/msn_natdetect.cpp new file mode 100644 index 0000000000..0d17f4a530 --- /dev/null +++ b/protocols/MSN/src/msn_natdetect.cpp @@ -0,0 +1,494 @@ +/* +Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. +Copyright (c) 2007-2012 Boris Krasnovskiy. + +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, see . +*/ + +#include "msn_global.h" +#include "msn_proto.h" +#include "SDK/netfw.h" + +#ifndef CLSID_NetFwMgr +#define MDEF_CLSID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const CLSID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + + MDEF_CLSID(CLSID_NetFwMgr, 0x304ce942, 0x6e39, 0x40d8, 0x94, 0x3a, 0xb9, 0x13, 0xc4, 0x0c, 0x9c, 0xd4); + MDEF_CLSID(IID_INetFwMgr, 0xf7898af5, 0xcac4, 0x4632, 0xa2, 0xec, 0xda ,0x06, 0xe5, 0x11, 0x1a, 0xf2); +#endif + + +MyConnectionType MyConnection; + +const char* conStr[] = +{ + "Unknown-Connect", + "Direct-Connect", + "Unknown-NAT", + "IP-Restrict-NAT", + "Port-Restrict-NAT", + "Symmetric-NAT", + "Firewall", + "ISALike" +}; + + +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)); +} + + +static void DiscardExtraPackets(SOCKET s) +{ + Sleep(3000); + + static const TIMEVAL tv = {0, 0}; + unsigned buf; + + for (;;) + { + if (Miranda_Terminated()) break; + + fd_set fd; + FD_ZERO(&fd); + FD_SET(s, &fd); + + if (select(1, &fd, NULL, NULL, &tv) == 1) + recv(s, (char*)&buf, sizeof(buf), 0); + else + break; + } +} + + +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) 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); +} + + +static bool IsIcfEnabled(void) +{ + HRESULT hr; + VARIANT_BOOL fwEnabled = VARIANT_FALSE; + + INetFwProfile* fwProfile = NULL; + INetFwMgr* fwMgr = NULL; + INetFwPolicy* fwPolicy = NULL; + INetFwAuthorizedApplication* fwApp = NULL; + INetFwAuthorizedApplications* fwApps = NULL; + BSTR fwBstrProcessImageFileName = NULL; + wchar_t *wszFileName = NULL; + + hr = CoInitialize(NULL); + if (FAILED(hr)) return false; + + // Create an instance of the firewall settings manager. + hr = CoCreateInstance(CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, + IID_INetFwMgr, (void**)&fwMgr ); + if (FAILED(hr)) goto error; + + // Retrieve the local firewall policy. + hr = fwMgr->get_LocalPolicy(&fwPolicy); + if (FAILED(hr)) goto error; + + // Retrieve the firewall profile currently in effect. + hr = fwPolicy->get_CurrentProfile(&fwProfile); + if (FAILED(hr)) goto error; + + // Get the current state of the firewall. + hr = fwProfile->get_FirewallEnabled(&fwEnabled); + if (FAILED(hr)) goto error; + + if (fwEnabled == VARIANT_FALSE) goto error; + + // Retrieve the authorized application collection. + hr = fwProfile->get_AuthorizedApplications(&fwApps); + if (FAILED(hr)) goto error; + + TCHAR szFileName[MAX_PATH]; + GetModuleFileName(NULL, szFileName, SIZEOF(szFileName)); + + wszFileName = mir_t2u(szFileName); + + // Allocate a BSTR for the process image file name. + fwBstrProcessImageFileName = SysAllocString(wszFileName); + if (FAILED(hr)) goto error; + + // Attempt to retrieve the authorized application. + hr = fwApps->Item(fwBstrProcessImageFileName, &fwApp); + if (SUCCEEDED(hr)) + { + // Find out if the authorized application is enabled. + fwApp->get_Enabled(&fwEnabled); + fwEnabled = ~fwEnabled; + } + +error: + // Free the BSTR. + SysFreeString(fwBstrProcessImageFileName); + mir_free(wszFileName); + + // Release the authorized application instance. + if (fwApp != NULL) fwApp->Release(); + + // Release the authorized application collection. + if (fwApps != NULL) fwApps->Release(); + + // Release the firewall profile. + if (fwProfile != NULL) fwProfile->Release(); + + // Release the local firewall policy. + if (fwPolicy != NULL) fwPolicy->Release(); + + // Release the firewall settings manager. + if (fwMgr != NULL) fwMgr->Release(); + + CoUninitialize(); + + return fwEnabled != VARIANT_FALSE; +} + + +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 MyConnectionType::SetUdpCon(const char* str) +{ + for (unsigned i=0; i= conIPRestrictNAT && udpConType <= conSymmetricNAT) + weight = upnpNAT ? 5 : 2; + else if (udpConType == conUnknownNAT) + weight = upnpNAT ? 4 : 1; + else if (udpConType == conUnknown) weight = 1; + else if (udpConType == conFirewall) weight = 2; + else if (udpConType == conISALike) weight = 3; +} -- cgit v1.2.3