From bb3b89fd5d857d396050d02bc185d765760de6ff Mon Sep 17 00:00:00 2001 From: George Hazan Date: Tue, 8 Oct 2019 18:50:08 +0300 Subject: WebSocket related code moved to the core --- src/mir_app/src/mir_app.def | 3 + src/mir_app/src/mir_app64.def | 3 + src/mir_app/src/netlib_autoproxy.cpp | 365 +++++++++++ src/mir_app/src/netlib_bind.cpp | 330 ++++++++++ src/mir_app/src/netlib_http.cpp | 1146 ++++++++++++++++++++++++++++++++++ src/mir_app/src/netlib_httpproxy.cpp | 85 +++ src/mir_app/src/netlib_log.cpp | 546 ++++++++++++++++ src/mir_app/src/netlib_openconn.cpp | 889 ++++++++++++++++++++++++++ src/mir_app/src/netlib_opts.cpp | 518 +++++++++++++++ src/mir_app/src/netlib_pktrecver.cpp | 80 +++ src/mir_app/src/netlib_security.cpp | 363 +++++++++++ src/mir_app/src/netlib_sock.cpp | 290 +++++++++ src/mir_app/src/netlib_upnp.cpp | 813 ++++++++++++++++++++++++ src/mir_app/src/netlib_websocket.cpp | 185 ++++++ src/mir_app/src/netlibautoproxy.cpp | 365 ----------- src/mir_app/src/netlibbind.cpp | 330 ---------- src/mir_app/src/netlibhttp.cpp | 1146 ---------------------------------- src/mir_app/src/netlibhttpproxy.cpp | 85 --- src/mir_app/src/netliblog.cpp | 546 ---------------- src/mir_app/src/netlibopenconn.cpp | 889 -------------------------- src/mir_app/src/netlibopts.cpp | 518 --------------- src/mir_app/src/netlibpktrecver.cpp | 80 --- src/mir_app/src/netlibsecurity.cpp | 363 ----------- src/mir_app/src/netlibsock.cpp | 290 --------- src/mir_app/src/netlibupnp.cpp | 813 ------------------------ 25 files changed, 5616 insertions(+), 5425 deletions(-) create mode 100644 src/mir_app/src/netlib_autoproxy.cpp create mode 100644 src/mir_app/src/netlib_bind.cpp create mode 100644 src/mir_app/src/netlib_http.cpp create mode 100644 src/mir_app/src/netlib_httpproxy.cpp create mode 100644 src/mir_app/src/netlib_log.cpp create mode 100644 src/mir_app/src/netlib_openconn.cpp create mode 100644 src/mir_app/src/netlib_opts.cpp create mode 100644 src/mir_app/src/netlib_pktrecver.cpp create mode 100644 src/mir_app/src/netlib_security.cpp create mode 100644 src/mir_app/src/netlib_sock.cpp create mode 100644 src/mir_app/src/netlib_upnp.cpp create mode 100644 src/mir_app/src/netlib_websocket.cpp delete mode 100644 src/mir_app/src/netlibautoproxy.cpp delete mode 100644 src/mir_app/src/netlibbind.cpp delete mode 100644 src/mir_app/src/netlibhttp.cpp delete mode 100644 src/mir_app/src/netlibhttpproxy.cpp delete mode 100644 src/mir_app/src/netliblog.cpp delete mode 100644 src/mir_app/src/netlibopenconn.cpp delete mode 100644 src/mir_app/src/netlibopts.cpp delete mode 100644 src/mir_app/src/netlibpktrecver.cpp delete mode 100644 src/mir_app/src/netlibsecurity.cpp delete mode 100644 src/mir_app/src/netlibsock.cpp delete mode 100644 src/mir_app/src/netlibupnp.cpp (limited to 'src/mir_app') diff --git a/src/mir_app/src/mir_app.def b/src/mir_app/src/mir_app.def index 35f48d6460..21fcd8a36f 100644 --- a/src/mir_app/src/mir_app.def +++ b/src/mir_app/src/mir_app.def @@ -716,3 +716,6 @@ UnregisterHppLogger @786 _RegisterSrmmLog@12 @805 NONAME _UnregisterSrmmLog@4 @806 NONAME ?GetType@CRtfLogWindow@@UAEHXZ @807 NONAME +_WebSocket_Send@12 @808 NONAME +_WebSocket_InitHeader@12 @809 NONAME +_WebSocket_Connect@8 @810 NONAME diff --git a/src/mir_app/src/mir_app64.def b/src/mir_app/src/mir_app64.def index 95a66ca811..02356fb3bb 100644 --- a/src/mir_app/src/mir_app64.def +++ b/src/mir_app/src/mir_app64.def @@ -716,3 +716,6 @@ UnregisterHppLogger @786 RegisterSrmmLog @805 NONAME UnregisterSrmmLog @806 NONAME ?GetType@CRtfLogWindow@@UEAAHXZ @807 NONAME +WebSocket_Send @808 NONAME +WebSocket_InitHeader @809 NONAME +WebSocket_Connect @810 NONAME diff --git a/src/mir_app/src/netlib_autoproxy.cpp b/src/mir_app/src/netlib_autoproxy.cpp new file mode 100644 index 0000000000..a4753f8ecd --- /dev/null +++ b/src/mir_app/src/netlib_autoproxy.cpp @@ -0,0 +1,365 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +// local module data + +static char *szProxyHost[3]; +static LIST proxyBypass(5); + +static HMODULE hModJS; + +static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll; +static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll; +static pfnInternetGetProxyInfo pInternetGetProxyInfo; + +static bool bEnabled, bOneProxy; + +///////////////////////////////////////////////////////////////////////////////////////// + +static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf) +{ + NetlibUser nlu = {}; + nlu.handleType = NLH_USER; + nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS; + nlu.user.szSettingsModule = "(NULL)"; + nlu.toLog = 1; + + // initialize the netlib request + NETLIBHTTPREQUEST nlhr = {}; + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT; + nlhr.szUrl = szUrl; + + // download the page + NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr)); + if (nlhrReply) { + if (nlhrReply->resultCode == 200) { + buf.lpszScriptBuffer = nlhrReply->pData; + buf.dwScriptBufferSize = nlhrReply->dataLength + 1; + + nlhrReply->dataLength = 0; + nlhrReply->pData = nullptr; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps) +{ + bool noHttp = false; + bool usingSsl = false; + char szUrl[1024]; + + if ((nlc->nloc.flags & NLOCF_HTTP) && (nlc->nloc.flags & NLOCF_SSL) || nlc->nloc.wPort == 443 || forceHttps) { + mir_snprintf(szUrl, "https://%s", nlc->nloc.szHost); + usingSsl = true; + } + else if ((nlc->nloc.flags & NLOCF_HTTP)) + mir_snprintf(szUrl, "http://%s", nlc->nloc.szHost); + else { + strncpy_s(szUrl, nlc->nloc.szHost, _TRUNCATE); + noHttp = true; + } + + mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr; + nlc->wProxyPort = 0; + nlc->proxyType = 0; + + char *mt = NetlibGetIeProxy(szUrl); + char *m = NEWSTR_ALLOCA(mt); + mir_free(mt); + + if (m == nullptr) + return false; + + // if multiple servers, use the first one + char *c = strchr(m, ';'); if (c) *c = 0; + + // if 'direct' no proxy + if (_stricmp(lrtrim(m), "direct") == 0) + return false; + + // find proxy address + char *h = strchr(m, ' '); + if (h == nullptr) + return false; + + // find proxy port + *h = 0; ++h; + char *p = strchr(h, ':'); + if (p) { *p = 0; ++p; } + + lrtrim(h); ltrim(p); + if (_stricmp(m, "proxy") == 0 && h[0]) { + nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP; + nlc->wProxyPort = p ? atol(p) : 8080; + nlc->szProxyServer = mir_strdup(h); + } + else if (_stricmp(m, "socks") == 0 && h[0]) { + nlc->proxyType = PROXYTYPE_SOCKS4; + nlc->wProxyPort = p ? atol(p) : 1080; + nlc->szProxyServer = mir_strdup(h); + } + else if (_stricmp(m, "socks5") == 0 && h[0]) { + nlc->proxyType = PROXYTYPE_SOCKS5; + nlc->wProxyPort = p ? atol(p) : 1080; + nlc->szProxyServer = mir_strdup(h); + } + else return false; + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static char szAutoUrlStr[MAX_PATH] = ""; +static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 }; +static HANDLE hIeProxyMutex; +static bool bAutoProxyInit; + +static void NetlibInitAutoProxy(void) +{ + if (bAutoProxyInit) return; + + if (!hModJS) { + if (!(hModJS = LoadLibraryA("jsproxy.dll"))) + return; + + pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll"); + pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"); + pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo"); + } + + if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) { + abuf.dwStructSize = sizeof(abuf); + GetFile(szAutoUrlStr, abuf); + } + bAutoProxyInit = true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct IeProxyParam +{ + char *szUrl; + char *szHost; + char *szProxy; +}; + +static void __cdecl NetlibIeProxyThread(IeProxyParam *param) +{ + param->szProxy = nullptr; + + if (!bAutoProxyInit) { + WaitForSingleObject(hIeProxyMutex, INFINITE); + NetlibInitAutoProxy(); + ReleaseMutex(hIeProxyMutex); + } + + BOOL res; + char *loc = strstr(szAutoUrlStr, "file://"); + if (loc || strstr(szAutoUrlStr, "://") == nullptr) { + Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc); + loc = loc ? loc + 7 : szAutoUrlStr; + res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr); + } + else { + Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize); + if (abuf.dwScriptBufferSize) + res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf); + else + res = false; + } + + if (res) { + char proxyBuffer[1024]; + char *proxy = proxyBuffer; + DWORD dwProxyLen = sizeof(proxyBuffer); + + if (pInternetGetProxyInfo(param->szUrl, (DWORD)mir_strlen(param->szUrl), + param->szHost, (DWORD)mir_strlen(param->szHost), &proxy, &dwProxyLen)) + param->szProxy = mir_strdup(lrtrim(proxy)); + + Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost); + pInternetDeInitializeAutoProxyDll(nullptr, 0); + } + else Netlib_Logf(nullptr, "Autoproxy init failed"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* NetlibGetIeProxy(char *szUrl) +{ + char *res = nullptr, *szHost; + { + char* p = strstr(szUrl, "://"); + if (p) p += 3; else p = szUrl; + + szHost = NEWSTR_ALLOCA(p); + p = strchr(szHost, '/'); if (p) *p = 0; + p = strchr(szHost, ':'); if (p) *p = 0; + _strlwr(szHost); + } + + if (bEnabled) { + for (auto &p : proxyBypass) { + if (mir_strcmp(p, "") == 0) { + if (strchr(szHost, '.') == nullptr) + return nullptr; + } + else if (wildcmp(szHost, p)) + return nullptr; + } + + int ind = -1; + if (strstr(szUrl, "http://")) + ind = szProxyHost[0] ? 0 : 2; + else if (strstr(szUrl, "https://")) + ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2); + else + ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2)); + + if (ind < 0 || !szProxyHost[ind]) + return nullptr; + + size_t len = mir_strlen(szHost) + 20; + res = (char*)mir_alloc(len); + mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]); + return res; + } + + if (szAutoUrlStr[0]) { + IeProxyParam param = { szUrl, szHost, nullptr }; + HANDLE hThread = mir_forkThread(NetlibIeProxyThread, ¶m); + WaitForSingleObject(hThread, INFINITE); + res = param.szProxy; + } + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void NetlibLoadIeProxy(void) +{ + HKEY hSettings; + if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings)) + return; + + DWORD tValueLen, enabled = 0; + char szHostStr[256] = "", szProxyBypassStr[4096] = ""; + + tValueLen = sizeof(enabled); + int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (BYTE*)&enabled, &tValueLen); + bEnabled = enabled && tResult == ERROR_SUCCESS; + + tValueLen = _countof(szHostStr); + tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (BYTE*)szHostStr, &tValueLen); + bEnabled = bEnabled && tResult == ERROR_SUCCESS; + + tValueLen = _countof(szAutoUrlStr); + RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (BYTE*)szAutoUrlStr, &tValueLen); + + tValueLen = _countof(szProxyBypassStr); + RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (BYTE*)szProxyBypassStr, &tValueLen); + + RegCloseKey(hSettings); + + if (bEnabled) { + char *szProxy = ltrim(szHostStr); + if (szProxy[0] == 0) { + enabled = false; + return; + } + + while (true) { + char *szProxyEnd = strchr(szProxy, ';'); + if (szProxyEnd) + *szProxyEnd = 0; + + int ind = -1; + if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; } + else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; } + else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; } + else if (strchr(szProxy, '=')) ind = -2; + + if (ind != -2) { + bOneProxy = ind < 0; if (ind < 0) ind = 0; + + lrtrim(szProxy); + + if (strchr(szProxy, ':')) + szProxyHost[ind] = mir_strdup(szProxy); + else { + size_t len = mir_strlen(szProxy) + 10; + szProxyHost[ind] = (char*)mir_alloc(len); + mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080); + } + if (bOneProxy) + break; + } + if (szProxyEnd == nullptr) + break; + szProxy = szProxyEnd + 1; + } + + char *szProxyBypass = szProxyBypassStr; + while (true) { + char *szProxyBypassEnd = strchr(szProxyBypass, ';'); + if (szProxyBypassEnd) + *szProxyBypassEnd = 0; + + lrtrim(szProxyBypass); + + proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass))); + if (szProxyBypassEnd == nullptr) + break; + + szProxyBypass = szProxyBypassEnd + 1; + } + } + + if (bEnabled || szAutoUrlStr[0]) + hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr); +} + +void NetlibUnloadIeProxy(void) +{ + for (int i = 0; i < 3; i++) + mir_free(szProxyHost[i]); + + for (auto &p : proxyBypass) + mir_free(p); + + mir_free(abuf.lpszScriptBuffer); + + CloseHandle(hIeProxyMutex); +} diff --git a/src/mir_app/src/netlib_bind.cpp b/src/mir_app/src/netlib_bind.cpp new file mode 100644 index 0000000000..fcbdc7c943 --- /dev/null +++ b/src/mir_app/src/netlib_bind.cpp @@ -0,0 +1,330 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) +{ + SOCKADDR_IN sin = {0}; + sin.sin_family = AF_INET; + + SOCKADDR_IN6 sin6 = {0}; + sin6.sin6_family = AF_INET6; + + mir_cslock lck(csNetlibUser); + + if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { + BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); + if (*portn == 0) + return false; + + WORD num; + Utils_GetRandom(&num, sizeof(WORD)); + *portn = num % *portn; + } + + bool before = false; + while (true) { + const char *psz; + char *pszEnd; + int portMin, portMax, port, portnum = 0; + + for (psz = szPorts;*psz;) { + while (*psz == ' ' || *psz == ',') psz++; + portMin = strtol(psz, &pszEnd, 0); + if (pszEnd == psz) + break; + while (*pszEnd == ' ') + pszEnd++; + if (*pszEnd == '-') { + psz = pszEnd + 1; + portMax = strtol(psz, &pszEnd, 0); + if (pszEnd == psz) portMax = 65535; + if (portMin > portMax) { + port = portMin; + portMin = portMax; + portMax = port; + } + } + else portMax = portMin; + if (portMax >= 1) { + if (portMin <= 0) portMin = 1; + for (port = portMin; port <= portMax; port++) { + if (port > 65535) + break; + + ++portnum; + + if (s == INVALID_SOCKET) continue; + if (!before && portnum <= *portn) continue; + if (before && portnum >= *portn) + return false; + + sin.sin_port = htons((WORD)port); + bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; + + sin6.sin6_port = htons((WORD)port); + bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; + + if (bV4Mapped && bV6Mapped) { + *portn = portnum + 1; + return true; + } + } + } + psz = pszEnd; + } + + if (*portn < 0) { + *portn = portnum; + return true; + } + + if (*portn >= portnum) + *portn = 0; + else + before = true; + } +} + +int NetlibFreeBoundPort(NetlibBoundPort *nlbp) +{ + NETLIBCONNECTIONEVENTINFO ncei; + + ZeroMemory(&ncei, sizeof(ncei)); + ncei.connected = 0; + ncei.listening = 1; + ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule; + int size = sizeof(SOCKADDR_IN); + getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size); + NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0); + + nlbp->close(); + if (nlbp->hThread) + WaitForSingleObject(nlbp->hThread, INFINITE); + Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); + delete nlbp; + return 1; +} + +static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp) +{ + Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort); + + while (true) { + fd_set r; + FD_ZERO(&r); + if (nlbp->s != INVALID_SOCKET) + FD_SET(nlbp->s, &r); + if (nlbp->s6 != INVALID_SOCKET) + FD_SET(nlbp->s6, &r); + if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) { + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError()); + break; + } + + sockaddr_in sin; + int sinLen = sizeof(sin); + memset(&sin, 0, sizeof(sin)); + + SOCKET s; + if (FD_ISSET(nlbp->s, &r)) { + s = accept(nlbp->s, (sockaddr*)&sin, &sinLen); + if (s == INVALID_SOCKET) { + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError()); + break; + } + } + else if (FD_ISSET(nlbp->s6, &r)) { + s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen); + if (s == INVALID_SOCKET) { + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError()); + break; + } + } + else s = 0; + + Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s); + + NetlibConnection *nlc = new NetlibConnection(); + nlc->nlu = nlbp->nlu; + nlc->s = s; + + if (nlbp->pfnNewConnection) + nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); + } + + NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); + nlbp->hThread = nullptr; + + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort); +} + +MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb) +{ + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb); + if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError()); +LBL_Error: + delete nlbp; + return nullptr; + } + + SOCKADDR_IN sin = { 0 }; + sin.sin_family = AF_INET; + + SOCKADDR_IN6 sin6 = { 0 }; + sin6.sin6_family = AF_INET6; + + /* if the netlib user wanted a free port given in the range, then + they better have given wPort == 0, let's hope so */ + int foundPort = 0; + if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { + if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { + Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); + SetLastError(WSAEADDRINUSE); + } + else foundPort = 1; + } + else { + /* if ->wPort == 0 then they'll get any free port, otherwise they'll + be asking for whatever was in nlb->wPort*/ + if (nlb->wPort != 0) { + Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort); + sin.sin_port = htons(nlb->wPort); + sin6.sin6_port = htons(nlb->wPort); + } + + if (nlbp->s != INVALID_SOCKET) + if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) { + SOCKADDR_IN sin2 = { 0 }; + int len = sizeof(sin2); + if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len)) + sin6.sin6_port = sin2.sin_port; + foundPort = 1; + } + + if (nlbp->s6 != INVALID_SOCKET) + if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) + foundPort = 1; + } + if (!foundPort) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError()); + goto LBL_Error; + } + + if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); + goto LBL_Error; + } + + if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); + goto LBL_Error; + } + + SOCKADDR_INET_M sinm = { 0 }; + int len = sizeof(sinm); + if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) { + nlb->wPort = ntohs(sinm.Ipv4.sin_port); + nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr); + } + else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len)) + nlb->wPort = ntohs(sinm.Ipv6.sin6_port); + else { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError()); + goto LBL_Error; + } + nlbp->wPort = nlb->wPort; + + if (nlb->dwInternalIP == 0) { + char hostname[64] = ""; + gethostname(hostname, _countof(hostname)); + + PHOSTENT he = gethostbyname(hostname); + if (he && he->h_addr) + nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); + } + + DWORD extIP; + if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) { + Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort); + nlb->wExPort = nlbp->wExPort; + nlb->dwExternalIP = extIP; + } + else { + if (nlu->settings.enableUPnP) + Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort); + else + Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort); + + nlbp->wExPort = 0; + nlb->wExPort = nlb->wPort; + nlb->dwExternalIP = nlb->dwInternalIP; + } + + nlbp->hThread = mir_forkThread(NetlibBindAcceptThread, nlbp); + + if (GetSubscribersCount((THook*)hEventConnected)) { + NETLIBCONNECTIONEVENTINFO ncei = {}; + ncei.connected = 1; + ncei.listening = 1; + ncei.szSettingsModule = nlu->user.szSettingsModule; + memcpy(&ncei.local, &sin, sizeof(sin)); + NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); + } + + return nlbp; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb) + : handleType(NLH_BOUNDPORT), + nlu(_nlu) +{ + pfnNewConnection = nlb->pfnNewConnection; + pExtra = nlb->pExtra; + + s = socket(PF_INET, SOCK_STREAM, 0); + s6 = socket(PF_INET6, SOCK_STREAM, 0); +} + +void NetlibBoundPort::close() +{ + if (s != INVALID_SOCKET) { + closesocket(s); + s = INVALID_SOCKET; + } + if (s6 != INVALID_SOCKET) { + closesocket(s6); + s6 = INVALID_SOCKET; + } +} diff --git a/src/mir_app/src/netlib_http.cpp b/src/mir_app/src/netlib_http.cpp new file mode 100644 index 0000000000..7925a73c73 --- /dev/null +++ b/src/mir_app/src/netlib_http.cpp @@ -0,0 +1,1146 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "../libs/zlib/src/zlib.h" +#include "netlib.h" + +#define HTTPRECVHEADERSTIMEOUT 30000 //in ms +#define HTTPRECVDATATIMEOUT 20000 + +struct ProxyAuth +{ + char *szServer; + char *szMethod; + + ProxyAuth(const char *pszServer, const char *pszMethod) + { + szServer = mir_strdup(pszServer); + szMethod = mir_strdup(pszMethod); + } + ~ProxyAuth() + { + mir_free(szServer); + mir_free(szMethod); + } + static int Compare(const ProxyAuth *p1, const ProxyAuth *p2) + { + return mir_strcmpi(p1->szServer, p2->szServer); + } +}; + +struct ProxyAuthList : OBJLIST +{ + ProxyAuthList() : OBJLIST(2, ProxyAuth::Compare) {} + + void add(const char *szServer, const char *szMethod) + { + if (szServer == nullptr) return; + int i = getIndex((ProxyAuth*)&szServer); + if (i >= 0) { + ProxyAuth &rec = (*this)[i]; + if (szMethod == nullptr) + remove(i); + else if (_stricmp(rec.szMethod, szMethod)) { + mir_free(rec.szMethod); + rec.szMethod = mir_strdup(szMethod); + } + } + else insert(new ProxyAuth(szServer, szMethod)); + } + + const char* find(const char *szServer) + { + ProxyAuth *rec = szServer ? OBJLIST::find((ProxyAuth*)&szServer) : nullptr; + return rec ? rec->szMethod : nullptr; + } +}; + +ProxyAuthList proxyAuthList; + +static int RecvWithTimeoutTime(NetlibConnection *nlc, unsigned dwTimeoutTime, char *buf, int len, int flags) +{ + DWORD dwTimeNow; + + if (nlc->foreBuf.isEmpty() && !sslApi.pending(nlc->hSsl)) { + while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) { + unsigned dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000); + int res = WaitUntilReadable(nlc->s, dwDeltaTime); + + switch (res) { + case SOCKET_ERROR: + return SOCKET_ERROR; + + case 1: + return Netlib_Recv(nlc, buf, len, flags); + } + + if (nlc->termRequested || Miranda_IsTerminated()) + return 0; + } + SetLastError(ERROR_TIMEOUT); + return SOCKET_ERROR; + } + return Netlib_Recv(nlc, buf, len, flags); +} + +static char* NetlibHttpFindHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr) +{ + for (int i=0; i < nlhrReply->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhrReply->headers[i]; + if (_stricmp(p.szName, hdr) == 0) + return p.szValue; + } + + return nullptr; +} + +static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider) +{ + char *szBasicHdr = nullptr; + char *szNegoHdr = nullptr; + char *szNtlmHdr = nullptr; + + for (int i=0; i < nlhrReply->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhrReply->headers[i]; + if (_stricmp(p.szName, hdr) == 0) { + if (_strnicmp(p.szValue, "Negotiate", 9) == 0) + szNegoHdr = p.szValue; + else if (_strnicmp(p.szValue, "NTLM", 4) == 0) + szNtlmHdr = p.szValue; + else if (_strnicmp(p.szValue, "Basic", 5) == 0) + szBasicHdr = p.szValue; + } + } + + if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr; + if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr; + if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr; + return nullptr; +} + +void NetlibConnFromUrl(const char *szUrl, bool secur, NETLIBOPENCONNECTION &nloc) +{ + secur = secur || _strnicmp(szUrl, "https", 5) == 0; + const char* phost = strstr(szUrl, "://"); + + char* szHost = mir_strdup(phost ? phost + 3 : szUrl); + + char* ppath = strchr(szHost, '/'); + if (ppath) *ppath = '\0'; + + memset(&nloc, 0, sizeof(nloc)); + nloc.szHost = szHost; + + char* pcolon = strrchr(szHost, ':'); + if (pcolon) { + *pcolon = '\0'; + nloc.wPort = (WORD)strtol(pcolon+1, nullptr, 10); + } + else nloc.wPort = secur ? 443 : 80; + nloc.flags = (secur ? NLOCF_SSL : 0); +} + +static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr) +{ + NETLIBOPENCONNECTION nloc; + + if (szUrl == nullptr) + NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, nloc); + else + NetlibConnFromUrl(szUrl, false, nloc); + + nloc.flags |= NLOCF_HTTP; + if (nloc.flags & NLOCF_SSL) + nlhr->flags |= NLHRF_SSL; + else + nlhr->flags &= ~NLHRF_SSL; + + if (nlc != nullptr) { + bool httpProxy = !(nloc.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP; + bool sameHost = mir_strcmp(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort; + + if (!httpProxy && !sameHost) { + NetlibDoCloseSocket(nlc); + + mir_free((char*)nlc->nloc.szHost); + nlc->nloc = nloc; + return NetlibDoConnect(nlc) ? nlc : nullptr; + } + } + else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, &nloc); + + mir_free((char*)nloc.szHost); + + return nlc; +} + +struct HttpSecurityContext +{ + HANDLE m_hNtlmSecurity; + char *m_szHost; + char *m_szProvider; + + HttpSecurityContext() + { + m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr; + } + + ~HttpSecurityContext() { Destroy(); } + + void Destroy(void) + { + if (!m_hNtlmSecurity) return; + + Netlib_DestroySecurityProvider(m_hNtlmSecurity); + m_hNtlmSecurity = nullptr; + mir_free(m_szHost); m_szHost = nullptr; + mir_free(m_szProvider); m_szProvider = nullptr; + } + + bool TryBasic(void) + { + return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic"); + } + + char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete) + { + char *szAuthHdr = nullptr; + bool justCreated = false; + NetlibUser *nlu = nlc->nlu; + + if (m_hNtlmSecurity) { + bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider); + newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost))); + if (newAuth) + Destroy(); + } + + if (m_hNtlmSecurity == nullptr) { + CMStringA szSpnStr; + if (szHost && _stricmp(szProvider, "Basic")) { + unsigned long ip = inet_addr(szHost); + PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET); + szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost); + _strlwr(szSpnStr.GetBuffer() + 5); + Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str()); + } + m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str())); + if (m_hNtlmSecurity) { + m_szProvider = mir_strdup(szProvider); + m_szHost = mir_strdup(szHost); + justCreated = true; + } + } + + if (m_hNtlmSecurity) { + ptrW szLogin, szPassw; + + if (nlu->settings.useProxyAuth) { + mir_cslock lck(csNetlibUser); + szLogin = mir_a2u(nlu->settings.szProxyAuthUser); + szPassw = mir_a2u(nlu->settings.szProxyAuthPassword); + } + + szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete); + if (!szAuthHdr) + Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)"); + else if (justCreated) + proxyAuthList.add(m_szHost, m_szProvider); + } + else complete = 1; + + return szAuthHdr; + } +}; + +static int HttpPeekFirstResponseLine(NetlibConnection *nlc, DWORD dwTimeoutTime, DWORD recvFlags, int *resultCode, char **ppszResultDescr, int *length) +{ + int bytesPeeked; + char buffer[2048], *peol; + + while (true) { + bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags); + if (bytesPeeked == 0) { + SetLastError(ERROR_HANDLE_EOF); + return 0; + } + if (bytesPeeked == SOCKET_ERROR) + return 0; + + buffer[bytesPeeked] = '\0'; + if ((peol = strchr(buffer, '\n')) != nullptr) + break; + + if ((int)mir_strlen(buffer) < bytesPeeked) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + if (bytesPeeked == _countof(buffer) - 1) { + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + if (Miranda_IsTerminated()) + return 0; + Sleep(10); + } + + if (peol == buffer) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + + *peol = '\0'; + + if (_strnicmp(buffer, "HTTP/", 5)) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + + size_t off = strcspn(buffer, " \t"); + if (off >= (unsigned)bytesPeeked) + return 0; + + char *pResultCode = buffer + off; + *(pResultCode++) = 0; + + char *pResultDescr; + *resultCode = strtol(pResultCode, &pResultDescr, 10); + + if (ppszResultDescr) + *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr)); + + if (length) + *length = peol - buffer + 1; + return 1; +} + +static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader) +{ + bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH); + + if (sendContentLengthHeader && sendData) + httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength); + else + httpRequest.AppendFormat("\r\n"); + + DWORD hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | + (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ? + MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags); + if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) { + DWORD sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | + (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? + MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags); + + bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR; + } + + return bytesSent; +} + +MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) +{ + NETLIBHTTPREQUEST *nlhrReply = nullptr; + HttpSecurityContext httpSecurity; + + char *szHost = nullptr, *szNewUrl = nullptr; + char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr; + int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader; + int bytesSent = 0; + bool lastFirstLineFail = false; + + if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + NetlibUser *nlu = nlc->nlu; + if (GetNetlibHandleType(nlu) != NLH_USER) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT; + + const char *pszRequest; + switch (nlhr->requestType) { + case REQUEST_GET: pszRequest = "GET"; break; + case REQUEST_POST: pszRequest = "POST"; break; + case REQUEST_CONNECT: pszRequest = "CONNECT"; break; + case REQUEST_HEAD: pszRequest = "HEAD"; break; + case REQUEST_PUT: pszRequest = "PUT"; break; + case REQUEST_DELETE: pszRequest = "DELETE"; break; + case REQUEST_PATCH: pszRequest = "PATCH"; break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) + return SOCKET_ERROR; + + const char *pszFullUrl = nlhr->szUrl; + const char *pszUrl = nullptr; + + unsigned complete = false; + int count = 11; + while (--count) { + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { + nlc = nullptr; + bytesSent = SOCKET_ERROR; + break; + } + + if (!NetlibReconnect(nlc)) { + bytesSent = SOCKET_ERROR; + break; + } + + if (!pszUrl) { + pszUrl = pszFullUrl; + if (!(nlhr->flags & NLHRF_MANUALHOST)) { + bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL); + + const char *ppath, *phost; + phost = strstr(pszUrl, "://"); + if (phost == nullptr) phost = pszUrl; + else phost += 3; + ppath = strchr(phost, '/'); + if (ppath == phost) + phost = nullptr; + + replaceStr(szHost, phost); + if (ppath && phost) + szHost[ppath - phost] = 0; + + if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy) + pszUrl = ppath ? ppath : "/"; + + if (usingProxy && phost && !nlc->dnsThroughProxy) { + char *tszHost = mir_strdup(phost); + if (ppath) + tszHost[ppath - phost] = 0; + char *cln = strchr(tszHost, ':'); if (cln) *cln = 0; + + if (inet_addr(tszHost) == INADDR_NONE) { + in_addr ip; + if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) { + mir_free(szHost); + if (cln) *cln = ':'; + szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach(); + } + } + mir_free(tszHost); + } + } + } + + if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) { + if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) { + const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer); + if (szAuthMethodNlu) { + mir_free(pszProxyAuthHdr); + pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete); + } + } + } + nlc->proxyAuthNeeded = false; + + CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); + + // HTTP headers + doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0; + for (i = 0; i < nlhr->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhr->headers[i]; + if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1; + else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1; + else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1; + else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1; + else if (!mir_strcmpi(p.szName, "Connection")) continue; + if (p.szValue == nullptr) continue; + httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue); + } + if (szHost && !doneHostHeader) + httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost); + if (pszProxyAuthHdr && !doneProxyAuthHeader) + httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr); + if (pszAuthHdr && !doneAuthHeader) + httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr); + httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive"); + httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive"); + + // Add Sticky Headers + if (nlu->szStickyHeaders != nullptr) + httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders); + + // send it + bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader); + if (bytesSent == SOCKET_ERROR) + break; + + // ntlm reply + if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD) + break; + + DWORD fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0); + DWORD dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout; + if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) { + DWORD err = GetLastError(); + Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count); + + // connection died while we were waiting + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { + nlc = nullptr; + break; + } + + if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) { + bytesSent = SOCKET_ERROR; + break; + } + + lastFirstLineFail = true; + continue; + } + + int resultCode = nlhr->resultCode; + lastFirstLineFail = false; + + DWORD hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ? + MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + DWORD dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP; + + if (resultCode == 100) + nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); + + else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect + pszUrl = nullptr; + + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + if (nlhrReply) { + char* tmpUrl = NetlibHttpFindHeader(nlhrReply, "Location"); + if (tmpUrl) { + size_t rlen = 0; + if (tmpUrl[0] == '/') { + const char *ppath, *phost; + phost = strstr(pszFullUrl, "://"); + phost = phost ? phost + 3 : pszFullUrl; + ppath = strchr(phost, '/'); + rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl); + } + + nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1); + + strncpy(nlc->szNewUrl, pszFullUrl, rlen); + mir_strcpy(nlc->szNewUrl + rlen, tmpUrl); + pszFullUrl = nlc->szNewUrl; + pszUrl = nullptr; + + if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) { + bytesSent = SOCKET_ERROR; + break; + } + } + else { + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else { + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else if (resultCode == 401 && !doneAuthHeader) { //auth required + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + replaceStr(pszAuthHdr, nullptr); + if (nlhrReply) { + char *szAuthStr = nullptr; + if (!complete) { + szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider); + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (!szChallenge || !*lrtrimp(szChallenge)) + complete = true; + } + } + if (complete && httpSecurity.m_hNtlmSecurity) + szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr; + + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } + + pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete); + } + } + if (pszAuthHdr == nullptr) { + proxyAuthList.add(szHost, nullptr); + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr; + if (nlhrReply) { + char *szAuthStr = nullptr; + if (!complete) { + szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider); + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (!szChallenge || !*lrtrimp(szChallenge + 1)) + complete = true; + } + } + if (complete && httpSecurity.m_hNtlmSecurity) + szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr; + + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } + + pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete); + } + } + if (pszProxyAuthHdr == nullptr) { + proxyAuthList.add(nlc->szProxyServer, nullptr); + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else break; + + if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader) + replaceStr(pszProxyAuthHdr, nullptr); + + if (pszAuthHdr && resultCode != 401 && !doneAuthHeader) + replaceStr(pszAuthHdr, nullptr); + + if (nlhrReply) { + Netlib_FreeHttpRequest(nlhrReply); + nlhrReply = nullptr; + } + } + + if (count == 0) bytesSent = SOCKET_ERROR; + if (nlhrReply) + Netlib_FreeHttpRequest(nlhrReply); + + //clean up + mir_free(pszProxyAuthHdr); + mir_free(pszAuthHdr); + mir_free(szHost); + mir_free(szNewUrl); + + if (nlc) + NetlibLeaveNestedCS(&nlc->ncsSend); + + return bytesSent; +} + +MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) +{ + if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + if (nlhr->headers) { + for (int i = 0; i < nlhr->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhr->headers[i]; + mir_free(p.szName); + mir_free(p.szValue); + } + mir_free(nlhr->headers); + } + mir_free(nlhr->pData); + mir_free(nlhr->szResultDescr); + mir_free(nlhr->szUrl); + mir_free(nlhr); + return true; +} + +#define NHRV_BUF_SIZE 8192 + +MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) +{ + NetlibConnection *nlc = (NetlibConnection*)hConnection; + if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) + return nullptr; + + DWORD dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; + NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); + nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); + nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions + nlhr->requestType = REQUEST_RESPONSE; + + int firstLineLength = 0; + if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + Netlib_FreeHttpRequest(nlhr); + return nullptr; + } + + char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1); + int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT); + if (bytesPeeked != firstLineLength) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + Netlib_FreeHttpRequest(nlhr); + if (bytesPeeked != SOCKET_ERROR) + SetLastError(ERROR_HANDLE_EOF); + return nullptr; + } + + // Make sure all headers arrived + MBinBuffer buf; + int headersCount = 0; + bytesPeeked = 0; + for (bool headersCompleted = false; !headersCompleted;) { + bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE); + if (bytesPeeked == 0) + break; + + if (bytesPeeked == SOCKET_ERROR) { + bytesPeeked = 0; + break; + } + + buf.append(buffer, bytesPeeked); + + headersCount = 0; + for (char *pbuffer = (char*)buf.data();; headersCount++) { + char *peol = strchr(pbuffer, '\n'); + if (peol == nullptr) break; + if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) { + bytesPeeked = peol - (char*)buf.data() + 1; + headersCompleted = true; + break; + } + pbuffer = peol + 1; + } + } + + if (bytesPeeked <= 0) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + Netlib_FreeHttpRequest(nlhr); + return nullptr; + } + + // Receive headers + nlhr->headersCount = headersCount; + nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount); + + headersCount = 0; + for (char *pbuffer = buf.data();; headersCount++) { + char *peol = strchr(pbuffer, '\n'); + if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r')) + break; + *peol = 0; + + char *pColon = strchr(pbuffer, ':'); + if (pColon == nullptr) { + Netlib_FreeHttpRequest(nlhr); nlhr = nullptr; + SetLastError(ERROR_INVALID_DATA); + break; + } + + *pColon = 0; + nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer)); + nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1)); + pbuffer = peol + 1; + } + + // remove processed data + buf.remove(bytesPeeked); + nlc->foreBuf.appendBefore(buf.data(), buf.length()); + + NetlibLeaveNestedCS(&nlc->ncsRecv); + return nlhr; +} + +MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) +{ + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || + nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || + nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION) + nlhr->nlc = nullptr; + + NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc); + if (nlc == nullptr) + return nullptr; + + NETLIBHTTPREQUEST nlhrSend = *nlhr; + nlhrSend.flags |= NLHRF_SMARTREMOVEHOST; + + bool doneUserAgentHeader = NetlibHttpFindHeader(nlhr, "User-Agent") != nullptr; + bool doneAcceptEncoding = NetlibHttpFindHeader(nlhr, "Accept-Encoding") != nullptr; + if (!doneUserAgentHeader || !doneAcceptEncoding) { + nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2)); + memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount); + } + + char szUserAgent[64]; + if (!doneUserAgentHeader) { + nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent"; + nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent; + ++nlhrSend.headersCount; + + char szMirandaVer[64]; + strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE); + #if defined(_WIN64) + strncat_s(szMirandaVer, " x64", _TRUNCATE); + #endif + + char *pspace = strchr(szMirandaVer, ' '); + if (pspace) { + *pspace++ = '\0'; + mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace); + } + else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer); + } + if (!doneAcceptEncoding) { + nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding"; + nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip"; + ++nlhrSend.headersCount; + } + if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) { + if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers); + nlhr->resultCode = nlhrSend.resultCode; + Netlib_CloseHandle(nlc); + return nullptr; + } + if (!doneUserAgentHeader || !doneAcceptEncoding) + mir_free(nlhrSend.headers); + + DWORD dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | + (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + DWORD hflags = + (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + NETLIBHTTPREQUEST *nlhrReply; + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = Netlib_RecvHttpHeaders(nlc); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + if (nlhrReply) { + nlhrReply->szUrl = nlc->szNewUrl; + nlc->szNewUrl = nullptr; + } + + if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) { + Netlib_CloseHandle(nlc); + if (nlhrReply) + nlhrReply->nlc = nullptr; + } + else nlhrReply->nlc = nlc; + + return nlhrReply; +} + +void NetlibHttpSetLastErrorUsingHttpResult(int result) +{ + if (result >= 200 && result < 300) { + SetLastError(ERROR_SUCCESS); + return; + } + switch (result) { + case 400: SetLastError(ERROR_BAD_FORMAT); break; + case 401: + case 402: + case 403: + case 407: SetLastError(ERROR_ACCESS_DENIED); break; + case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; + case 405: + case 406: SetLastError(ERROR_INVALID_FUNCTION); break; + case 408: SetLastError(ERROR_TIMEOUT); break; + default: SetLastError(ERROR_GEN_FAILURE); break; + } +} + +char* gzip_decode(char *gzip_data, int *len_ptr, int window) +{ + if (*len_ptr == 0) return nullptr; + + int gzip_len = *len_ptr * 5; + char* output_data = nullptr; + + int gzip_err; + z_stream zstr; + + do { + output_data = (char*)mir_realloc(output_data, gzip_len+1); + + zstr.next_in = (Bytef*)gzip_data; + zstr.avail_in = *len_ptr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream)); + + zstr.next_out = (Bytef*)output_data; + zstr.avail_out = gzip_len; + + gzip_err = inflate(&zstr, Z_FINISH); + + inflateEnd(&zstr); + gzip_len *= 2; + } while (gzip_err == Z_BUF_ERROR); + + gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1; + + if (gzip_len <= 0) { + mir_free(output_data); + output_data = nullptr; + } + else output_data[gzip_len] = 0; + + *len_ptr = gzip_len; + return output_data; +} + +static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, DWORD flags) +{ + MBinBuffer buf; + + while (true) { + char data[1000]; + int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags); + if (recvResult <= 0 || recvResult >= _countof(data)) + return SOCKET_ERROR; + + buf.append(data, recvResult); // add chunk + + const char *peol1 = (const char*)memchr(buf.data(), '\n', buf.length()); + if (peol1 == nullptr) + continue; + + int cbRest = int(peol1 - buf.data()) + 1; + const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest); + if (peol2 == nullptr) + continue; + + int sz = peol2 - buf.data() + 1; + int r = strtol(first ? buf.data() : peol1 + 1, nullptr, 16); + if (r == 0) { + const char *peol3 = strchr(peol2 + 1, '\n'); + if (peol3 == nullptr) + continue; + sz = peol3 - buf.data() + 1; + } + buf.remove(sz); // remove all our data from buffer + nlc->foreBuf.appendBefore(buf.data(), buf.length()); + return r; + } +} + +NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, DWORD hflags, DWORD dflags, bool isConnect) +{ + int dataLen = -1, i, chunkhdr = 0; + bool chunked = false; + int cenc = 0, cenctype = 0, close = 0; + +next: + NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); + if (nlhrReply == nullptr) + return nullptr; + + if (nlhrReply->resultCode == 100) { + Netlib_FreeHttpRequest(nlhrReply); + goto next; + } + + if (nlhrReply->resultCode == 204) + dataLen = 0; + + for (i = 0; i < nlhrReply->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhrReply->headers[i]; + if (!mir_strcmpi(p.szName, "Content-Length")) + dataLen = atoi(p.szValue); + + if (!mir_strcmpi(p.szName, "Content-Encoding")) { + cenc = i; + if (strstr(p.szValue, "gzip")) + cenctype = 1; + else if (strstr(p.szValue, "deflate")) + cenctype = 2; + } + + if (!mir_strcmpi(p.szName, "Connection")) + close = !mir_strcmpi(p.szValue, "close"); + + if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) { + chunked = true; + chunkhdr = i; + dataLen = -1; + } + } + + if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) { + int recvResult, chunksz = -1; + int dataBufferAlloced; + + if (chunked) { + chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0)); + if (chunksz == SOCKET_ERROR) { + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + dataLen = chunksz; + } + dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1; + nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); + + while (chunksz != 0) { + while (true) { + recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT, + nlhrReply->pData + nlhrReply->dataLength, + dataBufferAlloced - nlhrReply->dataLength - 1, + dflags | (cenctype ? MSG_NODUMP : 0)); + + if (recvResult == 0) break; + if (recvResult == SOCKET_ERROR) { + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + nlhrReply->dataLength += recvResult; + + if (dataLen >= 0) { + if (nlhrReply->dataLength >= dataLen) + break; + } + else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) { + dataBufferAlloced += 2048; + nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); + if (nlhrReply->pData == nullptr) { + SetLastError(ERROR_OUTOFMEMORY); + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + } + Sleep(10); + } + + if (!chunked) + break; + + chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP); + if (chunksz == SOCKET_ERROR) { + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + dataLen += chunksz; + dataBufferAlloced += chunksz; + + nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); + } + + nlhrReply->pData[nlhrReply->dataLength] = '\0'; + } + + if (chunked) { + nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16); + mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length"); + + nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16); + mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength); + } + + if (cenctype) { + int bufsz = nlhrReply->dataLength; + char* szData = nullptr; + + switch (cenctype) { + case 1: + szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS); + break; + + case 2: + szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS); + if (bufsz < 0) { + bufsz = nlhrReply->dataLength; + szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS); + } + break; + } + + if (bufsz > 0) { + NetlibDumpData(nlc, (PBYTE)szData, bufsz, 0, dflags | MSG_NOTITLE); + mir_free(nlhrReply->pData); + nlhrReply->pData = szData; + nlhrReply->dataLength = bufsz; + + mir_free(nlhrReply->headers[cenc].szName); + mir_free(nlhrReply->headers[cenc].szValue); + memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0])); + } + else if (bufsz == 0) { + mir_free(nlhrReply->pData); + nlhrReply->pData = nullptr; + nlhrReply->dataLength = 0; + } + } + + if (close && + (nlc->proxyType != PROXYTYPE_HTTP || nlc->nloc.flags & NLOCF_SSL) && + (!isConnect || nlhrReply->resultCode != 200)) + NetlibDoCloseSocket(nlc); + + return nlhrReply; +} diff --git a/src/mir_app/src/netlib_httpproxy.cpp b/src/mir_app/src/netlib_httpproxy.cpp new file mode 100644 index 0000000000..8d702d6884 --- /dev/null +++ b/src/mir_app/src/netlib_httpproxy.cpp @@ -0,0 +1,85 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +typedef enum +{ + reqHelloGet, + reqOldGet, + reqOldPost, + reqNewPost, +} +RequestType; + +///////////////////////////////////////////////////////////////////////////////////////// + +#define NETLIBHTTP_RETRYCOUNT 3 +#define NETLIBHTTP_RETRYTIMEOUT 2000 + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi) +{ + if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + mir_free(nlc->nlhpi.szHttpGetUrl); + mir_free(nlc->nlhpi.szHttpPostUrl); + + nlc->nlhpi.combinePackets = 1; + memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi)); + if (nlc->nlhpi.combinePackets == 0) + nlc->nlhpi.combinePackets = 1; + + nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl); + nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl); + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders) +{ + if (GetNetlibHandleType(nlu) != NLH_USER) + return ERROR_INVALID_PARAMETER; + + replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout) +{ + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) + return -1; + + int oldTimeout = nlc->pollingTimeout; + nlc->pollingTimeout = iTimeout; + return oldTimeout; +} diff --git a/src/mir_app/src/netlib_log.cpp b/src/mir_app/src/netlib_log.cpp new file mode 100644 index 0000000000..ca0d377dc9 --- /dev/null +++ b/src/mir_app/src/netlib_log.cpp @@ -0,0 +1,546 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#define MS_NETLIB_LOGWIN "Netlib/Log/Win" + +extern HANDLE hConnectionHeaderMutex; + +#define TIMEFORMAT_NONE 0 +#define TIMEFORMAT_HHMMSS 1 +#define TIMEFORMAT_MILLISECONDS 2 +#define TIMEFORMAT_MICROSECONDS 3 +struct { + HWND hwndOpts; + int toOutputDebugString; + int toFile; + int toLog; + int timeFormat; + int showUser; + int dumpSent, dumpRecv, dumpProxy, dumpSsl; + int textDumps, autoDetectText; + CMStringW tszFile, tszUserFile; +} +static logOptions = {}; + +struct LOGMSG +{ + const char* pszHead; + const char* pszMsg; +}; + +static __int64 mirandaStartTime, perfCounterFreq; +static int bIsActive = TRUE; +static HANDLE hLogEvent = nullptr; +static HANDLE hLogger = nullptr; + +static void InitLog() +{ + if (hLogger) { + mir_closeLog(hLogger); + hLogger = nullptr; + } + + ptrW szBuf(db_get_wsa(0, "Netlib", "File")); + if (mir_wstrlen(szBuf)) { + logOptions.tszUserFile = szBuf.get(); + + wchar_t path[MAX_PATH]; + PathToAbsoluteW(VARSW(szBuf), path); + logOptions.tszFile = path; + } + else { + db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt"); + logOptions.tszFile = VARSW(logOptions.tszUserFile); + } + + if (logOptions.toFile) + hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), logOptions.tszFile, 0); +} + +static const wchar_t* szTimeFormats[] = +{ + LPGENW("No times"), + LPGENW("Standard hh:mm:ss times"), + LPGENW("Times in milliseconds"), + LPGENW("Times in microseconds") +}; + +static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + wchar_t str[MAX_PATH]; + + switch (message) { + case WM_INITDIALOG: + logOptions.hwndOpts = hwndDlg; + TranslateDialogDefault(hwndDlg); + CheckDlgButton(hwndDlg, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED); + + for (auto &it : szTimeFormats) + SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_ADDSTRING, 0, (LPARAM)TranslateW(it)); + + SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_SETCURSEL, logOptions.timeFormat, 0); + CheckDlgButton(hwndDlg, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED); + SetDlgItemText(hwndDlg, IDC_FILENAME, logOptions.tszUserFile); + SetDlgItemText(hwndDlg, IDC_PATH, logOptions.tszFile); + CheckDlgButton(hwndDlg, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED); + { + ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart")); + if (szRun) + SetDlgItemTextA(hwndDlg, IDC_RUNATSTART, szRun); + + HWND hwndFilter = GetDlgItem(hwndDlg, IDC_FILTER); + SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES)); + + TVINSERTSTRUCT tvis = {}; + tvis.hInsertAfter = TVI_SORT; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; + tvis.item.stateMask = TVIS_STATEIMAGEMASK; + + for (auto &it : netlibUser) { + tvis.item.pszText = it->user.szDescriptiveName.w; + tvis.item.lParam = netlibUser.indexOf(&it); + tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1); + TreeView_InsertItem(hwndFilter, &tvis); + } + tvis.item.lParam = -1; + tvis.item.pszText = TranslateT("(Miranda core logging)"); + tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1); + TreeView_InsertItem(hwndFilter, &tvis); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_FILENAME: + if (HIWORD(wParam) == EN_CHANGE) { + if ((HWND)lParam == GetFocus()) + CheckDlgButton(hwndDlg, IDC_TOFILE, BST_CHECKED); + + wchar_t path[MAX_PATH]; + GetWindowText((HWND)lParam, path, _countof(path)); + + PathToAbsoluteW(VARSW(path), path); + SetDlgItemText(hwndDlg, IDC_PATH, path); + } + break; + + case IDC_FILENAMEBROWSE: + case IDC_RUNATSTARTBROWSE: + GetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str, _countof(str)); + { + wchar_t filter[200]; + mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0); + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwndDlg; + ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT; + if (LOWORD(wParam) == IDC_FILENAMEBROWSE) + ofn.lpstrTitle = TranslateT("Select where log file will be created"); + else { + ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + ofn.lpstrTitle = TranslateT("Select program to be run"); + } + ofn.lpstrFilter = filter; + ofn.lpstrFile = str; + ofn.nMaxFile = _countof(str) - 2; + ofn.nMaxFileTitle = MAX_PATH; + if (LOWORD(wParam) == IDC_FILENAMEBROWSE) { + if (!GetSaveFileName(&ofn)) return 1; + } + else if (!GetOpenFileName(&ofn)) + return 1; + + if (LOWORD(wParam) == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) { + memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t))); + str[0] = '"'; + mir_wstrcat(str, L"\""); + } + SetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str); + } + break; + + case IDC_RUNNOW: + GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str)); + if (str[0]) { + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + CreateProcess(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); + } + break; + + case IDOK: + GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str)); + db_set_ws(0, "Netlib", "RunAtStart", str); + db_set_b(0, "Netlib", "ShowLogOptsAtStart", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWTHISDLGATSTART)); + + GetDlgItemText(hwndDlg, IDC_FILENAME, str, _countof(str)); + logOptions.tszUserFile = rtrimw(str); + db_set_ws(0, "Netlib", "File", str); + + GetDlgItemText(hwndDlg, IDC_PATH, str, _countof(str)); + logOptions.tszFile = rtrimw(str); + + db_set_b(0, "Netlib", "DumpRecv", logOptions.dumpRecv = IsDlgButtonChecked(hwndDlg, IDC_DUMPRECV)); + db_set_b(0, "Netlib", "DumpSent", logOptions.dumpSent = IsDlgButtonChecked(hwndDlg, IDC_DUMPSENT)); + db_set_b(0, "Netlib", "DumpProxy", logOptions.dumpProxy = IsDlgButtonChecked(hwndDlg, IDC_DUMPPROXY)); + db_set_b(0, "Netlib", "DumpSsl", logOptions.dumpSsl = IsDlgButtonChecked(hwndDlg, IDC_DUMPSSL)); + db_set_b(0, "Netlib", "TextDumps", logOptions.textDumps = IsDlgButtonChecked(hwndDlg, IDC_TEXTDUMPS)); + db_set_b(0, "Netlib", "AutoDetectText", logOptions.autoDetectText = IsDlgButtonChecked(hwndDlg, IDC_AUTODETECTTEXT)); + db_set_b(0, "Netlib", "TimeFormat", logOptions.timeFormat = SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_GETCURSEL, 0, 0)); + db_set_b(0, "Netlib", "ShowUser", logOptions.showUser = IsDlgButtonChecked(hwndDlg, IDC_SHOWNAMES)); + db_set_b(0, "Netlib", "ToOutputDebugString", logOptions.toOutputDebugString = IsDlgButtonChecked(hwndDlg, IDC_TOOUTPUTDEBUGSTRING)); + db_set_b(0, "Netlib", "ToFile", logOptions.toFile = IsDlgButtonChecked(hwndDlg, IDC_TOFILE)); + { + HWND hwndFilter = GetDlgItem(logOptions.hwndOpts, IDC_FILTER); + TVITEM tvi = { 0 }; + BOOL checked; + + tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; + tvi.hItem = TreeView_GetRoot(hwndFilter); + + while (tvi.hItem) { + TreeView_GetItem(hwndFilter, &tvi); + checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2); + + if (tvi.lParam == -1) { + logOptions.toLog = checked; + db_set_dw(0, "Netlib", "NLlog", checked); + } + else if (tvi.lParam < netlibUser.getCount()) { + netlibUser[tvi.lParam]->toLog = checked; + db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked); + } + + tvi.hItem = TreeView_GetNextSibling(hwndFilter, tvi.hItem); + } + } + InitLog(); + __fallthrough; + + case IDCANCEL: + DestroyWindow(hwndDlg); + } + break; + + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + + case WM_DESTROY: + ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE)); + logOptions.hwndOpts = nullptr; + break; + } + return FALSE; +} + +void NetlibLogShowOptions(void) +{ + if (logOptions.hwndOpts == nullptr) + logOptions.hwndOpts = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NETLIBLOGOPTS), nullptr, LogOptionsDlgProc); + SetForegroundWindow(logOptions.hwndOpts); +} + +static INT_PTR ShowOptions(WPARAM, LPARAM) +{ + NetlibLogShowOptions(); + return 0; +} + +int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags) +{ + if (!bIsActive) + return 0; + + DWORD dwOriginalLastError = GetLastError(); + + if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + /* if the Netlib user handle is nullptr, just pretend its not */ + if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog)) + return 1; + + LARGE_INTEGER liTimeNow; + char szTime[32], szHead[128]; + switch (logOptions.timeFormat) { + case TIMEFORMAT_HHMMSS: + GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime)); + mir_strcat(szTime, " "); + break; + + case TIMEFORMAT_MILLISECONDS: + QueryPerformanceCounter(&liTimeNow); + liTimeNow.QuadPart -= mirandaStartTime; + mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq, + 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); + break; + + case TIMEFORMAT_MICROSECONDS: + QueryPerformanceCounter(&liTimeNow); + liTimeNow.QuadPart -= mirandaStartTime; + mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq, + 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); + break; + + default: + szTime[0] = '\0'; + break; + } + + if (flags & MSG_NOTITLE) + szHead[0] = 0; + else { + char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr; + if (szUser) + mir_snprintf(szHead, "[%s%04X] [%s] ", szTime, GetCurrentThreadId(), szUser); + else + mir_snprintf(szHead, "[%s%04X] ", szTime, GetCurrentThreadId()); + } + + if (logOptions.toOutputDebugString) { + if (szHead[0]) + OutputDebugStringA(szHead); + OutputDebugStringA(pszMsg); + OutputDebugStringA("\n"); + } + + if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) { + size_t len = mir_strlen(pszMsg); + mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n"); + } + + LOGMSG logMsg = { szHead, pszMsg }; + NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg); + + SetLastError(dwOriginalLastError); + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void PROTO_INTERFACE::debugLogA(const char *szFormat, ...) +{ + char buf[4096]; + va_list args; + va_start(args, szFormat); + int res = _vsnprintf(buf, _countof(buf), szFormat, args); + NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0); + va_end(args); +} + +void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...) +{ + WCHAR buf[4096]; + va_list args; + va_start(args, wszFormat); + int res = _vsnwprintf(buf, _countof(buf), wszFormat, args); + NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0); + va_end(args); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + char szText[8000]; + mir_vsnprintf(szText, _countof(szText), fmt, va); + va_end(va); + return NetlibLog_Worker(hUser, szText, 0); +} + +MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...) +{ + va_list va; + va_start(va, fmt); + wchar_t szText[8000]; + mir_vsnwprintf(szText, _countof(szText), fmt, va); + va_end(va); + return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr) +{ + return NetlibLog_Worker(hUser, pszStr, 0); +} + +MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr) +{ + return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void NetlibDumpData(NetlibConnection *nlc, PBYTE buf, int len, int sent, int flags) +{ + // This section checks a number of conditions and aborts + // the dump if the data should not be written to the log + + // Check packet flags + if (flags & (MSG_PEEK | MSG_NODUMP)) + return; + + // Check user's log settings + if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty()))) + return; + if ((sent && !logOptions.dumpSent) || (!sent && !logOptions.dumpRecv)) + return; + if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy) + return; + if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl) + return; + + CMStringA str; + + WaitForSingleObject(hConnectionHeaderMutex, INFINITE); + NetlibUser *nlu = nlc ? nlc->nlu : nullptr; + + if (!(flags & MSG_NOTITLE)) + str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : ""); + ReleaseMutex(hConnectionHeaderMutex); + + // check filter settings + if (nlu == nullptr) { + if (!logOptions.toLog) + return; + } + else if (!nlu->toLog) + return; + + bool isText = true; + if (!logOptions.textDumps) + isText = false; + else if (!(flags & MSG_DUMPASTEXT)) { + if (logOptions.autoDetectText) { + for (int i = 0; i < len; i++) { + if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) { + isText = false; + break; + } + } + } + else isText = false; + } + + // Text data + if (isText) { + str.Append((const char*)buf, len); + } + // Binary data + else { + for (int line = 0;; line += 16) { + PBYTE p = buf + line; + int colsInLine = min(16, len - line); + if (colsInLine == 16) + str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ", + line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + else { + str.AppendFormat("%08X: ", line); + + // Dump data as hex + int col; + for (col = 0; col < colsInLine; col++) + str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' '); + + // Fill out last line with blanks + for (; col < 16; col++) + str.Append(" "); + + str.AppendChar(' '); + } + + for (int col = 0; col < colsInLine; col++) + str.AppendChar((p[col] < ' ') ? '.' : p[col]); + + if (len - line <= 16) + break; + + str.AppendChar('\r'); // End each line with a break + str.AppendChar('\n'); // End each line with a break + } + } + + NetlibLog_Worker(nlu, str, flags); +} + +void NetlibLogInit(void) +{ + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + perfCounterFreq = li.QuadPart; + QueryPerformanceCounter(&li); + mirandaStartTime = li.QuadPart; + + CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions); + hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP); + + logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", 1); + logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", 1); + logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", 1); + logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", 0); + logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", 1); + logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", 1); + logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS); + logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", 1); + logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", 0); + logOptions.toFile = db_get_b(0, "Netlib", "ToFile", 0); + logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", 1); + + InitLog(); + + if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0)) + NetlibLogShowOptions(); + + ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart")); + if (szBuf) { + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); + } +} + +void NetlibLogShutdown(void) +{ + bIsActive = FALSE; + DestroyHookableEvent(hLogEvent); hLogEvent = nullptr; + if (IsWindow(logOptions.hwndOpts)) + DestroyWindow(logOptions.hwndOpts); +} diff --git a/src/mir_app/src/netlib_openconn.cpp b/src/mir_app/src/netlib_openconn.cpp new file mode 100644 index 0000000000..ee4bb5d996 --- /dev/null +++ b/src/mir_app/src/netlib_openconn.cpp @@ -0,0 +1,889 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +extern mir_cs csNetlibUser; +extern HANDLE hConnectionOpenMutex; +extern DWORD g_LastConnectionTick; +extern int connectionTimeout; +static int iUPnPCleanup = 0; + +#define RECV_DEFAULT_TIMEOUT 60000 + +//returns in network byte order +DWORD DnsLookup(NetlibUser *nlu, const char *szHost) +{ + HOSTENT* host; + DWORD ip = inet_addr(szHost); + if (ip != INADDR_NONE) + return ip; + + __try { + host = gethostbyname(szHost); + if (host) + return *(u_long*)host->h_addr_list[0]; + + Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError()); + } + __except (EXCEPTION_EXECUTE_HANDLER) {} + + return 0; +} + +int WaitUntilReadable(SOCKET s, DWORD dwTimeout, bool check) +{ + fd_set readfd; + TIMEVAL tv; + + if (s == INVALID_SOCKET) return SOCKET_ERROR; + + tv.tv_sec = dwTimeout / 1000; + tv.tv_usec = (dwTimeout % 1000) * 1000; + + FD_ZERO(&readfd); + FD_SET(s, &readfd); + + int result = select(0, &readfd, nullptr, nullptr, &tv); + if (result == 0 && !check) SetLastError(ERROR_TIMEOUT); + return result; +} + +int WaitUntilWritable(SOCKET s, DWORD dwTimeout) +{ + fd_set writefd; + TIMEVAL tv; + + tv.tv_sec = dwTimeout / 1000; + tv.tv_usec = (dwTimeout % 1000) * 1000; + + FD_ZERO(&writefd); + FD_SET(s, &writefd); + + switch (select(0, nullptr, &writefd, nullptr, &tv)) { + case 0: + SetLastError(ERROR_TIMEOUT); + case SOCKET_ERROR: + return 0; + } + return 1; +} + +bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, DWORD dwTimeout) +{ + int nReceived = 0; + DWORD dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout; + + while ((dwTimeNow = GetTickCount()) < dwCompleteTime) { + if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false; + nReceived = Netlib_Recv(nlc, buf, len, flags); + if (nReceived <= 0) return false; + + buf += nReceived; + len -= nReceived; + if (len <= 0) return true; + } + SetLastError(ERROR_TIMEOUT); + return false; +} + +static int NetlibInitSocks4Connection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) +{ + // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol + if (!nloc || !nloc->szHost || !nloc->szHost[0]) return 0; + + size_t nHostLen = mir_strlen(nloc->szHost) + 1; + size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1; + size_t len = 8 + nUserLen; + + char* pInit = (char*)alloca(len + nHostLen); + pInit[0] = 4; // SOCKS4 + pInit[1] = 1; //connect + *(PWORD)&pInit[2] = htons(nloc->wPort); + + if (nUserLen <= 1) pInit[8] = 0; + else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen); + + //if cannot resolve host, try resolving through proxy (requires SOCKS4a) + DWORD ip = DnsLookup(nlu, nloc->szHost); + *(PDWORD)&pInit[4] = ip ? ip : 0x01000000; + if (!ip) { + memcpy(&pInit[len], nloc->szHost, nHostLen); + len += nHostLen; + } + + if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + return 0; + } + + char reply[8]; + if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + + switch ((BYTE)reply[1]) { + case 90: return 1; + case 91: SetLastError(ERROR_ACCESS_DENIED); break; + case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; + case 93: SetLastError(ERROR_INVALID_ACCESS); break; + default: SetLastError(ERROR_INVALID_DATA); break; + } + Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (BYTE)reply[1], GetLastError()); + return 0; +} + +static int NetlibInitSocks5Connection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) +{ + //rfc1928 + BYTE buf[258]; + + buf[0] = 5; //yep, socks5 + buf[1] = 1; //one auth method + buf[2] = nlu->settings.useProxyAuth ? 2 : 0; + if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + return 0; + } + + //confirmation of auth method + if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + if ((buf[1] != 0 && buf[1] != 2)) { + SetLastError(ERROR_INVALID_ID_AUTHORITY); + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError()); + return 0; + } + + if (buf[1] == 2) { //rfc1929 + size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser); + size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword); + PBYTE pAuthBuf = (PBYTE)mir_alloc(3 + nUserLen + nPassLen); + pAuthBuf[0] = 1; //auth version + pAuthBuf[1] = (BYTE)nUserLen; + memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen); + pAuthBuf[2 + nUserLen] = (BYTE)nPassLen; + memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen); + if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + mir_free(pAuthBuf); + return 0; + } + mir_free(pAuthBuf); + + if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + if (buf[1]) { + SetLastError(ERROR_ACCESS_DENIED); + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + } + + size_t nHostLen; + DWORD hostIP; + + if (nlc->dnsThroughProxy) { + hostIP = inet_addr(nloc->szHost); + nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(nloc->szHost) + 1 : 4; + } + else { + hostIP = DnsLookup(nlu, nloc->szHost); + if (hostIP == 0) + return 0; + nHostLen = 4; + } + PBYTE pInit = (PBYTE)mir_alloc(6 + nHostLen); + pInit[0] = 5; //SOCKS5 + pInit[1] = nloc->flags & NLOCF_UDP ? 3 : 1; //connect or UDP + pInit[2] = 0; //reserved + if (hostIP == INADDR_NONE) { //DNS lookup through proxy + pInit[3] = 3; + pInit[4] = BYTE(nHostLen - 1); + memcpy(pInit + 5, nloc->szHost, nHostLen - 1); + } + else { + pInit[3] = 1; + *(PDWORD)(pInit + 4) = hostIP; + } + *(PWORD)(pInit + 4 + nHostLen) = htons(nloc->wPort); + if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + mir_free(pInit); + return 0; + } + mir_free(pInit); + + if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + + if (buf[0] != 5 || buf[1]) { + const char* err = "Unknown response"; + if (buf[0] != 5) + SetLastError(ERROR_BAD_FORMAT); + else { + switch (buf[1]) { + case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break; + case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break; + case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break; + case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break; + case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break; + case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break; + case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break; + case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break; + default: SetLastError(ERROR_INVALID_DATA); break; + } + } + Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err); + return 0; + } + + int nRecvSize = 0; + switch (buf[3]) { + case 1:// ipv4 addr + nRecvSize = 5; + break; + case 3:// dns name addr + nRecvSize = buf[4] + 2; + break; + case 4:// ipv6 addr + nRecvSize = 17; + break; + default: + Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]); + return 0; + } + if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + + //connected + return 1; +} + +static bool NetlibInitHttpsConnection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) +{ + // rfc2817 + CMStringA szUrl; + if (nlc->dnsThroughProxy) + szUrl.Format("%s:%u", nloc->szHost, nloc->wPort); + else { + DWORD ip = DnsLookup(nlu, nloc->szHost); + if (ip == 0) return false; + szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), nloc->wPort); + } + + NETLIBHTTPREQUEST nlhrSend = { 0 }; + nlhrSend.cbSize = sizeof(nlhrSend); + nlhrSend.requestType = REQUEST_CONNECT; + nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT; + nlhrSend.szUrl = szUrl.GetBuffer(); + + if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) + return false; + + NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true); + if (nlhrReply == nullptr) + return false; + + if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) { + if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) { + Netlib_FreeHttpRequest(nlhrReply); + nlc->dnsThroughProxy = 0; + return NetlibInitHttpsConnection(nlc, nlu, nloc); + } + + NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); + Netlib_Logf(nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__, nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr); + Netlib_FreeHttpRequest(nlhrReply); + return 0; + } + Netlib_FreeHttpRequest(nlhrReply); + return true; // connected +} + +static void FreePartiallyInitedConnection(NetlibConnection *nlc) +{ + DWORD dwOriginalLastError = GetLastError(); + + if (GetNetlibHandleType(nlc) == NLH_CONNECTION) + delete nlc; + + SetLastError(dwOriginalLastError); +} + +static bool my_connectIPv4(NetlibConnection *nlc, NETLIBOPENCONNECTION *nloc) +{ + int rc = 0, retrycnt = 0; + u_long notblocking = 1; + DWORD lasterr = 0; + static const TIMEVAL tv = { 1, 0 }; + NetlibUser *nlu = nlc->nlu; + + // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway + unsigned int dwTimeout = (nloc && (nloc->flags & NLOCF_V2) && (nloc->timeout > 0)) ? nloc->timeout : 30; + + // this is for XP SP2 where there is a default connection attempt limit of 10/second + if (connectionTimeout) { + WaitForSingleObject(hConnectionOpenMutex, 10000); + int waitdiff = GetTickCount() - g_LastConnectionTick; + if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); + g_LastConnectionTick = GetTickCount(); + ReleaseMutex(hConnectionOpenMutex); + + // might of died in between the wait + if (Miranda_IsTerminated()) return false; + } + + PHOSTENT he; + sockaddr_in sin = { 0 }; + sin.sin_family = AF_INET; + + if (nlc->proxyType) { + if (!nlc->szProxyServer) return false; + + if (nloc) + Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort); + else + Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort); + + sin.sin_port = htons(nlc->wProxyPort); + he = gethostbyname(nlc->szProxyServer); + } + else { + if (!nloc || !nloc->szHost || nloc->szHost[0] == '[' || strchr(nloc->szHost, ':')) return false; + Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort); + + sin.sin_port = htons(nloc->wPort); + he = gethostbyname(nloc->szHost); + } + + for (char** har = he->h_addr_list; *har && !Miranda_IsTerminated(); ++har) { + sin.sin_addr.s_addr = *(u_long*)*har; + + char *szIp = Netlib_AddressToString(&sin); + Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, szIp); + mir_free(szIp); + +retry: + nlc->s = socket(AF_INET, nloc->flags & NLOCF_UDP ? SOCK_DGRAM : SOCK_STREAM, 0); + if (nlc->s == INVALID_SOCKET) + return false; + + // return the socket to non blocking + if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) + return false; + + if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { + if (!BindSocketToPort(nlu->settings.szOutgoingPorts, nlc->s, INVALID_SOCKET, &nlu->inportnum)) + Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); + } + + // try a connect + if (connect(nlc->s, (LPSOCKADDR)&sin, sizeof(sin)) == 0) { + rc = 0; + break; + } + + // didn't work, was it cos of nonblocking? + if (WSAGetLastError() != WSAEWOULDBLOCK) { + rc = SOCKET_ERROR; + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + continue; + } + + while (true) { + fd_set r, w, e; + FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); + FD_SET(nlc->s, &r); + FD_SET(nlc->s, &w); + FD_SET(nlc->s, &e); + if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) + break; + + if (rc > 0) { + if (FD_ISSET(nlc->s, &w)) { + // connection was successful + rc = 0; + } + if (FD_ISSET(nlc->s, &r)) { + // connection was closed + rc = SOCKET_ERROR; + lasterr = WSAECONNRESET; + } + if (FD_ISSET(nlc->s, &e)) { + // connection failed. + int len = sizeof(lasterr); + rc = SOCKET_ERROR; + getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); + if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { + closesocket(nlc->s); + goto retry; + } + } + break; + } + else if (Miranda_IsTerminated()) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + else if (nloc->flags & NLOCF_V2 && nloc->waitcallback != nullptr && nloc->waitcallback(&dwTimeout) == 0) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + if (--dwTimeout == 0) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + } + + if (rc == 0) break; + + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + } + + notblocking = 0; + if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); + if (rc && lasterr) SetLastError(lasterr); + return rc == 0; +} + +static bool my_connectIPv6(NetlibConnection *nlc, NETLIBOPENCONNECTION *nloc) +{ + if (!nloc) + return false; + + NetlibUser *nlu = nlc->nlu; + int rc = SOCKET_ERROR, retrycnt = 0; + u_long notblocking = 1; + DWORD lasterr = 0; + static const TIMEVAL tv = { 1, 0 }; + unsigned int dwTimeout = (nloc->flags & NLOCF_V2) ? nloc->timeout : 0; + // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway + if (dwTimeout == 0) dwTimeout = 30; + + // this is for XP SP2 where there is a default connection attempt limit of 10/second + if (connectionTimeout) { + WaitForSingleObject(hConnectionOpenMutex, 10000); + int waitdiff = GetTickCount() - g_LastConnectionTick; + if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); + g_LastConnectionTick = GetTickCount(); + ReleaseMutex(hConnectionOpenMutex); + + // might of died in between the wait + if (Miranda_IsTerminated()) return false; + } + + char szPort[6]; + addrinfo *air = nullptr, *ai, hints = { 0 }; + + hints.ai_family = AF_UNSPEC; + + if (nloc->flags & NLOCF_UDP) { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } + else { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + + if (nlc->proxyType) { + if (!nlc->szProxyServer) + return false; + + Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort); + + _itoa(nlc->wProxyPort, szPort, 10); + if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) { + Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError()); + return false; + } + } + else { + if (!nloc->szHost) + return false; + + Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort); + + _itoa(nlc->nloc.wPort, szPort, 10); + + if (GetAddrInfoA(nlc->nloc.szHost, szPort, &hints, &air)) { + Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->nloc.szHost, WSAGetLastError()); + return false; + } + } + + for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) { + Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get()); +retry: + nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (nlc->s == INVALID_SOCKET) { + FreeAddrInfoA(air); + return false; + } + + // return the socket to non blocking + if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) { + FreeAddrInfoA(air); + return false; + } + + if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { + SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; + SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; + if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum)) + Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); + } + + // try a connect + if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) { + rc = 0; + break; + } + + // didn't work, was it cos of nonblocking? + if (WSAGetLastError() != WSAEWOULDBLOCK) { + rc = SOCKET_ERROR; + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + continue; + } + + while (true) { // timeout loop + fd_set r, w, e; + FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); + FD_SET(nlc->s, &r); + FD_SET(nlc->s, &w); + FD_SET(nlc->s, &e); + if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) + break; + + if (rc > 0) { + if (FD_ISSET(nlc->s, &w)) { + // connection was successful + rc = 0; + lasterr = 0; + } + if (FD_ISSET(nlc->s, &r)) { + // connection was closed + rc = SOCKET_ERROR; + lasterr = WSAECONNRESET; + } + if (FD_ISSET(nlc->s, &e)) { + // connection failed. + int len = sizeof(lasterr); + rc = SOCKET_ERROR; + getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); + if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + goto retry; + } + } + break; + } + else if (Miranda_IsTerminated()) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + else if (nloc->flags & NLOCF_V2 && nloc->waitcallback != nullptr && nloc->waitcallback(&dwTimeout) == 0) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + if (--dwTimeout == 0) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + } + + if (rc == 0) break; + + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + } + + FreeAddrInfoA(air); + + notblocking = 0; + if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); + if (rc && lasterr) SetLastError(lasterr); + return rc == 0; +} + +static int NetlibHttpFallbackToDirect(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) +{ + NetlibDoCloseSocket(nlc, true); + + Netlib_Logf(nlu, "Fallback to direct connection"); + + nlc->proxyAuthNeeded = false; + nlc->proxyType = 0; + replaceStr(nlc->szProxyServer, nullptr); + if (!my_connectIPv6(nlc, nloc)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); + return false; + } + return true; +} + +bool NetlibDoConnect(NetlibConnection *nlc) +{ + NETLIBOPENCONNECTION *nloc = &nlc->nloc; + NetlibUser *nlu = nlc->nlu; + + replaceStr(nlc->szProxyServer, nullptr); + + bool usingProxy = false, forceHttps = false; + if (nlu->settings.useProxy) { + if (nlu->settings.proxyType == PROXYTYPE_IE) + usingProxy = NetlibGetIeProxyConn(nlc, false); + else { + if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) { + nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer); + nlc->wProxyPort = nlu->settings.wProxyPort; + nlc->proxyType = nlu->settings.proxyType; + usingProxy = true; + } + } + } + + while (!my_connectIPv6(nlc, nloc)) { + // if connection failed, the state of nlc might be unpredictable + if (GetNetlibHandleType(nlc) == NLH_CONNECTION) { + // Fallback to direct only when using HTTP proxy, as this is what used by companies + // If other type of proxy used it's an indication of security nutcase, leave him alone + if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) { + usingProxy = false; + nlc->proxyType = 0; + Netlib_Logf(nlu, "Fallback to direct connection"); + continue; + } + if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) { + forceHttps = true; + usingProxy = NetlibGetIeProxyConn(nlc, true); + if (usingProxy) + continue; + } + } + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); + return false; + } + + if (usingProxy && !((nloc->flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) { + if (!WaitUntilWritable(nlc->s, 30000)) + return false; + + switch (nlc->proxyType) { + case PROXYTYPE_SOCKS4: + if (!NetlibInitSocks4Connection(nlc, nlu, nloc)) + return false; + break; + + case PROXYTYPE_SOCKS5: + if (!NetlibInitSocks5Connection(nlc, nlu, nloc)) + return false; + break; + + case PROXYTYPE_HTTPS: + case PROXYTYPE_HTTP: + nlc->proxyAuthNeeded = true; + if (!NetlibInitHttpsConnection(nlc, nlu, nloc)) { + usingProxy = false; + if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc)) + return false; + } + break; + + default: + SetLastError(ERROR_INVALID_PARAMETER); + FreePartiallyInitedConnection(nlc); + return false; + } + } + + Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nloc->szHost, nloc->wPort); + + if (GetSubscribersCount((THook*)hEventConnected)) { + NETLIBCONNECTIONEVENTINFO ncei = {}; + ncei.connected = 1; + ncei.szSettingsModule = nlu->user.szSettingsModule; + int size = sizeof(SOCKADDR_IN); + getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size); + if (nlu->settings.useProxy) { + size = sizeof(SOCKADDR_IN); + getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size); + ncei.remote.sin_family = AF_INET; + ncei.remote.sin_port = htons((short)nloc->wPort); + ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nloc->szHost); + } + else { + size = sizeof(SOCKADDR_IN); + getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size); + } + NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); + } + + if (NLOCF_SSL & nloc->flags) + return Netlib_StartSsl(nlc, nullptr) != 0; + + return true; +} + +bool NetlibReconnect(NetlibConnection *nlc) +{ + // a connection might be freed already + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) + return false; + + char buf[4]; + bool opened = nlc->s != INVALID_SOCKET; + if (opened) { + switch (WaitUntilReadable(nlc->s, 0, true)) { + case SOCKET_ERROR: + opened = false; + break; + + case 0: + opened = true; + break; + + case 1: + opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0; + break; + } + + if (!opened) + NetlibDoCloseSocket(nlc, true); + } + + if (!opened) { + if (Miranda_IsTerminated()) + return false; + + return NetlibDoConnect(nlc); + } + return true; +} + +MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const NETLIBOPENCONNECTION *nloc) +{ + if (nloc == nullptr || nloc->szHost == nullptr || nloc->wPort == 0) { + + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING)) + return nullptr; + + Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", nloc->szHost, nloc->wPort, nloc->flags); + + NetlibConnection *nlc = new NetlibConnection(); + nlc->nlu = nlu; + nlc->nloc = *nloc; + nlc->nloc.szHost = mir_strdup(nloc->szHost); + nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0; + + if (!NetlibDoConnect(nlc)) { + FreePartiallyInitedConnection(nlc); + return nullptr; + } + + if (iUPnPCleanup == 0) { + mir_cslock lck(csNetlibUser); + iUPnPCleanup = 1; + mir_forkthread(NetlibUPnPCleanup); + } + + return nlc; +} + +MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost) +{ + NetlibConnection *nlc = (NetlibConnection*)hConnection; + if (nlc == nullptr) + return 0; + + NetlibUser *nlu = nlc->nlu; + if (szHost == nullptr) + szHost = nlc->nloc.szHost; + + Netlib_Logf(nlu, "(%d %s) Starting SSL negotiation", nlc->s, szHost); + nlc->hSsl = sslApi.connect(nlc->s, szHost, nlu->settings.validateSSL); + + if (nlc->hSsl == nullptr) + Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL connection", nlc->s, szHost); + else + Netlib_Logf(nlu, "(%d %s) SSL negotiation successful", nlc->s, szHost); + + return nlc->hSsl != nullptr; +} + +NetlibConnection::NetlibConnection() +{ + handleType = NLH_CONNECTION; + s = s2 = INVALID_SOCKET; + hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); + NetlibInitializeNestedCS(&ncsSend); + NetlibInitializeNestedCS(&ncsRecv); +} + +NetlibConnection::~NetlibConnection() +{ + handleType = 0; + + if (s != INVALID_SOCKET) + closesocket(s); + + mir_free(szNewUrl); + mir_free(szProxyServer); + + mir_free(nlhpi.szHttpPostUrl); + mir_free(nlhpi.szHttpGetUrl); + + mir_free((char*)nloc.szHost); + + NetlibDeleteNestedCS(&ncsSend); + NetlibDeleteNestedCS(&ncsRecv); + + CloseHandle(hOkToCloseEvent); +} diff --git a/src/mir_app/src/netlib_opts.cpp b/src/mir_app/src/netlib_opts.cpp new file mode 100644 index 0000000000..46317d0708 --- /dev/null +++ b/src/mir_app/src/netlib_opts.cpp @@ -0,0 +1,518 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +struct NetlibTempSettings +{ + DWORD flags; + char *szSettingsModule; + NETLIBUSERSETTINGS settings; +}; + +static LIST tempSettings(5); + +static const UINT outgoingConnectionsControls[] = +{ + IDC_STATIC12, + IDC_USEPROXY, + IDC_STATIC21, IDC_PROXYTYPE, + IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, + IDC_PROXYAUTH, + IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, + IDC_PROXYDNS, + IDC_SPECIFYPORTSO, + IDC_PORTSRANGEO, + IDC_STATIC54, + IDC_VALIDATESSL}; +static const UINT useProxyControls[] = { + IDC_STATIC21, IDC_PROXYTYPE, + IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, + IDC_PROXYAUTH, + IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, + IDC_PROXYDNS}; +static const UINT specifyOPortsControls[] = { + IDC_PORTSRANGEO, + IDC_STATIC54 +}; +static const UINT incomingConnectionsControls[] = { + IDC_STATIC43, + IDC_SPECIFYPORTS, + IDC_PORTSRANGE, + IDC_STATIC52, + IDC_ENABLEUPNP}; +static const UINT specifyPortsControls[] = { + IDC_PORTSRANGE, + IDC_STATIC52}; + +static const wchar_t* szProxyTypes[] = {LPGENW(""), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"}; +static const WORD oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080}; + +#define M_REFRESHALL (WM_USER+100) +#define M_REFRESHENABLING (WM_USER+101) + +static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) +{ + for (int i = 0; i < cControls; i++) + ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); +} + +static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) +{ + for (int i = 0; i < cControls; i++) + EnableWindow(GetDlgItem(hwndDlg, controls[i]), state); +} + +static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType) +{ + int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type])); + SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type); + if (type == selectType) + SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0); +} + +static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source) +{ + *dest = *source; + if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); + if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); + if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); + if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); + if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); +} + +static void CombineSettingsStrings(char **dest, char **source) +{ + if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; } +} + +static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, DWORD *destFlags, NETLIBUSERSETTINGS *source, DWORD sourceFlags) +{ + if (sourceFlags & NUF_OUTGOING) { + if (*destFlags & NUF_OUTGOING) { + if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2; + if (dest->useProxy != source->useProxy) dest->useProxy = 2; + if (dest->proxyType != source->proxyType) dest->proxyType = 0; + CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer); + if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0; + if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2; + CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser); + CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword); + if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2; + if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2; + CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts); + } + else { + dest->validateSSL = source->validateSSL; + dest->useProxy = source->useProxy; + dest->proxyType = source->proxyType; + dest->szProxyServer = source->szProxyServer; + if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); + dest->wProxyPort = source->wProxyPort; + dest->useProxyAuth = source->useProxyAuth; + dest->szProxyAuthUser = source->szProxyAuthUser; + if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); + dest->szProxyAuthPassword = source->szProxyAuthPassword; + if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); + dest->dnsThroughProxy = source->dnsThroughProxy; + dest->specifyOutgoingPorts = source->specifyOutgoingPorts; + dest->szOutgoingPorts = source->szOutgoingPorts; + if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); + } + } + if (sourceFlags & NUF_INCOMING) { + if (*destFlags & NUF_INCOMING) { + if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2; + if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2; + CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts); + } + else { + dest->enableUPnP = source->enableUPnP; + dest->specifyIncomingPorts = source->specifyIncomingPorts; + dest->szIncomingPorts = source->szIncomingPorts; + if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); + } + } + if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION)) + *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION; + else *destFlags |= sourceFlags; +} + +static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) +{ + int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED; + CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED); + if (iUser == -1) { + for (auto &p : tempSettings) + if (!(p->flags & NUF_NOOPTIONS)) + *(int*)(((PBYTE)&p->settings) + memberOffset) = newValue; + } + else *(int*)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset) = newValue; + SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); +} + +static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) +{ + int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId)); + char *szNewValue = (char*)mir_alloc(newValueLen+1); + GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1); + if (iUser == -1) { + for (auto &p : tempSettings) { + if (!(p->flags & NUF_NOOPTIONS)) { + char **ppszNew = (char**)(((PBYTE)&p->settings) + memberOffset); + mir_free(*ppszNew); + *ppszNew = mir_strdup(szNewValue); + } + } + mir_free(szNewValue); + } + else { + char **ppszNew = (char**)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset); + mir_free(*ppszNew); + *ppszNew = szNewValue; + } +} + +static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, DWORD flags) +{ + if (flags & NUF_OUTGOING) { + db_set_b(0, szSettingsModule, "NLValidateSSL", (BYTE)settings->validateSSL); + db_set_b(0, szSettingsModule, "NLUseProxy", (BYTE)settings->useProxy); + db_set_b(0, szSettingsModule, "NLProxyType", (BYTE)settings->proxyType); + db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : ""); + db_set_w(0, szSettingsModule, "NLProxyPort", (WORD)settings->wProxyPort); + db_set_b(0, szSettingsModule, "NLUseProxyAuth", (BYTE)settings->useProxyAuth); + db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : ""); + db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : ""); + db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (BYTE)settings->dnsThroughProxy); + db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (BYTE)settings->specifyOutgoingPorts); + db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : ""); + } + if (flags & NUF_INCOMING) { + db_set_b(0, szSettingsModule, "NLEnableUPnP", (BYTE)settings->enableUPnP); + db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (BYTE)settings->specifyIncomingPorts); + db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : ""); + } +} + +void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings) +{ + mir_cslock lck(csNetlibUser); + + NetlibUser tUser; + tUser.user.szSettingsModule = (char*)szSettingsModule; + NetlibUser *thisUser = netlibUser.find(&tUser); + if (thisUser == nullptr) + return; + + NetlibFreeUserSettingsStruct(&thisUser->settings); + CopySettingsStruct(&thisUser->settings, settings); + WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags); + + NETLIBUSERSETTINGS combinedSettings = { 0 }; + combinedSettings.cbSize = sizeof(combinedSettings); + + DWORD flags = 0; + for (auto &p : netlibUser) { + if (p->user.flags & NUF_NOOPTIONS) + continue; + CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags); + } + if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0; + if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0; + if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5; + if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0; + if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1; + if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1; + if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0; + WriteSettingsStructToDb("Netlib", &combinedSettings, flags); + NetlibFreeUserSettingsStruct(&combinedSettings); +} + +static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + int iUser; + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("")); + SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1); + SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0); + + mir_cslock lck(csNetlibUser); + for (auto &it : netlibUser) { + NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings)); + thisSettings->flags = it->user.flags; + thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule); + CopySettingsStruct(&thisSettings->settings, &it->settings); + tempSettings.insert(thisSettings); + + if (it->user.flags & NUF_NOOPTIONS) + continue; + iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w); + SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it)); + } + } + + SendMessage(hwndDlg, M_REFRESHALL, 0, 0); + return TRUE; + + case M_REFRESHALL: + iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); + { + NETLIBUSERSETTINGS settings = { 0 }; + DWORD flags = 0; + + if (iUser == -1) { + settings.cbSize = sizeof(settings); + for (auto &p : tempSettings) + if (!(p->flags & NUF_NOOPTIONS)) + CombineSettingsStructs(&settings, &flags, &p->settings, p->flags); + } + else { + NetlibFreeUserSettingsStruct(&settings); + CopySettingsStruct(&settings, &tempSettings[iUser]->settings); + flags = tempSettings[iUser]->flags; + } + ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE); + CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy); + SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0); + if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType); + AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType); + AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType); + if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType); + if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType); + if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION)) + AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType); + SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : ""); + if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE); + else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, ""); + CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth); + SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : ""); + SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : ""); + CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy); + CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL); + + ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE); + CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts); + SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : ""); + + CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts); + SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : ""); + + CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP); + + NetlibFreeUserSettingsStruct(&settings); + SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); + } + break; + + case M_REFRESHENABLING: + wchar_t str[80]; + { + int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); + mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]); + SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str); + if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) { + int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1; + EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE); + if (selectedProxyType == 0) { + for (auto &p : tempSettings) { + if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING)) + continue; + + if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1; + else { + enableAuth = 1; + if (p->settings.useProxyAuth) + enableUser = enablePass = 1; + } + } + } + else { + if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1; + else { + if (selectedProxyType == PROXYTYPE_IE) enableServer = 0; + enableAuth = 1; + if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED) + enableUser = enablePass = 1; + } + } + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer); + } + else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE); + EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED); + EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED); + } + break; + + case WM_COMMAND: + iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); + switch (LOWORD(wParam)) { + case IDC_NETLIBUSERS: + if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0); + return 0; + + case IDC_LOGOPTIONS: + NetlibLogShowOptions(); + return 0; + + case IDC_PROXYTYPE: + if (HIWORD(wParam) == CBN_SELCHANGE) { + int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); + if (iUser == -1) { + if (newValue == 0) + return 0; + + for (auto &p : tempSettings) { + if (p->flags & NUF_NOOPTIONS) + continue; + if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS)) + p->settings.proxyType = PROXYTYPE_HTTPS; + else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION) + p->settings.proxyType = PROXYTYPE_HTTP; + else p->settings.proxyType = newValue; + } + SendMessage(hwndDlg, M_REFRESHALL, 0, 0); + } + else { + tempSettings[iUser]->settings.proxyType = newValue; + SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); + } + } + break; + case IDC_USEPROXY: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy)); + break; + case IDC_PROXYAUTH: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth)); + break; + case IDC_PROXYDNS: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy)); + break; + case IDC_SPECIFYPORTS: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts)); + break; + case IDC_SPECIFYPORTSO: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts)); + break; + case IDC_ENABLEUPNP: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP)); + break; + case IDC_VALIDATESSL: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL)); + break; + case IDC_PROXYHOST: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer)); + break; + case IDC_PROXYPORT: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + { + int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE); + if (iUser == -1) { + for (auto &p : tempSettings) + if (!(p->flags & NUF_NOOPTIONS)) + p->settings.wProxyPort = newValue; + } + else tempSettings[iUser]->settings.wProxyPort = newValue; + } + break; + case IDC_PROXYUSER: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser)); + break; + case IDC_PROXYPASS: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword)); + break; + case IDC_PORTSRANGE: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts)); + break; + case IDC_PORTSRANGEO: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts)); + break; + } + ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_APPLY: + for (auto &p : tempSettings) + NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings); + return TRUE; + } + break; + } + break; + + case WM_DESTROY: + for (auto &p : tempSettings) { + mir_free(p->szSettingsModule); + NetlibFreeUserSettingsStruct(&p->settings); + mir_free(p); + } + tempSettings.destroy(); + break; + } + return FALSE; +} + +int NetlibOptInitialise(WPARAM wParam, LPARAM) +{ + int optionsCount = 0; + { + mir_cslock lck(csNetlibUser); + for (auto &p : netlibUser) + if (!(p->user.flags & NUF_NOOPTIONS)) + ++optionsCount; + } + + if (optionsCount == 0) + return 0; + + OPTIONSDIALOGPAGE odp = {}; + odp.position = 900000000; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); + odp.szTitle.a = LPGEN("Network"); + odp.pfnDlgProc = DlgProcNetlibOpts; + odp.flags = ODPF_BOLDGROUPS; + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/mir_app/src/netlib_pktrecver.cpp b/src/mir_app/src/netlib_pktrecver.cpp new file mode 100644 index 0000000000..8b1902de7c --- /dev/null +++ b/src/mir_app/src/netlib_pktrecver.cpp @@ -0,0 +1,80 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize) +{ + if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); + nlpr->handleType = NLH_PACKETRECVER; + nlpr->nlc = nlc; + nlpr->packetRecver.bufferSize = iMaxSize; + nlpr->packetRecver.buffer = (PBYTE)mir_alloc(nlpr->packetRecver.bufferSize); + nlpr->packetRecver.bytesUsed = 0; + nlpr->packetRecver.bytesAvailable = 0; + return nlpr; +} + +MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam) +{ + NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver; + if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ + SetLastError(ERROR_TIMEOUT); + return SOCKET_ERROR; + } + nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout; + if (nlprParam->bytesUsed == 0) { + if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) { + nlpr->packetRecver.bytesAvailable = 0; + Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching"); + } + } + else { + memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed); + nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed; + } + + if (nlprParam->dwTimeout != INFINITE) { + if (!sslApi.pending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) { + *nlprParam = nlpr->packetRecver; + return SOCKET_ERROR; + } + } + + INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0); + if (recvResult > 0) + nlpr->packetRecver.bytesAvailable += recvResult; + *nlprParam = nlpr->packetRecver; + return recvResult; +} diff --git a/src/mir_app/src/netlib_security.cpp b/src/mir_app/src/netlib_security.cpp new file mode 100644 index 0000000000..c3b6e11ffa --- /dev/null +++ b/src/mir_app/src/netlib_security.cpp @@ -0,0 +1,363 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#define SECURITY_WIN32 +#include +#include + +#pragma comment(lib, "secur32.lib") + +struct NtlmHandleType +{ + CtxtHandle hClientContext; + CredHandle hClientCredential; + wchar_t* szProvider; + wchar_t* szPrincipal; + unsigned cbMaxToken; + bool hasDomain; +}; + +struct NTLM_String +{ + WORD len; + WORD allocedSpace; + DWORD offset; +}; + +struct NtlmType2packet +{ + char sign[8]; + DWORD type; // == 2 + NTLM_String targetName; + DWORD flags; + BYTE challenge[8]; + BYTE context[8]; + NTLM_String targetInfo; +}; + +static unsigned ntlmCnt = 0; +static mir_cs csSec; + +static void ReportSecError(SECURITY_STATUS scRet, int line) +{ + wchar_t szMsgBuf[256]; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); + + wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0; + + Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal) +{ + HANDLE hSecurity = nullptr; + + if (mir_wstrcmpi(szProvider, L"Basic") == 0) { + NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); + hNtlm->szProvider = mir_wstrdup(szProvider); + SecInvalidateHandle(&hNtlm->hClientContext); + SecInvalidateHandle(&hNtlm->hClientCredential); + ntlmCnt++; + + return hNtlm; + } + + mir_cslock lck(csSec); + + PSecPkgInfo ntlmSecurityPackageInfo; + SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo); + if (sc == SEC_E_OK) { + NtlmHandleType* hNtlm; + + hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); + hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; + FreeContextBuffer(ntlmSecurityPackageInfo); + + hNtlm->szProvider = mir_wstrdup(szProvider); + hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L""); + SecInvalidateHandle(&hNtlm->hClientContext); + SecInvalidateHandle(&hNtlm->hClientCredential); + ntlmCnt++; + } + return hSecurity; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity) +{ + if (hSecurity == nullptr) + return; + + mir_cslock lck(csSec); + + if (ntlmCnt != 0) { + NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; + if (hNtlm != nullptr) { + if (SecIsValidHandle(&hNtlm->hClientContext)) + DeleteSecurityContext(&hNtlm->hClientContext); + if (SecIsValidHandle(&hNtlm->hClientCredential)) + FreeCredentialsHandle(&hNtlm->hClientCredential); + mir_free(hNtlm->szProvider); + mir_free(hNtlm->szPrincipal); + mir_free(hNtlm); + } + + --ntlmCnt; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) +{ + if (!szChallenge || !szChallenge[0]) return nullptr; + + NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; + unsigned char inDataBuffer[1024]; + + SecBuffer inBuffers[2] = + { + { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, + { chlsz, SECBUFFER_STREAM, szChallenge } + }; + + SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; + + unsigned long qop = 0; + SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + // unsigned char LayerMask = inDataBuffer[0]; + // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); + + SecPkgContext_Sizes sizes; + sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); + unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); + + unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; + + SecBuffer outBuffers[3] = + { + { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, + { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, + { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } + }; + SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; + + sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + unsigned i, ressz = 0; + for (i = 0; i < outBuffersDesc.cBuffers; i++) + ressz += outBuffersDesc.pBuffers[i].cbBuffer; + + unsigned char *response = (unsigned char*)alloca(ressz), *p = response; + for (i = 0; i < outBuffersDesc.cBuffers; i++) { + memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); + p += outBuffersDesc.pBuffers[i].cbBuffer; + } + + return mir_base64_encode(response, ressz); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete) +{ + if (hSecurity == nullptr || ntlmCnt == 0) + return nullptr; + + SecBufferDesc outputBufferDescriptor, inputBufferDescriptor; + SecBuffer outputSecurityToken, inputSecurityToken; + char *szOutputToken; + + NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity; + + Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete); + + if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) { + bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0; + bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0'; + if (hasChallenge) { + size_t tokenLen; + BYTE *token = (BYTE*)mir_base64_decode(szChallenge, &tokenLen); + if (token == nullptr) + return nullptr; + + if (isGSSAPI && complete) + return CompleteGssapi(hSecurity, token, (unsigned)tokenLen); + + inputBufferDescriptor.cBuffers = 1; + inputBufferDescriptor.pBuffers = &inputSecurityToken; + inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; + inputSecurityToken.BufferType = SECBUFFER_TOKEN; + inputSecurityToken.cbBuffer = (unsigned)tokenLen; + inputSecurityToken.pvBuffer = token; + + // try to decode the domain name from the NTLM challenge + if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) { + NtlmType2packet* pkt = (NtlmType2packet*)token; + if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { + + wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset]; + int domainLen = pkt->targetName.len; + + // Negotiate ANSI? if yes, convert the ANSI name to unicode + if ((pkt->flags & 1) == 0) { + int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0); + wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t)); + domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; + buf[domainLen] = 0; + domainName = buf; + } + else domainLen /= sizeof(wchar_t); + + if (domainLen) { + CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login); + char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete); + mir_free(szChl); + } + } + } + } + else { + if (SecIsValidHandle(&hNtlm->hClientContext)) + DeleteSecurityContext(&hNtlm->hClientContext); + if (SecIsValidHandle(&hNtlm->hClientCredential)) + FreeCredentialsHandle(&hNtlm->hClientCredential); + + SEC_WINNT_AUTH_IDENTITY auth; + + if (login != nullptr && login[0] != '\0') { + memset(&auth, 0, sizeof(auth)); + + Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); + + const wchar_t* loginName = login; + const wchar_t* domainName = wcschr(login, '\\'); + size_t domainLen = 0; + size_t loginLen = mir_wstrlen(loginName); + if (domainName != nullptr) { + loginName = domainName + 1; + loginLen = mir_wstrlen(loginName); + domainLen = domainName - login; + domainName = login; + } + else if ((domainName = wcschr(login, '@')) != nullptr) { + loginName = login; + loginLen = domainName - login; + domainLen = mir_wstrlen(++domainName); + } + + auth.User = (PWORD)loginName; + auth.UserLength = (ULONG)loginLen; + auth.Password = (PWORD)psw; + auth.PasswordLength = (ULONG)mir_wstrlen(psw); + auth.Domain = (PWORD)domainName; + auth.DomainLength = (ULONG)domainLen; + auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + + hNtlm->hasDomain = domainLen != 0; + } + + TimeStamp tokenExpiration; + SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + } + + outputBufferDescriptor.cBuffers = 1; + outputBufferDescriptor.pBuffers = &outputSecurityToken; + outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; + outputSecurityToken.BufferType = SECBUFFER_TOKEN; + outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; + outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); + + ULONG contextAttributes; + TimeStamp tokenExpiration; + SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential, + hasChallenge ? &hNtlm->hClientContext : nullptr, + hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, + hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext, + &outputBufferDescriptor, &contextAttributes, &tokenExpiration); + Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc); + + complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); + if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { + sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); + Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc); + } + + if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer); + } + else { + if (!login || !psw) + return nullptr; + + T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw)); + szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth)); + complete = true; + } + + if (szOutputToken == nullptr) + return nullptr; + + if (!http) + return szOutputToken; + + CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken); + mir_free(szOutputToken); + return szResult.Detach(); +} + +MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete) +{ + return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete); +} diff --git a/src/mir_app/src/netlib_sock.cpp b/src/mir_app/src/netlib_sock.cpp new file mode 100644 index 0000000000..9ccb53258e --- /dev/null +++ b/src/mir_app/src/netlib_sock.cpp @@ -0,0 +1,290 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +extern HANDLE hConnectionHeaderMutex, hSendEvent, hRecvEvent; + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags) +{ + if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) + return SOCKET_ERROR; + + int result; + NetlibDumpData(nlc, (PBYTE)buf, len, 1, flags); + if (nlc->hSsl) + result = sslApi.write(nlc->hSsl, buf, len); + else + result = send(nlc->s, buf, len, flags & 0xFFFF); + + NetlibLeaveNestedCS(&nlc->ncsSend); + + NETLIBNOTIFY nln = { buf, len, flags, result }; + NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags) +{ + if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) + return SOCKET_ERROR; + + int recvResult; + if (!nlc->foreBuf.isEmpty()) { + recvResult = min(len, (int)nlc->foreBuf.length()); + memcpy(buf, nlc->foreBuf.data(), recvResult); + nlc->foreBuf.remove(recvResult); + } + else if (nlc->hSsl) + recvResult = sslApi.read(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0); + else + recvResult = recv(nlc->s, buf, len, flags & 0xFFFF); + + NetlibLeaveNestedCS(&nlc->ncsRecv); + if (recvResult <= 0) + return recvResult; + + NetlibDumpData(nlc, (PBYTE)buf, recvResult, 0, flags); + + if ((flags & MSG_PEEK) == 0) { + NETLIBNOTIFY nln = { buf, len, flags, recvResult }; + NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); + } + return recvResult; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending) +{ + FD_ZERO(fd); + for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) { + NetlibConnection *nlcCheck = hConns[i]; + if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) { + SetLastError(ERROR_INVALID_DATA); + return 0; + } + FD_SET(nlcCheck->s, fd); + if (!nlcCheck->foreBuf.isEmpty() || sslApi.pending(nlcCheck->hSsl)) + pending++; + } + return 1; +} + +MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls) +{ + if (nls == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + int pending = 0; + fd_set readfd, writefd, exceptfd; + WaitForSingleObject(hConnectionHeaderMutex, INFINITE); + if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) + || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) + || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) + { + ReleaseMutex(hConnectionHeaderMutex); + return SOCKET_ERROR; + } + ReleaseMutex(hConnectionHeaderMutex); + if (pending) + return 1; + + TIMEVAL tv; + tv.tv_sec = nls->dwTimeout / 1000; + tv.tv_usec = (nls->dwTimeout % 1000) * 1000; + return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); +} + +MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls) +{ + if (nls == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + TIMEVAL tv; + tv.tv_sec = nls->dwTimeout / 1000; + tv.tv_usec = (nls->dwTimeout % 1000) * 1000; + WaitForSingleObject(hConnectionHeaderMutex, INFINITE); + + int pending = 0; + fd_set readfd, writefd, exceptfd; + if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) + || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) + || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) + { + ReleaseMutex(hConnectionHeaderMutex); + return SOCKET_ERROR; + } + ReleaseMutex(hConnectionHeaderMutex); + + int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); + + WaitForSingleObject(hConnectionHeaderMutex, INFINITE); + /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() + to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) + This happens for read/write/except */ + NetlibConnection *conn = nullptr; + int j; + for (j = 0; j < FD_SETSIZE; j++) { + conn = (NetlibConnection*)nls->hReadConns[j]; + if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; + + if (sslApi.pending(conn->hSsl)) + nls->hReadStatus[j] = TRUE; + nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd); + } + + for (j = 0; j < FD_SETSIZE; j++) { + conn = (NetlibConnection*)nls->hWriteConns[j]; + if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; + nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd); + } + + for (j = 0; j < FD_SETSIZE; j++) { + conn = (NetlibConnection*)nls->hExceptConns[j]; + if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; + nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd); + } + ReleaseMutex(hConnectionHeaderMutex); + return rc; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr) +{ + if (!str) return false; + + int len = sizeof(SOCKADDR_INET_M); + return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len); +} + +MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr) +{ + char saddr[128]; + DWORD len = sizeof(saddr); + if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len)) + return mir_strdup(saddr); + + if (addr->sin_family == AF_INET) { + char *szIp = inet_ntoa(addr->sin_addr); + if (addr->sin_port != 0) { + mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port); + return mir_strdup(saddr); + } + return mir_strdup(szIp); + } + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN hConnection, NETLIBCONNINFO *connInfo) +{ + NetlibConnection *nlc = (NetlibConnection*)hConnection; + if (!nlc || !connInfo) + return 1; + + sockaddr_in sin = { 0 }; + int len = sizeof(sin); + if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) { + connInfo->wPort = ntohs(sin.sin_port); + connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0; + strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE); + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +inline bool IsAddrGlobal(const IN6_ADDR *a) +{ + unsigned char High = a->s6_bytes[0] & 0xf0; + return High != 0 && High != 0xf0; +} + +MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly) +{ + addrinfo *air = nullptr, *ai, hints = { 0 }; + const char *szMyHost = ""; + + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + + if (GetAddrInfoA(szMyHost, nullptr, &hints, &air)) + return nullptr; + + unsigned n = 0; + for (ai = air; ai; ai = ai->ai_next) { + SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr; + if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) + ++n; + } + + NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); + addr->cbNum = n; + + unsigned i = 0; + for (ai = air; ai; ai = ai->ai_next) { + sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr; + if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) { + char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr); + if (szIp) + strncpy_s(addr->szIp[i++], szIp, _TRUNCATE); + mir_free(szIp); + } + } + FreeAddrInfoA(air); + return addr; +} + +static NETLIBIPLIST* GetMyIpv4(void) +{ + char hostname[256] = ""; + + gethostname(hostname, sizeof(hostname)); + PHOSTENT he = gethostbyname(hostname); + + unsigned n; + for (n = 0; he->h_addr_list[n]; ++n) + ; + + NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); + addr->cbNum = n; + + for (unsigned i = 0; i < n; i++) + strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE); + + return addr; +} diff --git a/src/mir_app/src/netlib_upnp.cpp b/src/mir_app/src/netlib_upnp.cpp new file mode 100644 index 0000000000..dfd409ee1f --- /dev/null +++ b/src/mir_app/src/netlib_upnp.cpp @@ -0,0 +1,813 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +static const char search_request_msg[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 1\r\n" + "ST: urn:schemas-upnp-org:service:%s\r\n" + "\r\n"; + +static const char xml_get_hdr[] = + "GET %s HTTP/1.1\r\n" + "HOST: %s:%u\r\n" + "ACCEPT-LANGUAGE: *\r\n\r\n"; + +static const char soap_post_hdr[] = + "POST %s HTTP/1.1\r\n" + "HOST: %s:%u\r\n" + "CONTENT-LENGTH: %u\r\n" + "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" + "SOAPACTION: \"%s#%s\"\r\n\r\n" + "%s"; + +static const char soap_post_hdr_m[] = + "M-POST %s URL HTTP/1.1\r\n" + "HOST: %s:%u\r\n" + "CONTENT-LENGTH: %u\r\n" + "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" + "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n" + "01-SOAPACTION: \"%s#%s\"\r\n\r\n" + "%s"; + +static const char search_device[] = + "%s"; + +static const char soap_action[] = + "\r\n" + "\r\n" + " \r\n" + " \r\n" + "%s" + " \r\n" + " \r\n" + "\r\n"; + +static const char soap_query[] = + "\r\n" + " \r\n" + " \r\n" + " %s\r\n" + " \r\n" + " \r\n" + "\r\n"; + +static const char add_port_mapping[] = + " \r\n" + " %i\r\n" + " %s\r\n" + " %i\r\n" + " %s\r\n" + " 1\r\n" + " Miranda\r\n" + " 0\r\n"; + +static const char delete_port_mapping[] = + " \r\n" + " %i\r\n" + " %s\r\n"; + +static const char get_port_mapping[] = + " %i\r\n"; + +static bool gatewayFound; +static SOCKADDR_IN locIP; +static time_t lastDiscTime; +static int expireTime = 120; + +static int retryCount; +static SOCKET sock = INVALID_SOCKET; +static char szConnHost[256]; +static unsigned short sConnPort; + +static WORD *portList; +static unsigned numports, numportsAlloc; +static HANDLE portListMutex; + +static char szCtlUrl[256], szDev[256]; + +typedef enum +{ + DeviceGetReq, + ControlAction, + ControlQuery +} ReqType; + +static bool txtParseParam(char* szData, char* presearch, + char* start, char* finish, char* param, size_t size) +{ + char *cp, *cp1; + size_t len; + + *param = 0; + + if (presearch != nullptr) { + cp1 = strstr(szData, presearch); + if (cp1 == nullptr) return false; + } + else + cp1 = szData; + + cp = strstr(cp1, start); + if (cp == nullptr) return false; + cp += mir_strlen(start); + while (*cp == ' ') ++cp; + + cp1 = strstr(cp, finish); + if (cp1 == nullptr) return false; + while (*(cp1-1) == ' ' && cp1 > cp) --cp1; + + len = min((size_t)(cp1 - cp), size-1); + strncpy(param, cp, len); + param[len] = 0; + + return true; +} + +void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath) +{ + char *ppath, *phost, *pport; + int sz; + + phost = strstr(szUrl, "://"); + if (phost == nullptr) phost = szUrl; + else phost += 3; + + ppath = strchr(phost, '/'); + if (ppath == nullptr) ppath = phost + mir_strlen(phost); + + pport = strchr(phost, ':'); + if (pport == nullptr) pport = ppath; + + if (szHost != nullptr) { + sz = pport - phost + 1; + if (sz > 256) sz = 256; + strncpy(szHost, phost, sz); + szHost[sz - 1] = 0; + } + + if (sPort != nullptr) { + if (pport < ppath) { + long prt = atol(pport + 1); + *sPort = prt != 0 ? (unsigned short)prt : 80; + } + else + *sPort = 80; + } + + if (szPath != nullptr) { + strncpy(szPath, ppath, 256); + szPath[255] = 0; + } +} + +static void LongLog(char* szData) +{ + Netlib_Logf(nullptr, szData); +} + +static void closeRouterConnection(void) +{ + if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } +} + +static void validateSocket(void) +{ + static const TIMEVAL tv = { 0, 0 }; + fd_set rfd; + char buf[4]; + + if (sock == INVALID_SOCKET) + return; + + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + + bool opened = false; + switch (select(1, &rfd, nullptr, nullptr, &tv)) { + case 0: + opened = true; + break; + + case 1: + opened = recv(sock, buf, 1, MSG_PEEK) > 0; + break; + } + + if (!opened) + closeRouterConnection(); +} + +static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype) +{ + // Parse URL + char szHost[256], szPath[256], szRes[16]; + int sz = 0, res = 0; + unsigned short sPort; + bool needClose = false; + + const char* szPostHdr = soap_post_hdr; + char* szData = (char*)mir_alloc(4096); + char* szReq = nullptr; + + parseURL(szUrl, szHost, &sPort, szPath); + + if (sPort != sConnPort || _stricmp(szHost, szConnHost)) + closeRouterConnection(); + else + validateSocket(); + + while (true) { + retryCount = 0; + switch (reqtype) { + case DeviceGetReq: + sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort); + break; + + case ControlAction: + { + char szData1[1024]; + + szReq = mir_strdup(szResult); + sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName); + sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1); + } + break; + + case ControlQuery: + { + char szData1[1024]; + sz = mir_snprintf(szData1, soap_query, szActionName); + sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1); + } + break; + } + szResult[0] = 0; + { + static const TIMEVAL tv = { 6, 0 }; + static unsigned ttl = 4; + static u_long mode = 1; + fd_set rfd, wfd, efd; + SOCKADDR_IN enetaddr; + +retrycon: + if (sock == INVALID_SOCKET) { + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + enetaddr.sin_family = AF_INET; + enetaddr.sin_port = htons(sPort); + enetaddr.sin_addr.s_addr = inet_addr(szHost); + + // Resolve host name if needed + if (enetaddr.sin_addr.s_addr == INADDR_NONE) { + PHOSTENT he = gethostbyname(szHost); + if (he) + enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; + } + + Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort); + + FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); + FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd); + + // Limit the scope of the connection (does not work for + setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); + + // Put socket into non-blocking mode for timeout on connect + ioctlsocket(sock, FIONBIO, &mode); + + // Connect to the remote host + if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) { + int err = WSAGetLastError(); + + // Socket connection failed + if (err != WSAEWOULDBLOCK) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP connect failed %d", err); + break; + } + // Wait for socket to connect + else if (select(1, &rfd, &wfd, &efd, &tv) != 1) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP connect timeout"); + break; + } + else if (!FD_ISSET(sock, &wfd)) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP connect failed"); + break; + } + } + strncpy_s(szConnHost, szHost, _TRUNCATE); + sConnPort = sPort; + } + + if (send(sock, szData, sz, 0) != SOCKET_ERROR) { + char *hdrend = nullptr; + int acksz = 0, pktsz = 0; + + if (szActionName == nullptr) { + int len = sizeof(locIP); + getsockname(sock, (SOCKADDR*)&locIP, &len); + if (locIP.sin_addr.S_un.S_addr == 0x0100007f) { + struct hostent *he; + + gethostname(szPath, sizeof(szPath)); + he = gethostbyname(szPath); + if (he != nullptr) + locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0]; + } + } + + LongLog(szData); + sz = 0; + while (true) { + int bytesRecv; + + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + + // Wait for the next packet + if (select(1, &rfd, nullptr, nullptr, &tv) != 1) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP recieve timeout"); + break; + } + + // + bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0); + + // Connection closed or aborted, all data received + if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) { + closeRouterConnection(); + if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) { + ++retryCount; + goto retrycon; + } + break; + } + + sz += bytesRecv; + + // Insert null terminator to use string functions + if (sz >= (resSize - 1)) { + szResult[resSize - 1] = 0; + break; + } + else + szResult[sz] = 0; + + // HTTP header found? + if (hdrend == nullptr) { + // Find HTTP header end + hdrend = strstr(szResult, "\r\n\r\n"); + if (hdrend == nullptr) { + hdrend = strstr(szResult, "\n\n"); + if (hdrend) hdrend += 2; + } + + else + hdrend += 4; + + if (hdrend != nullptr) { + // Get packet size if provided + if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) || + txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) { + // Add size of HTTP header to the packet size to compute full transmission size + pktsz = atol(ltrimp(szRes)) + (hdrend - szResult); + } + // Get encoding type if provided + else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) { + if (_stricmp(lrtrimp(szRes), "Chunked") == 0) + acksz = hdrend - szResult; + } + if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) { + needClose = (_stricmp(lrtrimp(szRes), "close") == 0); + } + } + } + + // Content-Length bytes reached, all data received + if (sz >= pktsz && pktsz != 0) { + szResult[pktsz] = 0; + break; + } + + // Chunked encoding processing + if (sz > acksz && acksz != 0) { +retry: + // Parse out chunk size + char* data = szResult + acksz; + char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n'); + if (peol1 != nullptr) { + char *peol2 = strchr(++peol1, '\n'); + if (peol2 != nullptr) { + // Get chunk size + int chunkBytes = strtol(peol1, nullptr, 16); + acksz += chunkBytes; + peol2++; + + memmove(data, peol2, mir_strlen(peol2) + 1); + sz -= peol2 - data; + + // Last chunk, all data received + if (chunkBytes == 0) break; + if (sz > acksz) goto retry; + } + } + } + } + LongLog(szResult); + } + else { + if (retryCount < 2) { + closeRouterConnection(); + ++retryCount; + goto retrycon; + } + else + Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError()); + } + } + txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); + res = atol(szRes); + if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr) + szPostHdr = soap_post_hdr_m; + else + break; + } + + if (needClose) + closeRouterConnection(); + + mir_free(szData); + mir_free(szReq); + return res; +} + +static unsigned getExtIP(void) +{ + char szExtIP[30]; + char* szData = (char*)mir_alloc(4096); szData[0] = 0; + + unsigned extip = 0; + int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction); + if (res == 200 && txtParseParam(szData, "", "<", szExtIP, sizeof(szExtIP))) + extip = ntohl(inet_addr(szExtIP)); + + mir_free(szData); + return extip; +} + +static bool getUPnPURLs(char* szUrl, size_t sizeUrl) +{ + char* szData = (char*)mir_alloc(8192); + + gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200; + if (gatewayFound) { + char szTemp[256], *rpth; + size_t ctlLen; + + txtParseParam(szData, nullptr, "", "", szTemp, sizeof(szTemp)); + strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl)); + szCtlUrl[sizeof(szCtlUrl) - 1] = 0; + + mir_snprintf(szTemp, search_device, szDev); + txtParseParam(szData, szTemp, "", "", szUrl, sizeUrl); + + // URL combining per RFC 2396 + if (szUrl[0] != 0) { + if (strstr(szUrl, "://") != nullptr) // absolute URI + rpth = szCtlUrl; + else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path + { + rpth = strstr(szCtlUrl, "//"); + if (rpth == nullptr) rpth = szCtlUrl; + } + else if (szUrl[0] == '/') // relative URI abs_path + { + rpth = strstr(szCtlUrl, "//"); + rpth = rpth ? rpth + 2 : szCtlUrl; + + rpth = strchr(rpth, '/'); + if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl); + } + else { // relative URI rel_path + size_t ctlCLen = mir_strlen(szCtlUrl); + rpth = szCtlUrl + ctlCLen; + if (ctlCLen != 0 && *(rpth - 1) != '/') + strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen); + } + + ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl); + strncpy(rpth, szUrl, ctlLen); + szCtlUrl[sizeof(szCtlUrl) - 1] = 0; + } + else { + szCtlUrl[0] = 0; + gatewayFound = false; + } + } + mir_free(szData); + + return gatewayFound; +} + +static void discoverUPnP(void) +{ + char* buf; + int buflen; + unsigned i, j, nip = 0; + unsigned* ips = nullptr; + + static const unsigned any = INADDR_ANY; + static const TIMEVAL tv = { 1, 600000 }; + + char szUrl[256] = ""; + char hostname[256]; + PHOSTENT he; + fd_set readfd; + + SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + SOCKADDR_IN enetaddr; + enetaddr.sin_family = AF_INET; + enetaddr.sin_port = htons(1900); + enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); + + gethostname(hostname, sizeof(hostname)); + he = gethostbyname(hostname); + + if (he) { + while (he->h_addr_list[nip]) ++nip; + + ips = (unsigned*)mir_alloc(nip * sizeof(unsigned)); + + for (j = 0; j < nip; j++) + ips[j] = *(unsigned*)he->h_addr_list[j]; + } + + buf = (char*)mir_alloc(1500); + + for (i = 3; --i && szUrl[0] == 0;) { + for (j = 0; j < nip; j++) { + if (ips) + setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned)); + + buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1"); + sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); + LongLog(buf); + + buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1"); + sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); + LongLog(buf); + } + + if (Miranda_IsTerminated()) break; + + FD_ZERO(&readfd); + FD_SET(s, &readfd); + + while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) { + buflen = recv(s, buf, 1500, 0); + if (buflen != SOCKET_ERROR) { + buf[buflen] = 0; + LongLog(buf); + + if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) || + txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) { + char age[30]; + char szHostNew[256], szHostExist[256]; + + lrtrim(szUrl); + + parseURL(szUrl, szHostNew, nullptr, nullptr); + parseURL(szCtlUrl, szHostExist, nullptr, nullptr); + if (mir_strcmp(szHostNew, szHostExist) == 0) { + gatewayFound = true; + break; + } + + txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev)); + txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age)); + expireTime = atoi(lrtrimp(age)); + lrtrim(szDev); + + if (getUPnPURLs(szUrl, sizeof(szUrl))) { + gatewayFound = getExtIP() != 0; + if (gatewayFound) break; + } + } + } + FD_ZERO(&readfd); + FD_SET(s, &readfd); + } + } + + mir_free(buf); + mir_free(ips); + setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned)); + closesocket(s); +} + +static bool findUPnPGateway(void) +{ + if ((time(0) - lastDiscTime) >= expireTime) { + WaitForSingleObject(portListMutex, INFINITE); + + time_t curTime = time(0); + + if ((curTime - lastDiscTime) >= expireTime) { + gatewayFound = false; + + discoverUPnP(); + lastDiscTime = curTime; + + Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl); + } + + ReleaseMutex(portListMutex); + } + + return gatewayFound; +} + +bool NetlibUPnPAddPortMapping(WORD intport, char *proto, WORD *extport, DWORD *extip, bool search) +{ + int res = 0, i = 5; + + if (findUPnPGateway()) { + char* szData = (char*)mir_alloc(4096); + char szExtIP[30]; + + *extport = intport - 1; + *extip = ntohl(locIP.sin_addr.S_un.S_addr); + + WaitForSingleObject(portListMutex, INFINITE); + + do { + ++*extport; + mir_snprintf(szData, 4096, add_port_mapping, + *extport, proto, intport, inet_ntoa(locIP.sin_addr)); + res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction); + txtParseParam(szData, nullptr, "", "", szExtIP, sizeof(szExtIP)); + + } while (search && res == 500 && atol(szExtIP) == 718 && --i); + + mir_free(szData); + + if (res == 200) { + unsigned ip = getExtIP(); + if (ip) *extip = ip; + + if (numports >= numportsAlloc) + mir_realloc(portList, sizeof(WORD)*(numportsAlloc += 10)); + portList[numports++] = *extport; + } + + ReleaseMutex(portListMutex); + } + + return res == 200; +} + +void NetlibUPnPDeletePortMapping(WORD extport, char* proto) +{ + if (extport == 0) + return; + + // findUPnPGateway(); + + if (gatewayFound) { + unsigned i; + char* szData = (char*)mir_alloc(4096); + + WaitForSingleObject(portListMutex, INFINITE); + mir_snprintf(szData, 4096, delete_port_mapping, extport, proto); + httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction); + + for (i = 0; i < numports; i++) + if (portList[i] == extport && --numports > 0) + memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(WORD)); + + mir_free(szData); + ReleaseMutex(portListMutex); + } +} + +void NetlibUPnPCleanup(void*) +{ + // upnp is disabled globally, no need for a cleanup + if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0) + return; + + { + int incoming = 0; + mir_cslock lck(csNetlibUser); + for (auto &p : netlibUser) + if (p->user.flags & NUF_INCOMING) { + incoming = 1; + break; + } + + if (!incoming) + return; + } + + if (findUPnPGateway()) { + char *szData = (char*)alloca(4096); + char buf[50], lip[50]; + unsigned j = 0, k, num = 100; + + strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE); + + WaitForSingleObject(portListMutex, INFINITE); + + if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 && + txtParseParam(szData, "QueryStateVariableResponse", "", "<", buf, sizeof(buf))) + num = atol(buf); + + WORD ports[30]; + for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) { + mir_snprintf(szData, 4096, get_port_mapping, i); + + ReleaseMutex(portListMutex); + WaitForSingleObject(portListMutex, INFINITE); + + if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200) + break; + + if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0) + continue; + + if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0) + continue; + + if (txtParseParam(szData, "", "<", buf, sizeof(buf))) { + WORD mport = (WORD)atol(buf); + + if (j >= _countof(ports)) + break; + + for (k = 0; k < numports; ++k) + if (portList[k] == mport) + break; + + if (k >= numports) + ports[j++] = mport; + } + } + + ReleaseMutex(portListMutex); + + for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++) + NetlibUPnPDeletePortMapping(ports[i], "TCP"); + } +} + +void NetlibUPnPInit(void) +{ + numports = 0; + numportsAlloc = 10; + portList = (WORD*)mir_alloc(sizeof(WORD)*numportsAlloc); + + portListMutex = CreateMutex(nullptr, FALSE, nullptr); +} + +void NetlibUPnPDestroy(void) +{ + mir_free(portList); + CloseHandle(portListMutex); +} diff --git a/src/mir_app/src/netlib_websocket.cpp b/src/mir_app/src/netlib_websocket.cpp new file mode 100644 index 0000000000..64b5fda069 --- /dev/null +++ b/src/mir_app/src/netlib_websocket.cpp @@ -0,0 +1,185 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#include "../../libs/zlib/src/zlib.h" + +MIR_APP_DLL(HNETLIBCONN) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost) +{ + CMStringA tmpHost(szHost); + + // connect to the gateway server + if (!mir_strncmp(tmpHost, "wss://", 6)) + tmpHost.Delete(0, 6); + + NETLIBOPENCONNECTION conn = {}; + conn.flags = NLOCF_V2 | NLOCF_SSL; + conn.timeout = 5; + + int pos = tmpHost.Find(':'); + if (pos != -1) { + conn.wPort = atoi(tmpHost.GetBuffer() + pos + 1); + tmpHost.Truncate(pos); + } + else conn.wPort = 443; + + CMStringA args; + if ((pos = tmpHost.Find('/')) != -1) { + args = tmpHost.Mid(pos); + tmpHost.Truncate(pos); + } + + conn.szHost = tmpHost; + + HNETLIBCONN res = Netlib_OpenConnection(nlu, &conn); + if (res == nullptr) { + Netlib_Logf(nlu, "WebSocket connection failed to connect to %s:%d, exiting", tmpHost.c_str(), conn.wPort); + return false; + } + + CMStringA szBuf; + szBuf.AppendFormat("GET https://%s%s HTTP/1.1\r\n", tmpHost.c_str(), args.c_str()); + szBuf.AppendFormat("Host: %s\r\n", tmpHost.c_str()); + szBuf.AppendFormat("Upgrade: websocket\r\n"); + szBuf.AppendFormat("Pragma: no-cache\r\n"); + szBuf.AppendFormat("Cache-Control: no-cache\r\n"); + szBuf.AppendFormat("Connection: Upgrade\r\n"); + szBuf.AppendFormat("Sec-WebSocket-Key: KFShSwLlp4E6C7JZc5h4sg==\r\n"); + szBuf.AppendFormat("Sec-WebSocket-Version: 13\r\n"); + szBuf.AppendFormat("Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"); + szBuf.AppendFormat("\r\n"); + if (Netlib_Send(res, szBuf, szBuf.GetLength(), MSG_DUMPASTEXT) == SOCKET_ERROR) { + Netlib_Logf(nlu, "Error establishing WebSocket connection to %s:%d, send failed", tmpHost.c_str(), conn.wPort); + Netlib_CloseHandle(res); + return nullptr; + } + + char buf[1024]; + int bufSize = Netlib_Recv(res, buf, _countof(buf), MSG_DUMPASTEXT); + if (bufSize <= 0) { + Netlib_Logf(nlu, "Error establishing WebSocket connection to %s:%d, read failed", tmpHost.c_str(), conn.wPort); + Netlib_CloseHandle(res); + return nullptr; + } + + int status = 0; + if (sscanf(buf, "HTTP/1.1 %d", &status) != 1 || status != 101) { + Netlib_Logf(nlu, "Error establishing WebSocket connection to %s:%d, status %d", tmpHost.c_str(), conn.wPort, status); + Netlib_CloseHandle(res); + return nullptr; + } + + return res; +} + +MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize) +{ + if (bufSize < 2) + return false; + + auto *buf = (const BYTE *)pData; + hdr.bIsFinal = (buf[0] & 0x80) != 0; + hdr.bIsMasked = (buf[1] & 0x80) != 0; + hdr.opCode = buf[0] & 0x0F; + hdr.firstByte = buf[1] & 0x7F; + hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0); + if (bufSize < hdr.headerSize) + return false; + + uint64_t tmpSize = 0; + switch (hdr.firstByte) { + case 0x7F: + tmpSize += ((uint64_t)buf[2]) << 56; + tmpSize += ((uint64_t)buf[3]) << 48; + tmpSize += ((uint64_t)buf[4]) << 40; + tmpSize += ((uint64_t)buf[5]) << 32; + tmpSize += ((uint64_t)buf[6]) << 24; + tmpSize += ((uint64_t)buf[7]) << 16; + tmpSize += ((uint64_t)buf[8]) << 8; + tmpSize += ((uint64_t)buf[9]); + break; + + case 0x7E: + tmpSize += ((uint64_t)buf[2]) << 8; + tmpSize += ((uint64_t)buf[3]); + break; + + default: + tmpSize = hdr.firstByte; + } + hdr.payloadSize = tmpSize; + return true; +} + +MIR_APP_DLL(void) WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t strLen) +{ + BYTE header[20]; + int opCode = 1; + size_t datalen; + + header[0] = 0x80 + (opCode & 0x7F); + if (strLen < 126) { + header[1] = (strLen & 0xFF); + datalen = 2; + } + else if (strLen < 65536) { + header[1] = 0x7E; + header[2] = (strLen >> 8) & 0xFF; + header[3] = strLen & 0xFF; + datalen = 4; + } + else { + header[1] = 0x7F; + header[2] = (strLen >> 56) & 0xff; + header[3] = (strLen >> 48) & 0xff; + header[4] = (strLen >> 40) & 0xff; + header[5] = (strLen >> 32) & 0xff; + header[6] = (strLen >> 24) & 0xff; + header[7] = (strLen >> 16) & 0xff; + header[8] = (strLen >> 8) & 0xff; + header[9] = strLen & 0xff; + datalen = 10; + } + + union { + uint32_t dwMask; + uint8_t arMask[4]; + }; + + dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)strLen); + memcpy(header + datalen, arMask, _countof(arMask)); + datalen += _countof(arMask); + header[1] |= 0x80; + + ptrA sendBuf((char*)mir_alloc(strLen + datalen)); + memcpy(sendBuf, header, datalen); + if (strLen) { + memcpy(sendBuf.get() + datalen, pData, strLen); + for (size_t i = 0; i < strLen; i++) + sendBuf[i + datalen] ^= arMask[i & 3]; + } + Netlib_Send(nlc, sendBuf, int(strLen + datalen), 0); +} diff --git a/src/mir_app/src/netlibautoproxy.cpp b/src/mir_app/src/netlibautoproxy.cpp deleted file mode 100644 index 63f823c8fd..0000000000 --- a/src/mir_app/src/netlibautoproxy.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include - -///////////////////////////////////////////////////////////////////////////////////////// -// local module data - -static char *szProxyHost[3]; -static LIST proxyBypass(5); - -static HMODULE hModJS; - -static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll; -static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll; -static pfnInternetGetProxyInfo pInternetGetProxyInfo; - -static bool bEnabled, bOneProxy; - -///////////////////////////////////////////////////////////////////////////////////////// - -static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf) -{ - NetlibUser nlu = {}; - nlu.handleType = NLH_USER; - nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS; - nlu.user.szSettingsModule = "(NULL)"; - nlu.toLog = 1; - - // initialize the netlib request - NETLIBHTTPREQUEST nlhr = {}; - nlhr.cbSize = sizeof(nlhr); - nlhr.requestType = REQUEST_GET; - nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT; - nlhr.szUrl = szUrl; - - // download the page - NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr)); - if (nlhrReply) { - if (nlhrReply->resultCode == 200) { - buf.lpszScriptBuffer = nlhrReply->pData; - buf.dwScriptBufferSize = nlhrReply->dataLength + 1; - - nlhrReply->dataLength = 0; - nlhrReply->pData = nullptr; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps) -{ - bool noHttp = false; - bool usingSsl = false; - char szUrl[1024]; - - if ((nlc->nloc.flags & NLOCF_HTTP) && (nlc->nloc.flags & NLOCF_SSL) || nlc->nloc.wPort == 443 || forceHttps) { - mir_snprintf(szUrl, "https://%s", nlc->nloc.szHost); - usingSsl = true; - } - else if ((nlc->nloc.flags & NLOCF_HTTP)) - mir_snprintf(szUrl, "http://%s", nlc->nloc.szHost); - else { - strncpy_s(szUrl, nlc->nloc.szHost, _TRUNCATE); - noHttp = true; - } - - mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr; - nlc->wProxyPort = 0; - nlc->proxyType = 0; - - char *mt = NetlibGetIeProxy(szUrl); - char *m = NEWSTR_ALLOCA(mt); - mir_free(mt); - - if (m == nullptr) - return false; - - // if multiple servers, use the first one - char *c = strchr(m, ';'); if (c) *c = 0; - - // if 'direct' no proxy - if (_stricmp(lrtrim(m), "direct") == 0) - return false; - - // find proxy address - char *h = strchr(m, ' '); - if (h == nullptr) - return false; - - // find proxy port - *h = 0; ++h; - char *p = strchr(h, ':'); - if (p) { *p = 0; ++p; } - - lrtrim(h); ltrim(p); - if (_stricmp(m, "proxy") == 0 && h[0]) { - nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP; - nlc->wProxyPort = p ? atol(p) : 8080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS4; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks5") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS5; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else return false; - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szAutoUrlStr[MAX_PATH] = ""; -static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 }; -static HANDLE hIeProxyMutex; -static bool bAutoProxyInit; - -static void NetlibInitAutoProxy(void) -{ - if (bAutoProxyInit) return; - - if (!hModJS) { - if (!(hModJS = LoadLibraryA("jsproxy.dll"))) - return; - - pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll"); - pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"); - pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo"); - } - - if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) { - abuf.dwStructSize = sizeof(abuf); - GetFile(szAutoUrlStr, abuf); - } - bAutoProxyInit = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct IeProxyParam -{ - char *szUrl; - char *szHost; - char *szProxy; -}; - -static void __cdecl NetlibIeProxyThread(IeProxyParam *param) -{ - param->szProxy = nullptr; - - if (!bAutoProxyInit) { - WaitForSingleObject(hIeProxyMutex, INFINITE); - NetlibInitAutoProxy(); - ReleaseMutex(hIeProxyMutex); - } - - BOOL res; - char *loc = strstr(szAutoUrlStr, "file://"); - if (loc || strstr(szAutoUrlStr, "://") == nullptr) { - Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc); - loc = loc ? loc + 7 : szAutoUrlStr; - res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr); - } - else { - Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize); - if (abuf.dwScriptBufferSize) - res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf); - else - res = false; - } - - if (res) { - char proxyBuffer[1024]; - char *proxy = proxyBuffer; - DWORD dwProxyLen = sizeof(proxyBuffer); - - if (pInternetGetProxyInfo(param->szUrl, (DWORD)mir_strlen(param->szUrl), - param->szHost, (DWORD)mir_strlen(param->szHost), &proxy, &dwProxyLen)) - param->szProxy = mir_strdup(lrtrim(proxy)); - - Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost); - pInternetDeInitializeAutoProxyDll(nullptr, 0); - } - else Netlib_Logf(nullptr, "Autoproxy init failed"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NetlibGetIeProxy(char *szUrl) -{ - char *res = nullptr, *szHost; - { - char* p = strstr(szUrl, "://"); - if (p) p += 3; else p = szUrl; - - szHost = NEWSTR_ALLOCA(p); - p = strchr(szHost, '/'); if (p) *p = 0; - p = strchr(szHost, ':'); if (p) *p = 0; - _strlwr(szHost); - } - - if (bEnabled) { - for (auto &p : proxyBypass) { - if (mir_strcmp(p, "") == 0) { - if (strchr(szHost, '.') == nullptr) - return nullptr; - } - else if (wildcmp(szHost, p)) - return nullptr; - } - - int ind = -1; - if (strstr(szUrl, "http://")) - ind = szProxyHost[0] ? 0 : 2; - else if (strstr(szUrl, "https://")) - ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2); - else - ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2)); - - if (ind < 0 || !szProxyHost[ind]) - return nullptr; - - size_t len = mir_strlen(szHost) + 20; - res = (char*)mir_alloc(len); - mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]); - return res; - } - - if (szAutoUrlStr[0]) { - IeProxyParam param = { szUrl, szHost, nullptr }; - HANDLE hThread = mir_forkThread(NetlibIeProxyThread, ¶m); - WaitForSingleObject(hThread, INFINITE); - res = param.szProxy; - } - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLoadIeProxy(void) -{ - HKEY hSettings; - if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings)) - return; - - DWORD tValueLen, enabled = 0; - char szHostStr[256] = "", szProxyBypassStr[4096] = ""; - - tValueLen = sizeof(enabled); - int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (BYTE*)&enabled, &tValueLen); - bEnabled = enabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szHostStr); - tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (BYTE*)szHostStr, &tValueLen); - bEnabled = bEnabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szAutoUrlStr); - RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (BYTE*)szAutoUrlStr, &tValueLen); - - tValueLen = _countof(szProxyBypassStr); - RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (BYTE*)szProxyBypassStr, &tValueLen); - - RegCloseKey(hSettings); - - if (bEnabled) { - char *szProxy = ltrim(szHostStr); - if (szProxy[0] == 0) { - enabled = false; - return; - } - - while (true) { - char *szProxyEnd = strchr(szProxy, ';'); - if (szProxyEnd) - *szProxyEnd = 0; - - int ind = -1; - if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; } - else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; } - else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; } - else if (strchr(szProxy, '=')) ind = -2; - - if (ind != -2) { - bOneProxy = ind < 0; if (ind < 0) ind = 0; - - lrtrim(szProxy); - - if (strchr(szProxy, ':')) - szProxyHost[ind] = mir_strdup(szProxy); - else { - size_t len = mir_strlen(szProxy) + 10; - szProxyHost[ind] = (char*)mir_alloc(len); - mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080); - } - if (bOneProxy) - break; - } - if (szProxyEnd == nullptr) - break; - szProxy = szProxyEnd + 1; - } - - char *szProxyBypass = szProxyBypassStr; - while (true) { - char *szProxyBypassEnd = strchr(szProxyBypass, ';'); - if (szProxyBypassEnd) - *szProxyBypassEnd = 0; - - lrtrim(szProxyBypass); - - proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass))); - if (szProxyBypassEnd == nullptr) - break; - - szProxyBypass = szProxyBypassEnd + 1; - } - } - - if (bEnabled || szAutoUrlStr[0]) - hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUnloadIeProxy(void) -{ - for (int i = 0; i < 3; i++) - mir_free(szProxyHost[i]); - - for (auto &p : proxyBypass) - mir_free(p); - - mir_free(abuf.lpszScriptBuffer); - - CloseHandle(hIeProxyMutex); -} diff --git a/src/mir_app/src/netlibbind.cpp b/src/mir_app/src/netlibbind.cpp deleted file mode 100644 index 4f39d7d9c2..0000000000 --- a/src/mir_app/src/netlibbind.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) -{ - SOCKADDR_IN sin = {0}; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = {0}; - sin6.sin6_family = AF_INET6; - - mir_cslock lck(csNetlibUser); - - if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { - BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); - if (*portn == 0) - return false; - - WORD num; - Utils_GetRandom(&num, sizeof(WORD)); - *portn = num % *portn; - } - - bool before = false; - while (true) { - const char *psz; - char *pszEnd; - int portMin, portMax, port, portnum = 0; - - for (psz = szPorts;*psz;) { - while (*psz == ' ' || *psz == ',') psz++; - portMin = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) - break; - while (*pszEnd == ' ') - pszEnd++; - if (*pszEnd == '-') { - psz = pszEnd + 1; - portMax = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) portMax = 65535; - if (portMin > portMax) { - port = portMin; - portMin = portMax; - portMax = port; - } - } - else portMax = portMin; - if (portMax >= 1) { - if (portMin <= 0) portMin = 1; - for (port = portMin; port <= portMax; port++) { - if (port > 65535) - break; - - ++portnum; - - if (s == INVALID_SOCKET) continue; - if (!before && portnum <= *portn) continue; - if (before && portnum >= *portn) - return false; - - sin.sin_port = htons((WORD)port); - bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; - - sin6.sin6_port = htons((WORD)port); - bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; - - if (bV4Mapped && bV6Mapped) { - *portn = portnum + 1; - return true; - } - } - } - psz = pszEnd; - } - - if (*portn < 0) { - *portn = portnum; - return true; - } - - if (*portn >= portnum) - *portn = 0; - else - before = true; - } -} - -int NetlibFreeBoundPort(NetlibBoundPort *nlbp) -{ - NETLIBCONNECTIONEVENTINFO ncei; - - ZeroMemory(&ncei, sizeof(ncei)); - ncei.connected = 0; - ncei.listening = 1; - ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size); - NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0); - - nlbp->close(); - if (nlbp->hThread) - WaitForSingleObject(nlbp->hThread, INFINITE); - Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); - delete nlbp; - return 1; -} - -static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp) -{ - Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort); - - while (true) { - fd_set r; - FD_ZERO(&r); - if (nlbp->s != INVALID_SOCKET) - FD_SET(nlbp->s, &r); - if (nlbp->s6 != INVALID_SOCKET) - FD_SET(nlbp->s6, &r); - if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - - sockaddr_in sin; - int sinLen = sizeof(sin); - memset(&sin, 0, sizeof(sin)); - - SOCKET s; - if (FD_ISSET(nlbp->s, &r)) { - s = accept(nlbp->s, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else if (FD_ISSET(nlbp->s6, &r)) { - s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else s = 0; - - Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlbp->nlu; - nlc->s = s; - - if (nlbp->pfnNewConnection) - nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); - } - - NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); - nlbp->hThread = nullptr; - - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort); -} - -MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb); - if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError()); -LBL_Error: - delete nlbp; - return nullptr; - } - - SOCKADDR_IN sin = { 0 }; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = { 0 }; - sin6.sin6_family = AF_INET6; - - /* if the netlib user wanted a free port given in the range, then - they better have given wPort == 0, let's hope so */ - int foundPort = 0; - if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { - if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { - Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); - SetLastError(WSAEADDRINUSE); - } - else foundPort = 1; - } - else { - /* if ->wPort == 0 then they'll get any free port, otherwise they'll - be asking for whatever was in nlb->wPort*/ - if (nlb->wPort != 0) { - Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort); - sin.sin_port = htons(nlb->wPort); - sin6.sin6_port = htons(nlb->wPort); - } - - if (nlbp->s != INVALID_SOCKET) - if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) { - SOCKADDR_IN sin2 = { 0 }; - int len = sizeof(sin2); - if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len)) - sin6.sin6_port = sin2.sin_port; - foundPort = 1; - } - - if (nlbp->s6 != INVALID_SOCKET) - if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) - foundPort = 1; - } - if (!foundPort) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - SOCKADDR_INET_M sinm = { 0 }; - int len = sizeof(sinm); - if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) { - nlb->wPort = ntohs(sinm.Ipv4.sin_port); - nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr); - } - else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len)) - nlb->wPort = ntohs(sinm.Ipv6.sin6_port); - else { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError()); - goto LBL_Error; - } - nlbp->wPort = nlb->wPort; - - if (nlb->dwInternalIP == 0) { - char hostname[64] = ""; - gethostname(hostname, _countof(hostname)); - - PHOSTENT he = gethostbyname(hostname); - if (he && he->h_addr) - nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); - } - - DWORD extIP; - if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) { - Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort); - nlb->wExPort = nlbp->wExPort; - nlb->dwExternalIP = extIP; - } - else { - if (nlu->settings.enableUPnP) - Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort); - else - Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort); - - nlbp->wExPort = 0; - nlb->wExPort = nlb->wPort; - nlb->dwExternalIP = nlb->dwInternalIP; - } - - nlbp->hThread = mir_forkThread(NetlibBindAcceptThread, nlbp); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.listening = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - memcpy(&ncei.local, &sin, sizeof(sin)); - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - return nlbp; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb) - : handleType(NLH_BOUNDPORT), - nlu(_nlu) -{ - pfnNewConnection = nlb->pfnNewConnection; - pExtra = nlb->pExtra; - - s = socket(PF_INET, SOCK_STREAM, 0); - s6 = socket(PF_INET6, SOCK_STREAM, 0); -} - -void NetlibBoundPort::close() -{ - if (s != INVALID_SOCKET) { - closesocket(s); - s = INVALID_SOCKET; - } - if (s6 != INVALID_SOCKET) { - closesocket(s6); - s6 = INVALID_SOCKET; - } -} diff --git a/src/mir_app/src/netlibhttp.cpp b/src/mir_app/src/netlibhttp.cpp deleted file mode 100644 index 4936841796..0000000000 --- a/src/mir_app/src/netlibhttp.cpp +++ /dev/null @@ -1,1146 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "../libs/zlib/src/zlib.h" -#include "netlib.h" - -#define HTTPRECVHEADERSTIMEOUT 30000 //in ms -#define HTTPRECVDATATIMEOUT 20000 - -struct ProxyAuth -{ - char *szServer; - char *szMethod; - - ProxyAuth(const char *pszServer, const char *pszMethod) - { - szServer = mir_strdup(pszServer); - szMethod = mir_strdup(pszMethod); - } - ~ProxyAuth() - { - mir_free(szServer); - mir_free(szMethod); - } - static int Compare(const ProxyAuth *p1, const ProxyAuth *p2) - { - return mir_strcmpi(p1->szServer, p2->szServer); - } -}; - -struct ProxyAuthList : OBJLIST -{ - ProxyAuthList() : OBJLIST(2, ProxyAuth::Compare) {} - - void add(const char *szServer, const char *szMethod) - { - if (szServer == nullptr) return; - int i = getIndex((ProxyAuth*)&szServer); - if (i >= 0) { - ProxyAuth &rec = (*this)[i]; - if (szMethod == nullptr) - remove(i); - else if (_stricmp(rec.szMethod, szMethod)) { - mir_free(rec.szMethod); - rec.szMethod = mir_strdup(szMethod); - } - } - else insert(new ProxyAuth(szServer, szMethod)); - } - - const char* find(const char *szServer) - { - ProxyAuth *rec = szServer ? OBJLIST::find((ProxyAuth*)&szServer) : nullptr; - return rec ? rec->szMethod : nullptr; - } -}; - -ProxyAuthList proxyAuthList; - -static int RecvWithTimeoutTime(NetlibConnection *nlc, unsigned dwTimeoutTime, char *buf, int len, int flags) -{ - DWORD dwTimeNow; - - if (nlc->foreBuf.isEmpty() && !sslApi.pending(nlc->hSsl)) { - while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) { - unsigned dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000); - int res = WaitUntilReadable(nlc->s, dwDeltaTime); - - switch (res) { - case SOCKET_ERROR: - return SOCKET_ERROR; - - case 1: - return Netlib_Recv(nlc, buf, len, flags); - } - - if (nlc->termRequested || Miranda_IsTerminated()) - return 0; - } - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - return Netlib_Recv(nlc, buf, len, flags); -} - -static char* NetlibHttpFindHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr) -{ - for (int i=0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (_stricmp(p.szName, hdr) == 0) - return p.szValue; - } - - return nullptr; -} - -static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider) -{ - char *szBasicHdr = nullptr; - char *szNegoHdr = nullptr; - char *szNtlmHdr = nullptr; - - for (int i=0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (_stricmp(p.szName, hdr) == 0) { - if (_strnicmp(p.szValue, "Negotiate", 9) == 0) - szNegoHdr = p.szValue; - else if (_strnicmp(p.szValue, "NTLM", 4) == 0) - szNtlmHdr = p.szValue; - else if (_strnicmp(p.szValue, "Basic", 5) == 0) - szBasicHdr = p.szValue; - } - } - - if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr; - if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr; - if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr; - return nullptr; -} - -void NetlibConnFromUrl(const char *szUrl, bool secur, NETLIBOPENCONNECTION &nloc) -{ - secur = secur || _strnicmp(szUrl, "https", 5) == 0; - const char* phost = strstr(szUrl, "://"); - - char* szHost = mir_strdup(phost ? phost + 3 : szUrl); - - char* ppath = strchr(szHost, '/'); - if (ppath) *ppath = '\0'; - - memset(&nloc, 0, sizeof(nloc)); - nloc.szHost = szHost; - - char* pcolon = strrchr(szHost, ':'); - if (pcolon) { - *pcolon = '\0'; - nloc.wPort = (WORD)strtol(pcolon+1, nullptr, 10); - } - else nloc.wPort = secur ? 443 : 80; - nloc.flags = (secur ? NLOCF_SSL : 0); -} - -static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr) -{ - NETLIBOPENCONNECTION nloc; - - if (szUrl == nullptr) - NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, nloc); - else - NetlibConnFromUrl(szUrl, false, nloc); - - nloc.flags |= NLOCF_HTTP; - if (nloc.flags & NLOCF_SSL) - nlhr->flags |= NLHRF_SSL; - else - nlhr->flags &= ~NLHRF_SSL; - - if (nlc != nullptr) { - bool httpProxy = !(nloc.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP; - bool sameHost = mir_strcmp(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort; - - if (!httpProxy && !sameHost) { - NetlibDoCloseSocket(nlc); - - mir_free((char*)nlc->nloc.szHost); - nlc->nloc = nloc; - return NetlibDoConnect(nlc) ? nlc : nullptr; - } - } - else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, &nloc); - - mir_free((char*)nloc.szHost); - - return nlc; -} - -struct HttpSecurityContext -{ - HANDLE m_hNtlmSecurity; - char *m_szHost; - char *m_szProvider; - - HttpSecurityContext() - { - m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr; - } - - ~HttpSecurityContext() { Destroy(); } - - void Destroy(void) - { - if (!m_hNtlmSecurity) return; - - Netlib_DestroySecurityProvider(m_hNtlmSecurity); - m_hNtlmSecurity = nullptr; - mir_free(m_szHost); m_szHost = nullptr; - mir_free(m_szProvider); m_szProvider = nullptr; - } - - bool TryBasic(void) - { - return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic"); - } - - char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete) - { - char *szAuthHdr = nullptr; - bool justCreated = false; - NetlibUser *nlu = nlc->nlu; - - if (m_hNtlmSecurity) { - bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider); - newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost))); - if (newAuth) - Destroy(); - } - - if (m_hNtlmSecurity == nullptr) { - CMStringA szSpnStr; - if (szHost && _stricmp(szProvider, "Basic")) { - unsigned long ip = inet_addr(szHost); - PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET); - szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost); - _strlwr(szSpnStr.GetBuffer() + 5); - Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str()); - } - m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str())); - if (m_hNtlmSecurity) { - m_szProvider = mir_strdup(szProvider); - m_szHost = mir_strdup(szHost); - justCreated = true; - } - } - - if (m_hNtlmSecurity) { - ptrW szLogin, szPassw; - - if (nlu->settings.useProxyAuth) { - mir_cslock lck(csNetlibUser); - szLogin = mir_a2u(nlu->settings.szProxyAuthUser); - szPassw = mir_a2u(nlu->settings.szProxyAuthPassword); - } - - szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete); - if (!szAuthHdr) - Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)"); - else if (justCreated) - proxyAuthList.add(m_szHost, m_szProvider); - } - else complete = 1; - - return szAuthHdr; - } -}; - -static int HttpPeekFirstResponseLine(NetlibConnection *nlc, DWORD dwTimeoutTime, DWORD recvFlags, int *resultCode, char **ppszResultDescr, int *length) -{ - int bytesPeeked; - char buffer[2048], *peol; - - while (true) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags); - if (bytesPeeked == 0) { - SetLastError(ERROR_HANDLE_EOF); - return 0; - } - if (bytesPeeked == SOCKET_ERROR) - return 0; - - buffer[bytesPeeked] = '\0'; - if ((peol = strchr(buffer, '\n')) != nullptr) - break; - - if ((int)mir_strlen(buffer) < bytesPeeked) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - if (bytesPeeked == _countof(buffer) - 1) { - SetLastError(ERROR_BUFFER_OVERFLOW); - return 0; - } - if (Miranda_IsTerminated()) - return 0; - Sleep(10); - } - - if (peol == buffer) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - *peol = '\0'; - - if (_strnicmp(buffer, "HTTP/", 5)) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - size_t off = strcspn(buffer, " \t"); - if (off >= (unsigned)bytesPeeked) - return 0; - - char *pResultCode = buffer + off; - *(pResultCode++) = 0; - - char *pResultDescr; - *resultCode = strtol(pResultCode, &pResultDescr, 10); - - if (ppszResultDescr) - *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr)); - - if (length) - *length = peol - buffer + 1; - return 1; -} - -static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader) -{ - bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH); - - if (sendContentLengthHeader && sendData) - httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength); - else - httpRequest.AppendFormat("\r\n"); - - DWORD hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags); - if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) { - DWORD sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags); - - bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR; - } - - return bytesSent; -} - -MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) -{ - NETLIBHTTPREQUEST *nlhrReply = nullptr; - HttpSecurityContext httpSecurity; - - char *szHost = nullptr, *szNewUrl = nullptr; - char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr; - int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader; - int bytesSent = 0; - bool lastFirstLineFail = false; - - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - NetlibUser *nlu = nlc->nlu; - if (GetNetlibHandleType(nlu) != NLH_USER) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT; - - const char *pszRequest; - switch (nlhr->requestType) { - case REQUEST_GET: pszRequest = "GET"; break; - case REQUEST_POST: pszRequest = "POST"; break; - case REQUEST_CONNECT: pszRequest = "CONNECT"; break; - case REQUEST_HEAD: pszRequest = "HEAD"; break; - case REQUEST_PUT: pszRequest = "PUT"; break; - case REQUEST_DELETE: pszRequest = "DELETE"; break; - case REQUEST_PATCH: pszRequest = "PATCH"; break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - const char *pszFullUrl = nlhr->szUrl; - const char *pszUrl = nullptr; - - unsigned complete = false; - int count = 11; - while (--count) { - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - bytesSent = SOCKET_ERROR; - break; - } - - if (!NetlibReconnect(nlc)) { - bytesSent = SOCKET_ERROR; - break; - } - - if (!pszUrl) { - pszUrl = pszFullUrl; - if (!(nlhr->flags & NLHRF_MANUALHOST)) { - bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL); - - const char *ppath, *phost; - phost = strstr(pszUrl, "://"); - if (phost == nullptr) phost = pszUrl; - else phost += 3; - ppath = strchr(phost, '/'); - if (ppath == phost) - phost = nullptr; - - replaceStr(szHost, phost); - if (ppath && phost) - szHost[ppath - phost] = 0; - - if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy) - pszUrl = ppath ? ppath : "/"; - - if (usingProxy && phost && !nlc->dnsThroughProxy) { - char *tszHost = mir_strdup(phost); - if (ppath) - tszHost[ppath - phost] = 0; - char *cln = strchr(tszHost, ':'); if (cln) *cln = 0; - - if (inet_addr(tszHost) == INADDR_NONE) { - in_addr ip; - if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) { - mir_free(szHost); - if (cln) *cln = ':'; - szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach(); - } - } - mir_free(tszHost); - } - } - } - - if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) { - if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) { - const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer); - if (szAuthMethodNlu) { - mir_free(pszProxyAuthHdr); - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete); - } - } - } - nlc->proxyAuthNeeded = false; - - CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); - - // HTTP headers - doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0; - for (i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1; - else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1; - else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Connection")) continue; - if (p.szValue == nullptr) continue; - httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue); - } - if (szHost && !doneHostHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost); - if (pszProxyAuthHdr && !doneProxyAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr); - if (pszAuthHdr && !doneAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr); - httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive"); - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive"); - - // Add Sticky Headers - if (nlu->szStickyHeaders != nullptr) - httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders); - - // send it - bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader); - if (bytesSent == SOCKET_ERROR) - break; - - // ntlm reply - if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD) - break; - - DWORD fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0); - DWORD dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout; - if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) { - DWORD err = GetLastError(); - Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count); - - // connection died while we were waiting - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - break; - } - - if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) { - bytesSent = SOCKET_ERROR; - break; - } - - lastFirstLineFail = true; - continue; - } - - int resultCode = nlhr->resultCode; - lastFirstLineFail = false; - - DWORD hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - DWORD dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP; - - if (resultCode == 100) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - - else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect - pszUrl = nullptr; - - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - char* tmpUrl = NetlibHttpFindHeader(nlhrReply, "Location"); - if (tmpUrl) { - size_t rlen = 0; - if (tmpUrl[0] == '/') { - const char *ppath, *phost; - phost = strstr(pszFullUrl, "://"); - phost = phost ? phost + 3 : pszFullUrl; - ppath = strchr(phost, '/'); - rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl); - } - - nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1); - - strncpy(nlc->szNewUrl, pszFullUrl, rlen); - mir_strcpy(nlc->szNewUrl + rlen, tmpUrl); - pszFullUrl = nlc->szNewUrl; - pszUrl = nullptr; - - if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) { - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 401 && !doneAuthHeader) { //auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - replaceStr(pszAuthHdr, nullptr); - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete); - } - } - if (pszAuthHdr == nullptr) { - proxyAuthList.add(szHost, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr; - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge + 1)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete); - } - } - if (pszProxyAuthHdr == nullptr) { - proxyAuthList.add(nlc->szProxyServer, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else break; - - if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader) - replaceStr(pszProxyAuthHdr, nullptr); - - if (pszAuthHdr && resultCode != 401 && !doneAuthHeader) - replaceStr(pszAuthHdr, nullptr); - - if (nlhrReply) { - Netlib_FreeHttpRequest(nlhrReply); - nlhrReply = nullptr; - } - } - - if (count == 0) bytesSent = SOCKET_ERROR; - if (nlhrReply) - Netlib_FreeHttpRequest(nlhrReply); - - //clean up - mir_free(pszProxyAuthHdr); - mir_free(pszAuthHdr); - mir_free(szHost); - mir_free(szNewUrl); - - if (nlc) - NetlibLeaveNestedCS(&nlc->ncsSend); - - return bytesSent; -} - -MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) -{ - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - if (nlhr->headers) { - for (int i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - mir_free(p.szName); - mir_free(p.szValue); - } - mir_free(nlhr->headers); - } - mir_free(nlhr->pData); - mir_free(nlhr->szResultDescr); - mir_free(nlhr->szUrl); - mir_free(nlhr); - return true; -} - -#define NHRV_BUF_SIZE 8192 - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return nullptr; - - DWORD dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; - NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); - nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); - nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions - nlhr->requestType = REQUEST_RESPONSE; - - int firstLineLength = 0; - if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1); - int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT); - if (bytesPeeked != firstLineLength) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - if (bytesPeeked != SOCKET_ERROR) - SetLastError(ERROR_HANDLE_EOF); - return nullptr; - } - - // Make sure all headers arrived - MBinBuffer buf; - int headersCount = 0; - bytesPeeked = 0; - for (bool headersCompleted = false; !headersCompleted;) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE); - if (bytesPeeked == 0) - break; - - if (bytesPeeked == SOCKET_ERROR) { - bytesPeeked = 0; - break; - } - - buf.append(buffer, bytesPeeked); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr) break; - if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) { - bytesPeeked = peol - (char*)buf.data() + 1; - headersCompleted = true; - break; - } - pbuffer = peol + 1; - } - } - - if (bytesPeeked <= 0) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - // Receive headers - nlhr->headersCount = headersCount; - nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount); - - headersCount = 0; - for (char *pbuffer = buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r')) - break; - *peol = 0; - - char *pColon = strchr(pbuffer, ':'); - if (pColon == nullptr) { - Netlib_FreeHttpRequest(nlhr); nlhr = nullptr; - SetLastError(ERROR_INVALID_DATA); - break; - } - - *pColon = 0; - nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer)); - nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1)); - pbuffer = peol + 1; - } - - // remove processed data - buf.remove(bytesPeeked); - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - return nlhr; -} - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || - nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || - nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION) - nlhr->nlc = nullptr; - - NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc); - if (nlc == nullptr) - return nullptr; - - NETLIBHTTPREQUEST nlhrSend = *nlhr; - nlhrSend.flags |= NLHRF_SMARTREMOVEHOST; - - bool doneUserAgentHeader = NetlibHttpFindHeader(nlhr, "User-Agent") != nullptr; - bool doneAcceptEncoding = NetlibHttpFindHeader(nlhr, "Accept-Encoding") != nullptr; - if (!doneUserAgentHeader || !doneAcceptEncoding) { - nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2)); - memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount); - } - - char szUserAgent[64]; - if (!doneUserAgentHeader) { - nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent"; - nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent; - ++nlhrSend.headersCount; - - char szMirandaVer[64]; - strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE); - #if defined(_WIN64) - strncat_s(szMirandaVer, " x64", _TRUNCATE); - #endif - - char *pspace = strchr(szMirandaVer, ' '); - if (pspace) { - *pspace++ = '\0'; - mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace); - } - else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer); - } - if (!doneAcceptEncoding) { - nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding"; - nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip"; - ++nlhrSend.headersCount; - } - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) { - if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers); - nlhr->resultCode = nlhrSend.resultCode; - Netlib_CloseHandle(nlc); - return nullptr; - } - if (!doneUserAgentHeader || !doneAcceptEncoding) - mir_free(nlhrSend.headers); - - DWORD dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - DWORD hflags = - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - NETLIBHTTPREQUEST *nlhrReply; - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - nlhrReply->szUrl = nlc->szNewUrl; - nlc->szNewUrl = nullptr; - } - - if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) { - Netlib_CloseHandle(nlc); - if (nlhrReply) - nlhrReply->nlc = nullptr; - } - else nlhrReply->nlc = nlc; - - return nlhrReply; -} - -void NetlibHttpSetLastErrorUsingHttpResult(int result) -{ - if (result >= 200 && result < 300) { - SetLastError(ERROR_SUCCESS); - return; - } - switch (result) { - case 400: SetLastError(ERROR_BAD_FORMAT); break; - case 401: - case 402: - case 403: - case 407: SetLastError(ERROR_ACCESS_DENIED); break; - case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; - case 405: - case 406: SetLastError(ERROR_INVALID_FUNCTION); break; - case 408: SetLastError(ERROR_TIMEOUT); break; - default: SetLastError(ERROR_GEN_FAILURE); break; - } -} - -char* gzip_decode(char *gzip_data, int *len_ptr, int window) -{ - if (*len_ptr == 0) return nullptr; - - int gzip_len = *len_ptr * 5; - char* output_data = nullptr; - - int gzip_err; - z_stream zstr; - - do { - output_data = (char*)mir_realloc(output_data, gzip_len+1); - - zstr.next_in = (Bytef*)gzip_data; - zstr.avail_in = *len_ptr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream)); - - zstr.next_out = (Bytef*)output_data; - zstr.avail_out = gzip_len; - - gzip_err = inflate(&zstr, Z_FINISH); - - inflateEnd(&zstr); - gzip_len *= 2; - } while (gzip_err == Z_BUF_ERROR); - - gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1; - - if (gzip_len <= 0) { - mir_free(output_data); - output_data = nullptr; - } - else output_data[gzip_len] = 0; - - *len_ptr = gzip_len; - return output_data; -} - -static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, DWORD flags) -{ - MBinBuffer buf; - - while (true) { - char data[1000]; - int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags); - if (recvResult <= 0 || recvResult >= _countof(data)) - return SOCKET_ERROR; - - buf.append(data, recvResult); // add chunk - - const char *peol1 = (const char*)memchr(buf.data(), '\n', buf.length()); - if (peol1 == nullptr) - continue; - - int cbRest = int(peol1 - buf.data()) + 1; - const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest); - if (peol2 == nullptr) - continue; - - int sz = peol2 - buf.data() + 1; - int r = strtol(first ? buf.data() : peol1 + 1, nullptr, 16); - if (r == 0) { - const char *peol3 = strchr(peol2 + 1, '\n'); - if (peol3 == nullptr) - continue; - sz = peol3 - buf.data() + 1; - } - buf.remove(sz); // remove all our data from buffer - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - return r; - } -} - -NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, DWORD hflags, DWORD dflags, bool isConnect) -{ - int dataLen = -1, i, chunkhdr = 0; - bool chunked = false; - int cenc = 0, cenctype = 0, close = 0; - -next: - NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - if (nlhrReply == nullptr) - return nullptr; - - if (nlhrReply->resultCode == 100) { - Netlib_FreeHttpRequest(nlhrReply); - goto next; - } - - if (nlhrReply->resultCode == 204) - dataLen = 0; - - for (i = 0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (!mir_strcmpi(p.szName, "Content-Length")) - dataLen = atoi(p.szValue); - - if (!mir_strcmpi(p.szName, "Content-Encoding")) { - cenc = i; - if (strstr(p.szValue, "gzip")) - cenctype = 1; - else if (strstr(p.szValue, "deflate")) - cenctype = 2; - } - - if (!mir_strcmpi(p.szName, "Connection")) - close = !mir_strcmpi(p.szValue, "close"); - - if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) { - chunked = true; - chunkhdr = i; - dataLen = -1; - } - } - - if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) { - int recvResult, chunksz = -1; - int dataBufferAlloced; - - if (chunked) { - chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0)); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen = chunksz; - } - dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - - while (chunksz != 0) { - while (true) { - recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT, - nlhrReply->pData + nlhrReply->dataLength, - dataBufferAlloced - nlhrReply->dataLength - 1, - dflags | (cenctype ? MSG_NODUMP : 0)); - - if (recvResult == 0) break; - if (recvResult == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - nlhrReply->dataLength += recvResult; - - if (dataLen >= 0) { - if (nlhrReply->dataLength >= dataLen) - break; - } - else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) { - dataBufferAlloced += 2048; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - if (nlhrReply->pData == nullptr) { - SetLastError(ERROR_OUTOFMEMORY); - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - } - Sleep(10); - } - - if (!chunked) - break; - - chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen += chunksz; - dataBufferAlloced += chunksz; - - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - } - - nlhrReply->pData[nlhrReply->dataLength] = '\0'; - } - - if (chunked) { - nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16); - mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length"); - - nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16); - mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength); - } - - if (cenctype) { - int bufsz = nlhrReply->dataLength; - char* szData = nullptr; - - switch (cenctype) { - case 1: - szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS); - break; - - case 2: - szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS); - if (bufsz < 0) { - bufsz = nlhrReply->dataLength; - szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS); - } - break; - } - - if (bufsz > 0) { - NetlibDumpData(nlc, (PBYTE)szData, bufsz, 0, dflags | MSG_NOTITLE); - mir_free(nlhrReply->pData); - nlhrReply->pData = szData; - nlhrReply->dataLength = bufsz; - - mir_free(nlhrReply->headers[cenc].szName); - mir_free(nlhrReply->headers[cenc].szValue); - memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0])); - } - else if (bufsz == 0) { - mir_free(nlhrReply->pData); - nlhrReply->pData = nullptr; - nlhrReply->dataLength = 0; - } - } - - if (close && - (nlc->proxyType != PROXYTYPE_HTTP || nlc->nloc.flags & NLOCF_SSL) && - (!isConnect || nlhrReply->resultCode != 200)) - NetlibDoCloseSocket(nlc); - - return nlhrReply; -} diff --git a/src/mir_app/src/netlibhttpproxy.cpp b/src/mir_app/src/netlibhttpproxy.cpp deleted file mode 100644 index ccc791e702..0000000000 --- a/src/mir_app/src/netlibhttpproxy.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -typedef enum -{ - reqHelloGet, - reqOldGet, - reqOldPost, - reqNewPost, -} -RequestType; - -///////////////////////////////////////////////////////////////////////////////////////// - -#define NETLIBHTTP_RETRYCOUNT 3 -#define NETLIBHTTP_RETRYTIMEOUT 2000 - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - mir_free(nlc->nlhpi.szHttpGetUrl); - mir_free(nlc->nlhpi.szHttpPostUrl); - - nlc->nlhpi.combinePackets = 1; - memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi)); - if (nlc->nlhpi.combinePackets == 0) - nlc->nlhpi.combinePackets = 1; - - nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl); - nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders) -{ - if (GetNetlibHandleType(nlu) != NLH_USER) - return ERROR_INVALID_PARAMETER; - - replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return -1; - - int oldTimeout = nlc->pollingTimeout; - nlc->pollingTimeout = iTimeout; - return oldTimeout; -} diff --git a/src/mir_app/src/netliblog.cpp b/src/mir_app/src/netliblog.cpp deleted file mode 100644 index bd41723192..0000000000 --- a/src/mir_app/src/netliblog.cpp +++ /dev/null @@ -1,546 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define MS_NETLIB_LOGWIN "Netlib/Log/Win" - -extern HANDLE hConnectionHeaderMutex; - -#define TIMEFORMAT_NONE 0 -#define TIMEFORMAT_HHMMSS 1 -#define TIMEFORMAT_MILLISECONDS 2 -#define TIMEFORMAT_MICROSECONDS 3 -struct { - HWND hwndOpts; - int toOutputDebugString; - int toFile; - int toLog; - int timeFormat; - int showUser; - int dumpSent, dumpRecv, dumpProxy, dumpSsl; - int textDumps, autoDetectText; - CMStringW tszFile, tszUserFile; -} -static logOptions = {}; - -struct LOGMSG -{ - const char* pszHead; - const char* pszMsg; -}; - -static __int64 mirandaStartTime, perfCounterFreq; -static int bIsActive = TRUE; -static HANDLE hLogEvent = nullptr; -static HANDLE hLogger = nullptr; - -static void InitLog() -{ - if (hLogger) { - mir_closeLog(hLogger); - hLogger = nullptr; - } - - ptrW szBuf(db_get_wsa(0, "Netlib", "File")); - if (mir_wstrlen(szBuf)) { - logOptions.tszUserFile = szBuf.get(); - - wchar_t path[MAX_PATH]; - PathToAbsoluteW(VARSW(szBuf), path); - logOptions.tszFile = path; - } - else { - db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt"); - logOptions.tszFile = VARSW(logOptions.tszUserFile); - } - - if (logOptions.toFile) - hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), logOptions.tszFile, 0); -} - -static const wchar_t* szTimeFormats[] = -{ - LPGENW("No times"), - LPGENW("Standard hh:mm:ss times"), - LPGENW("Times in milliseconds"), - LPGENW("Times in microseconds") -}; - -static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - wchar_t str[MAX_PATH]; - - switch (message) { - case WM_INITDIALOG: - logOptions.hwndOpts = hwndDlg; - TranslateDialogDefault(hwndDlg); - CheckDlgButton(hwndDlg, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED); - - for (auto &it : szTimeFormats) - SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_ADDSTRING, 0, (LPARAM)TranslateW(it)); - - SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_SETCURSEL, logOptions.timeFormat, 0); - CheckDlgButton(hwndDlg, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED); - SetDlgItemText(hwndDlg, IDC_FILENAME, logOptions.tszUserFile); - SetDlgItemText(hwndDlg, IDC_PATH, logOptions.tszFile); - CheckDlgButton(hwndDlg, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED); - { - ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart")); - if (szRun) - SetDlgItemTextA(hwndDlg, IDC_RUNATSTART, szRun); - - HWND hwndFilter = GetDlgItem(hwndDlg, IDC_FILTER); - SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES)); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - - for (auto &it : netlibUser) { - tvis.item.pszText = it->user.szDescriptiveName.w; - tvis.item.lParam = netlibUser.indexOf(&it); - tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1); - TreeView_InsertItem(hwndFilter, &tvis); - } - tvis.item.lParam = -1; - tvis.item.pszText = TranslateT("(Miranda core logging)"); - tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1); - TreeView_InsertItem(hwndFilter, &tvis); - } - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_FILENAME: - if (HIWORD(wParam) == EN_CHANGE) { - if ((HWND)lParam == GetFocus()) - CheckDlgButton(hwndDlg, IDC_TOFILE, BST_CHECKED); - - wchar_t path[MAX_PATH]; - GetWindowText((HWND)lParam, path, _countof(path)); - - PathToAbsoluteW(VARSW(path), path); - SetDlgItemText(hwndDlg, IDC_PATH, path); - } - break; - - case IDC_FILENAMEBROWSE: - case IDC_RUNATSTARTBROWSE: - GetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str, _countof(str)); - { - wchar_t filter[200]; - mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = hwndDlg; - ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT; - if (LOWORD(wParam) == IDC_FILENAMEBROWSE) - ofn.lpstrTitle = TranslateT("Select where log file will be created"); - else { - ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - ofn.lpstrTitle = TranslateT("Select program to be run"); - } - ofn.lpstrFilter = filter; - ofn.lpstrFile = str; - ofn.nMaxFile = _countof(str) - 2; - ofn.nMaxFileTitle = MAX_PATH; - if (LOWORD(wParam) == IDC_FILENAMEBROWSE) { - if (!GetSaveFileName(&ofn)) return 1; - } - else if (!GetOpenFileName(&ofn)) - return 1; - - if (LOWORD(wParam) == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) { - memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t))); - str[0] = '"'; - mir_wstrcat(str, L"\""); - } - SetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str); - } - break; - - case IDC_RUNNOW: - GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str)); - if (str[0]) { - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcess(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - } - break; - - case IDOK: - GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str)); - db_set_ws(0, "Netlib", "RunAtStart", str); - db_set_b(0, "Netlib", "ShowLogOptsAtStart", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWTHISDLGATSTART)); - - GetDlgItemText(hwndDlg, IDC_FILENAME, str, _countof(str)); - logOptions.tszUserFile = rtrimw(str); - db_set_ws(0, "Netlib", "File", str); - - GetDlgItemText(hwndDlg, IDC_PATH, str, _countof(str)); - logOptions.tszFile = rtrimw(str); - - db_set_b(0, "Netlib", "DumpRecv", logOptions.dumpRecv = IsDlgButtonChecked(hwndDlg, IDC_DUMPRECV)); - db_set_b(0, "Netlib", "DumpSent", logOptions.dumpSent = IsDlgButtonChecked(hwndDlg, IDC_DUMPSENT)); - db_set_b(0, "Netlib", "DumpProxy", logOptions.dumpProxy = IsDlgButtonChecked(hwndDlg, IDC_DUMPPROXY)); - db_set_b(0, "Netlib", "DumpSsl", logOptions.dumpSsl = IsDlgButtonChecked(hwndDlg, IDC_DUMPSSL)); - db_set_b(0, "Netlib", "TextDumps", logOptions.textDumps = IsDlgButtonChecked(hwndDlg, IDC_TEXTDUMPS)); - db_set_b(0, "Netlib", "AutoDetectText", logOptions.autoDetectText = IsDlgButtonChecked(hwndDlg, IDC_AUTODETECTTEXT)); - db_set_b(0, "Netlib", "TimeFormat", logOptions.timeFormat = SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_GETCURSEL, 0, 0)); - db_set_b(0, "Netlib", "ShowUser", logOptions.showUser = IsDlgButtonChecked(hwndDlg, IDC_SHOWNAMES)); - db_set_b(0, "Netlib", "ToOutputDebugString", logOptions.toOutputDebugString = IsDlgButtonChecked(hwndDlg, IDC_TOOUTPUTDEBUGSTRING)); - db_set_b(0, "Netlib", "ToFile", logOptions.toFile = IsDlgButtonChecked(hwndDlg, IDC_TOFILE)); - { - HWND hwndFilter = GetDlgItem(logOptions.hwndOpts, IDC_FILTER); - TVITEM tvi = { 0 }; - BOOL checked; - - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; - tvi.hItem = TreeView_GetRoot(hwndFilter); - - while (tvi.hItem) { - TreeView_GetItem(hwndFilter, &tvi); - checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2); - - if (tvi.lParam == -1) { - logOptions.toLog = checked; - db_set_dw(0, "Netlib", "NLlog", checked); - } - else if (tvi.lParam < netlibUser.getCount()) { - netlibUser[tvi.lParam]->toLog = checked; - db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked); - } - - tvi.hItem = TreeView_GetNextSibling(hwndFilter, tvi.hItem); - } - } - InitLog(); - __fallthrough; - - case IDCANCEL: - DestroyWindow(hwndDlg); - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE)); - logOptions.hwndOpts = nullptr; - break; - } - return FALSE; -} - -void NetlibLogShowOptions(void) -{ - if (logOptions.hwndOpts == nullptr) - logOptions.hwndOpts = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NETLIBLOGOPTS), nullptr, LogOptionsDlgProc); - SetForegroundWindow(logOptions.hwndOpts); -} - -static INT_PTR ShowOptions(WPARAM, LPARAM) -{ - NetlibLogShowOptions(); - return 0; -} - -int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags) -{ - if (!bIsActive) - return 0; - - DWORD dwOriginalLastError = GetLastError(); - - if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - /* if the Netlib user handle is nullptr, just pretend its not */ - if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog)) - return 1; - - LARGE_INTEGER liTimeNow; - char szTime[32], szHead[128]; - switch (logOptions.timeFormat) { - case TIMEFORMAT_HHMMSS: - GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime)); - mir_strcat(szTime, " "); - break; - - case TIMEFORMAT_MILLISECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - case TIMEFORMAT_MICROSECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - default: - szTime[0] = '\0'; - break; - } - - if (flags & MSG_NOTITLE) - szHead[0] = 0; - else { - char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr; - if (szUser) - mir_snprintf(szHead, "[%s%04X] [%s] ", szTime, GetCurrentThreadId(), szUser); - else - mir_snprintf(szHead, "[%s%04X] ", szTime, GetCurrentThreadId()); - } - - if (logOptions.toOutputDebugString) { - if (szHead[0]) - OutputDebugStringA(szHead); - OutputDebugStringA(pszMsg); - OutputDebugStringA("\n"); - } - - if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) { - size_t len = mir_strlen(pszMsg); - mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n"); - } - - LOGMSG logMsg = { szHead, pszMsg }; - NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg); - - SetLastError(dwOriginalLastError); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void PROTO_INTERFACE::debugLogA(const char *szFormat, ...) -{ - char buf[4096]; - va_list args; - va_start(args, szFormat); - int res = _vsnprintf(buf, _countof(buf), szFormat, args); - NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0); - va_end(args); -} - -void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...) -{ - WCHAR buf[4096]; - va_list args; - va_start(args, wszFormat); - int res = _vsnwprintf(buf, _countof(buf), wszFormat, args); - NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - char szText[8000]; - mir_vsnprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, szText, 0); -} - -MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...) -{ - va_list va; - va_start(va, fmt); - wchar_t szText[8000]; - mir_vsnwprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr) -{ - return NetlibLog_Worker(hUser, pszStr, 0); -} - -MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr) -{ - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibDumpData(NetlibConnection *nlc, PBYTE buf, int len, int sent, int flags) -{ - // This section checks a number of conditions and aborts - // the dump if the data should not be written to the log - - // Check packet flags - if (flags & (MSG_PEEK | MSG_NODUMP)) - return; - - // Check user's log settings - if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty()))) - return; - if ((sent && !logOptions.dumpSent) || (!sent && !logOptions.dumpRecv)) - return; - if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy) - return; - if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl) - return; - - CMStringA str; - - WaitForSingleObject(hConnectionHeaderMutex, INFINITE); - NetlibUser *nlu = nlc ? nlc->nlu : nullptr; - - if (!(flags & MSG_NOTITLE)) - str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : ""); - ReleaseMutex(hConnectionHeaderMutex); - - // check filter settings - if (nlu == nullptr) { - if (!logOptions.toLog) - return; - } - else if (!nlu->toLog) - return; - - bool isText = true; - if (!logOptions.textDumps) - isText = false; - else if (!(flags & MSG_DUMPASTEXT)) { - if (logOptions.autoDetectText) { - for (int i = 0; i < len; i++) { - if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) { - isText = false; - break; - } - } - } - else isText = false; - } - - // Text data - if (isText) { - str.Append((const char*)buf, len); - } - // Binary data - else { - for (int line = 0;; line += 16) { - PBYTE p = buf + line; - int colsInLine = min(16, len - line); - if (colsInLine == 16) - str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ", - line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); - else { - str.AppendFormat("%08X: ", line); - - // Dump data as hex - int col; - for (col = 0; col < colsInLine; col++) - str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' '); - - // Fill out last line with blanks - for (; col < 16; col++) - str.Append(" "); - - str.AppendChar(' '); - } - - for (int col = 0; col < colsInLine; col++) - str.AppendChar((p[col] < ' ') ? '.' : p[col]); - - if (len - line <= 16) - break; - - str.AppendChar('\r'); // End each line with a break - str.AppendChar('\n'); // End each line with a break - } - } - - NetlibLog_Worker(nlu, str, flags); -} - -void NetlibLogInit(void) -{ - LARGE_INTEGER li; - QueryPerformanceFrequency(&li); - perfCounterFreq = li.QuadPart; - QueryPerformanceCounter(&li); - mirandaStartTime = li.QuadPart; - - CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions); - hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP); - - logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", 1); - logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", 1); - logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", 1); - logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", 0); - logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", 1); - logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", 1); - logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS); - logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", 1); - logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", 0); - logOptions.toFile = db_get_b(0, "Netlib", "ToFile", 0); - logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", 1); - - InitLog(); - - if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0)) - NetlibLogShowOptions(); - - ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart")); - if (szBuf) { - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - } -} - -void NetlibLogShutdown(void) -{ - bIsActive = FALSE; - DestroyHookableEvent(hLogEvent); hLogEvent = nullptr; - if (IsWindow(logOptions.hwndOpts)) - DestroyWindow(logOptions.hwndOpts); -} diff --git a/src/mir_app/src/netlibopenconn.cpp b/src/mir_app/src/netlibopenconn.cpp deleted file mode 100644 index ee4bb5d996..0000000000 --- a/src/mir_app/src/netlibopenconn.cpp +++ /dev/null @@ -1,889 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern mir_cs csNetlibUser; -extern HANDLE hConnectionOpenMutex; -extern DWORD g_LastConnectionTick; -extern int connectionTimeout; -static int iUPnPCleanup = 0; - -#define RECV_DEFAULT_TIMEOUT 60000 - -//returns in network byte order -DWORD DnsLookup(NetlibUser *nlu, const char *szHost) -{ - HOSTENT* host; - DWORD ip = inet_addr(szHost); - if (ip != INADDR_NONE) - return ip; - - __try { - host = gethostbyname(szHost); - if (host) - return *(u_long*)host->h_addr_list[0]; - - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError()); - } - __except (EXCEPTION_EXECUTE_HANDLER) {} - - return 0; -} - -int WaitUntilReadable(SOCKET s, DWORD dwTimeout, bool check) -{ - fd_set readfd; - TIMEVAL tv; - - if (s == INVALID_SOCKET) return SOCKET_ERROR; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - int result = select(0, &readfd, nullptr, nullptr, &tv); - if (result == 0 && !check) SetLastError(ERROR_TIMEOUT); - return result; -} - -int WaitUntilWritable(SOCKET s, DWORD dwTimeout) -{ - fd_set writefd; - TIMEVAL tv; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&writefd); - FD_SET(s, &writefd); - - switch (select(0, nullptr, &writefd, nullptr, &tv)) { - case 0: - SetLastError(ERROR_TIMEOUT); - case SOCKET_ERROR: - return 0; - } - return 1; -} - -bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, DWORD dwTimeout) -{ - int nReceived = 0; - DWORD dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout; - - while ((dwTimeNow = GetTickCount()) < dwCompleteTime) { - if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false; - nReceived = Netlib_Recv(nlc, buf, len, flags); - if (nReceived <= 0) return false; - - buf += nReceived; - len -= nReceived; - if (len <= 0) return true; - } - SetLastError(ERROR_TIMEOUT); - return false; -} - -static int NetlibInitSocks4Connection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) -{ - // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol - if (!nloc || !nloc->szHost || !nloc->szHost[0]) return 0; - - size_t nHostLen = mir_strlen(nloc->szHost) + 1; - size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1; - size_t len = 8 + nUserLen; - - char* pInit = (char*)alloca(len + nHostLen); - pInit[0] = 4; // SOCKS4 - pInit[1] = 1; //connect - *(PWORD)&pInit[2] = htons(nloc->wPort); - - if (nUserLen <= 1) pInit[8] = 0; - else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen); - - //if cannot resolve host, try resolving through proxy (requires SOCKS4a) - DWORD ip = DnsLookup(nlu, nloc->szHost); - *(PDWORD)&pInit[4] = ip ? ip : 0x01000000; - if (!ip) { - memcpy(&pInit[len], nloc->szHost, nHostLen); - len += nHostLen; - } - - if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - char reply[8]; - if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - switch ((BYTE)reply[1]) { - case 90: return 1; - case 91: SetLastError(ERROR_ACCESS_DENIED); break; - case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; - case 93: SetLastError(ERROR_INVALID_ACCESS); break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (BYTE)reply[1], GetLastError()); - return 0; -} - -static int NetlibInitSocks5Connection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) -{ - //rfc1928 - BYTE buf[258]; - - buf[0] = 5; //yep, socks5 - buf[1] = 1; //one auth method - buf[2] = nlu->settings.useProxyAuth ? 2 : 0; - if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - //confirmation of auth method - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if ((buf[1] != 0 && buf[1] != 2)) { - SetLastError(ERROR_INVALID_ID_AUTHORITY); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError()); - return 0; - } - - if (buf[1] == 2) { //rfc1929 - size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser); - size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword); - PBYTE pAuthBuf = (PBYTE)mir_alloc(3 + nUserLen + nPassLen); - pAuthBuf[0] = 1; //auth version - pAuthBuf[1] = (BYTE)nUserLen; - memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen); - pAuthBuf[2 + nUserLen] = (BYTE)nPassLen; - memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen); - if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pAuthBuf); - return 0; - } - mir_free(pAuthBuf); - - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if (buf[1]) { - SetLastError(ERROR_ACCESS_DENIED); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - } - - size_t nHostLen; - DWORD hostIP; - - if (nlc->dnsThroughProxy) { - hostIP = inet_addr(nloc->szHost); - nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(nloc->szHost) + 1 : 4; - } - else { - hostIP = DnsLookup(nlu, nloc->szHost); - if (hostIP == 0) - return 0; - nHostLen = 4; - } - PBYTE pInit = (PBYTE)mir_alloc(6 + nHostLen); - pInit[0] = 5; //SOCKS5 - pInit[1] = nloc->flags & NLOCF_UDP ? 3 : 1; //connect or UDP - pInit[2] = 0; //reserved - if (hostIP == INADDR_NONE) { //DNS lookup through proxy - pInit[3] = 3; - pInit[4] = BYTE(nHostLen - 1); - memcpy(pInit + 5, nloc->szHost, nHostLen - 1); - } - else { - pInit[3] = 1; - *(PDWORD)(pInit + 4) = hostIP; - } - *(PWORD)(pInit + 4 + nHostLen) = htons(nloc->wPort); - if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pInit); - return 0; - } - mir_free(pInit); - - if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - if (buf[0] != 5 || buf[1]) { - const char* err = "Unknown response"; - if (buf[0] != 5) - SetLastError(ERROR_BAD_FORMAT); - else { - switch (buf[1]) { - case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break; - case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break; - case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break; - case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break; - case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break; - case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break; - case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break; - case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - } - Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err); - return 0; - } - - int nRecvSize = 0; - switch (buf[3]) { - case 1:// ipv4 addr - nRecvSize = 5; - break; - case 3:// dns name addr - nRecvSize = buf[4] + 2; - break; - case 4:// ipv6 addr - nRecvSize = 17; - break; - default: - Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]); - return 0; - } - if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - //connected - return 1; -} - -static bool NetlibInitHttpsConnection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) -{ - // rfc2817 - CMStringA szUrl; - if (nlc->dnsThroughProxy) - szUrl.Format("%s:%u", nloc->szHost, nloc->wPort); - else { - DWORD ip = DnsLookup(nlu, nloc->szHost); - if (ip == 0) return false; - szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), nloc->wPort); - } - - NETLIBHTTPREQUEST nlhrSend = { 0 }; - nlhrSend.cbSize = sizeof(nlhrSend); - nlhrSend.requestType = REQUEST_CONNECT; - nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT; - nlhrSend.szUrl = szUrl.GetBuffer(); - - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) - return false; - - NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true); - if (nlhrReply == nullptr) - return false; - - if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) { - if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) { - Netlib_FreeHttpRequest(nlhrReply); - nlc->dnsThroughProxy = 0; - return NetlibInitHttpsConnection(nlc, nlu, nloc); - } - - NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); - Netlib_Logf(nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__, nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr); - Netlib_FreeHttpRequest(nlhrReply); - return 0; - } - Netlib_FreeHttpRequest(nlhrReply); - return true; // connected -} - -static void FreePartiallyInitedConnection(NetlibConnection *nlc) -{ - DWORD dwOriginalLastError = GetLastError(); - - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) - delete nlc; - - SetLastError(dwOriginalLastError); -} - -static bool my_connectIPv4(NetlibConnection *nlc, NETLIBOPENCONNECTION *nloc) -{ - int rc = 0, retrycnt = 0; - u_long notblocking = 1; - DWORD lasterr = 0; - static const TIMEVAL tv = { 1, 0 }; - NetlibUser *nlu = nlc->nlu; - - // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway - unsigned int dwTimeout = (nloc && (nloc->flags & NLOCF_V2) && (nloc->timeout > 0)) ? nloc->timeout : 30; - - // this is for XP SP2 where there is a default connection attempt limit of 10/second - if (connectionTimeout) { - WaitForSingleObject(hConnectionOpenMutex, 10000); - int waitdiff = GetTickCount() - g_LastConnectionTick; - if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); - g_LastConnectionTick = GetTickCount(); - ReleaseMutex(hConnectionOpenMutex); - - // might of died in between the wait - if (Miranda_IsTerminated()) return false; - } - - PHOSTENT he; - sockaddr_in sin = { 0 }; - sin.sin_family = AF_INET; - - if (nlc->proxyType) { - if (!nlc->szProxyServer) return false; - - if (nloc) - Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort); - else - Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort); - - sin.sin_port = htons(nlc->wProxyPort); - he = gethostbyname(nlc->szProxyServer); - } - else { - if (!nloc || !nloc->szHost || nloc->szHost[0] == '[' || strchr(nloc->szHost, ':')) return false; - Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort); - - sin.sin_port = htons(nloc->wPort); - he = gethostbyname(nloc->szHost); - } - - for (char** har = he->h_addr_list; *har && !Miranda_IsTerminated(); ++har) { - sin.sin_addr.s_addr = *(u_long*)*har; - - char *szIp = Netlib_AddressToString(&sin); - Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, szIp); - mir_free(szIp); - -retry: - nlc->s = socket(AF_INET, nloc->flags & NLOCF_UDP ? SOCK_DGRAM : SOCK_STREAM, 0); - if (nlc->s == INVALID_SOCKET) - return false; - - // return the socket to non blocking - if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) - return false; - - if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { - if (!BindSocketToPort(nlu->settings.szOutgoingPorts, nlc->s, INVALID_SOCKET, &nlu->inportnum)) - Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); - } - - // try a connect - if (connect(nlc->s, (LPSOCKADDR)&sin, sizeof(sin)) == 0) { - rc = 0; - break; - } - - // didn't work, was it cos of nonblocking? - if (WSAGetLastError() != WSAEWOULDBLOCK) { - rc = SOCKET_ERROR; - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - continue; - } - - while (true) { - fd_set r, w, e; - FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); - FD_SET(nlc->s, &r); - FD_SET(nlc->s, &w); - FD_SET(nlc->s, &e); - if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) - break; - - if (rc > 0) { - if (FD_ISSET(nlc->s, &w)) { - // connection was successful - rc = 0; - } - if (FD_ISSET(nlc->s, &r)) { - // connection was closed - rc = SOCKET_ERROR; - lasterr = WSAECONNRESET; - } - if (FD_ISSET(nlc->s, &e)) { - // connection failed. - int len = sizeof(lasterr); - rc = SOCKET_ERROR; - getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); - if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { - closesocket(nlc->s); - goto retry; - } - } - break; - } - else if (Miranda_IsTerminated()) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - else if (nloc->flags & NLOCF_V2 && nloc->waitcallback != nullptr && nloc->waitcallback(&dwTimeout) == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - if (--dwTimeout == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - } - - if (rc == 0) break; - - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - } - - notblocking = 0; - if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); - if (rc && lasterr) SetLastError(lasterr); - return rc == 0; -} - -static bool my_connectIPv6(NetlibConnection *nlc, NETLIBOPENCONNECTION *nloc) -{ - if (!nloc) - return false; - - NetlibUser *nlu = nlc->nlu; - int rc = SOCKET_ERROR, retrycnt = 0; - u_long notblocking = 1; - DWORD lasterr = 0; - static const TIMEVAL tv = { 1, 0 }; - unsigned int dwTimeout = (nloc->flags & NLOCF_V2) ? nloc->timeout : 0; - // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway - if (dwTimeout == 0) dwTimeout = 30; - - // this is for XP SP2 where there is a default connection attempt limit of 10/second - if (connectionTimeout) { - WaitForSingleObject(hConnectionOpenMutex, 10000); - int waitdiff = GetTickCount() - g_LastConnectionTick; - if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); - g_LastConnectionTick = GetTickCount(); - ReleaseMutex(hConnectionOpenMutex); - - // might of died in between the wait - if (Miranda_IsTerminated()) return false; - } - - char szPort[6]; - addrinfo *air = nullptr, *ai, hints = { 0 }; - - hints.ai_family = AF_UNSPEC; - - if (nloc->flags & NLOCF_UDP) { - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - } - else { - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - } - - if (nlc->proxyType) { - if (!nlc->szProxyServer) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort); - - _itoa(nlc->wProxyPort, szPort, 10); - if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError()); - return false; - } - } - else { - if (!nloc->szHost) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort); - - _itoa(nlc->nloc.wPort, szPort, 10); - - if (GetAddrInfoA(nlc->nloc.szHost, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->nloc.szHost, WSAGetLastError()); - return false; - } - } - - for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) { - Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get()); -retry: - nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (nlc->s == INVALID_SOCKET) { - FreeAddrInfoA(air); - return false; - } - - // return the socket to non blocking - if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) { - FreeAddrInfoA(air); - return false; - } - - if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { - SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; - SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; - if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum)) - Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); - } - - // try a connect - if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) { - rc = 0; - break; - } - - // didn't work, was it cos of nonblocking? - if (WSAGetLastError() != WSAEWOULDBLOCK) { - rc = SOCKET_ERROR; - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - continue; - } - - while (true) { // timeout loop - fd_set r, w, e; - FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); - FD_SET(nlc->s, &r); - FD_SET(nlc->s, &w); - FD_SET(nlc->s, &e); - if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) - break; - - if (rc > 0) { - if (FD_ISSET(nlc->s, &w)) { - // connection was successful - rc = 0; - lasterr = 0; - } - if (FD_ISSET(nlc->s, &r)) { - // connection was closed - rc = SOCKET_ERROR; - lasterr = WSAECONNRESET; - } - if (FD_ISSET(nlc->s, &e)) { - // connection failed. - int len = sizeof(lasterr); - rc = SOCKET_ERROR; - getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); - if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - goto retry; - } - } - break; - } - else if (Miranda_IsTerminated()) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - else if (nloc->flags & NLOCF_V2 && nloc->waitcallback != nullptr && nloc->waitcallback(&dwTimeout) == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - if (--dwTimeout == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - } - - if (rc == 0) break; - - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - } - - FreeAddrInfoA(air); - - notblocking = 0; - if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); - if (rc && lasterr) SetLastError(lasterr); - return rc == 0; -} - -static int NetlibHttpFallbackToDirect(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc) -{ - NetlibDoCloseSocket(nlc, true); - - Netlib_Logf(nlu, "Fallback to direct connection"); - - nlc->proxyAuthNeeded = false; - nlc->proxyType = 0; - replaceStr(nlc->szProxyServer, nullptr); - if (!my_connectIPv6(nlc, nloc)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - return true; -} - -bool NetlibDoConnect(NetlibConnection *nlc) -{ - NETLIBOPENCONNECTION *nloc = &nlc->nloc; - NetlibUser *nlu = nlc->nlu; - - replaceStr(nlc->szProxyServer, nullptr); - - bool usingProxy = false, forceHttps = false; - if (nlu->settings.useProxy) { - if (nlu->settings.proxyType == PROXYTYPE_IE) - usingProxy = NetlibGetIeProxyConn(nlc, false); - else { - if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) { - nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer); - nlc->wProxyPort = nlu->settings.wProxyPort; - nlc->proxyType = nlu->settings.proxyType; - usingProxy = true; - } - } - } - - while (!my_connectIPv6(nlc, nloc)) { - // if connection failed, the state of nlc might be unpredictable - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) { - // Fallback to direct only when using HTTP proxy, as this is what used by companies - // If other type of proxy used it's an indication of security nutcase, leave him alone - if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) { - usingProxy = false; - nlc->proxyType = 0; - Netlib_Logf(nlu, "Fallback to direct connection"); - continue; - } - if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) { - forceHttps = true; - usingProxy = NetlibGetIeProxyConn(nlc, true); - if (usingProxy) - continue; - } - } - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - - if (usingProxy && !((nloc->flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) { - if (!WaitUntilWritable(nlc->s, 30000)) - return false; - - switch (nlc->proxyType) { - case PROXYTYPE_SOCKS4: - if (!NetlibInitSocks4Connection(nlc, nlu, nloc)) - return false; - break; - - case PROXYTYPE_SOCKS5: - if (!NetlibInitSocks5Connection(nlc, nlu, nloc)) - return false; - break; - - case PROXYTYPE_HTTPS: - case PROXYTYPE_HTTP: - nlc->proxyAuthNeeded = true; - if (!NetlibInitHttpsConnection(nlc, nlu, nloc)) { - usingProxy = false; - if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc)) - return false; - } - break; - - default: - SetLastError(ERROR_INVALID_PARAMETER); - FreePartiallyInitedConnection(nlc); - return false; - } - } - - Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nloc->szHost, nloc->wPort); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size); - if (nlu->settings.useProxy) { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size); - ncei.remote.sin_family = AF_INET; - ncei.remote.sin_port = htons((short)nloc->wPort); - ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nloc->szHost); - } - else { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size); - } - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - if (NLOCF_SSL & nloc->flags) - return Netlib_StartSsl(nlc, nullptr) != 0; - - return true; -} - -bool NetlibReconnect(NetlibConnection *nlc) -{ - // a connection might be freed already - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return false; - - char buf[4]; - bool opened = nlc->s != INVALID_SOCKET; - if (opened) { - switch (WaitUntilReadable(nlc->s, 0, true)) { - case SOCKET_ERROR: - opened = false; - break; - - case 0: - opened = true; - break; - - case 1: - opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - NetlibDoCloseSocket(nlc, true); - } - - if (!opened) { - if (Miranda_IsTerminated()) - return false; - - return NetlibDoConnect(nlc); - } - return true; -} - -MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const NETLIBOPENCONNECTION *nloc) -{ - if (nloc == nullptr || nloc->szHost == nullptr || nloc->wPort == 0) { - - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING)) - return nullptr; - - Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", nloc->szHost, nloc->wPort, nloc->flags); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlu; - nlc->nloc = *nloc; - nlc->nloc.szHost = mir_strdup(nloc->szHost); - nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0; - - if (!NetlibDoConnect(nlc)) { - FreePartiallyInitedConnection(nlc); - return nullptr; - } - - if (iUPnPCleanup == 0) { - mir_cslock lck(csNetlibUser); - iUPnPCleanup = 1; - mir_forkthread(NetlibUPnPCleanup); - } - - return nlc; -} - -MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (nlc == nullptr) - return 0; - - NetlibUser *nlu = nlc->nlu; - if (szHost == nullptr) - szHost = nlc->nloc.szHost; - - Netlib_Logf(nlu, "(%d %s) Starting SSL negotiation", nlc->s, szHost); - nlc->hSsl = sslApi.connect(nlc->s, szHost, nlu->settings.validateSSL); - - if (nlc->hSsl == nullptr) - Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL connection", nlc->s, szHost); - else - Netlib_Logf(nlu, "(%d %s) SSL negotiation successful", nlc->s, szHost); - - return nlc->hSsl != nullptr; -} - -NetlibConnection::NetlibConnection() -{ - handleType = NLH_CONNECTION; - s = s2 = INVALID_SOCKET; - hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); - NetlibInitializeNestedCS(&ncsSend); - NetlibInitializeNestedCS(&ncsRecv); -} - -NetlibConnection::~NetlibConnection() -{ - handleType = 0; - - if (s != INVALID_SOCKET) - closesocket(s); - - mir_free(szNewUrl); - mir_free(szProxyServer); - - mir_free(nlhpi.szHttpPostUrl); - mir_free(nlhpi.szHttpGetUrl); - - mir_free((char*)nloc.szHost); - - NetlibDeleteNestedCS(&ncsSend); - NetlibDeleteNestedCS(&ncsRecv); - - CloseHandle(hOkToCloseEvent); -} diff --git a/src/mir_app/src/netlibopts.cpp b/src/mir_app/src/netlibopts.cpp deleted file mode 100644 index 9ffeab1cce..0000000000 --- a/src/mir_app/src/netlibopts.cpp +++ /dev/null @@ -1,518 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -struct NetlibTempSettings -{ - DWORD flags; - char *szSettingsModule; - NETLIBUSERSETTINGS settings; -}; - -static LIST tempSettings(5); - -static const UINT outgoingConnectionsControls[] = -{ - IDC_STATIC12, - IDC_USEPROXY, - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS, - IDC_SPECIFYPORTSO, - IDC_PORTSRANGEO, - IDC_STATIC54, - IDC_VALIDATESSL}; -static const UINT useProxyControls[] = { - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS}; -static const UINT specifyOPortsControls[] = { - IDC_PORTSRANGEO, - IDC_STATIC54 -}; -static const UINT incomingConnectionsControls[] = { - IDC_STATIC43, - IDC_SPECIFYPORTS, - IDC_PORTSRANGE, - IDC_STATIC52, - IDC_ENABLEUPNP}; -static const UINT specifyPortsControls[] = { - IDC_PORTSRANGE, - IDC_STATIC52}; - -static const wchar_t* szProxyTypes[] = {LPGENW(""), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"}; -static const WORD oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080}; - -#define M_REFRESHALL (WM_USER+100) -#define M_REFRESHENABLING (WM_USER+101) - -static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - EnableWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType) -{ - int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type])); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type); - if (type == selectType) - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0); -} - -static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source) -{ - *dest = *source; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); -} - -static void CombineSettingsStrings(char **dest, char **source) -{ - if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; } -} - -static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, DWORD *destFlags, NETLIBUSERSETTINGS *source, DWORD sourceFlags) -{ - if (sourceFlags & NUF_OUTGOING) { - if (*destFlags & NUF_OUTGOING) { - if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2; - if (dest->useProxy != source->useProxy) dest->useProxy = 2; - if (dest->proxyType != source->proxyType) dest->proxyType = 0; - CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer); - if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0; - if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2; - CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser); - CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword); - if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2; - if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2; - CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts); - } - else { - dest->validateSSL = source->validateSSL; - dest->useProxy = source->useProxy; - dest->proxyType = source->proxyType; - dest->szProxyServer = source->szProxyServer; - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); - dest->wProxyPort = source->wProxyPort; - dest->useProxyAuth = source->useProxyAuth; - dest->szProxyAuthUser = source->szProxyAuthUser; - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - dest->szProxyAuthPassword = source->szProxyAuthPassword; - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - dest->dnsThroughProxy = source->dnsThroughProxy; - dest->specifyOutgoingPorts = source->specifyOutgoingPorts; - dest->szOutgoingPorts = source->szOutgoingPorts; - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - } - } - if (sourceFlags & NUF_INCOMING) { - if (*destFlags & NUF_INCOMING) { - if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2; - if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2; - CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts); - } - else { - dest->enableUPnP = source->enableUPnP; - dest->specifyIncomingPorts = source->specifyIncomingPorts; - dest->szIncomingPorts = source->szIncomingPorts; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - } - } - if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION)) - *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION; - else *destFlags |= sourceFlags; -} - -static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED; - CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - *(int*)(((PBYTE)&p->settings) + memberOffset) = newValue; - } - else *(int*)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset) = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); -} - -static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId)); - char *szNewValue = (char*)mir_alloc(newValueLen+1); - GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1); - if (iUser == -1) { - for (auto &p : tempSettings) { - if (!(p->flags & NUF_NOOPTIONS)) { - char **ppszNew = (char**)(((PBYTE)&p->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = mir_strdup(szNewValue); - } - } - mir_free(szNewValue); - } - else { - char **ppszNew = (char**)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = szNewValue; - } -} - -static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, DWORD flags) -{ - if (flags & NUF_OUTGOING) { - db_set_b(0, szSettingsModule, "NLValidateSSL", (BYTE)settings->validateSSL); - db_set_b(0, szSettingsModule, "NLUseProxy", (BYTE)settings->useProxy); - db_set_b(0, szSettingsModule, "NLProxyType", (BYTE)settings->proxyType); - db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : ""); - db_set_w(0, szSettingsModule, "NLProxyPort", (WORD)settings->wProxyPort); - db_set_b(0, szSettingsModule, "NLUseProxyAuth", (BYTE)settings->useProxyAuth); - db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : ""); - db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : ""); - db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (BYTE)settings->dnsThroughProxy); - db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (BYTE)settings->specifyOutgoingPorts); - db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : ""); - } - if (flags & NUF_INCOMING) { - db_set_b(0, szSettingsModule, "NLEnableUPnP", (BYTE)settings->enableUPnP); - db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (BYTE)settings->specifyIncomingPorts); - db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : ""); - } -} - -void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings) -{ - mir_cslock lck(csNetlibUser); - - NetlibUser tUser; - tUser.user.szSettingsModule = (char*)szSettingsModule; - NetlibUser *thisUser = netlibUser.find(&tUser); - if (thisUser == nullptr) - return; - - NetlibFreeUserSettingsStruct(&thisUser->settings); - CopySettingsStruct(&thisUser->settings, settings); - WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags); - - NETLIBUSERSETTINGS combinedSettings = { 0 }; - combinedSettings.cbSize = sizeof(combinedSettings); - - DWORD flags = 0; - for (auto &p : netlibUser) { - if (p->user.flags & NUF_NOOPTIONS) - continue; - CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags); - } - if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0; - if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0; - if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5; - if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0; - if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1; - if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1; - if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0; - WriteSettingsStructToDb("Netlib", &combinedSettings, flags); - NetlibFreeUserSettingsStruct(&combinedSettings); -} - -static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int iUser; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("")); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0); - - mir_cslock lck(csNetlibUser); - for (auto &it : netlibUser) { - NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings)); - thisSettings->flags = it->user.flags; - thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule); - CopySettingsStruct(&thisSettings->settings, &it->settings); - tempSettings.insert(thisSettings); - - if (it->user.flags & NUF_NOOPTIONS) - continue; - iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it)); - } - } - - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return TRUE; - - case M_REFRESHALL: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - { - NETLIBUSERSETTINGS settings = { 0 }; - DWORD flags = 0; - - if (iUser == -1) { - settings.cbSize = sizeof(settings); - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - CombineSettingsStructs(&settings, &flags, &p->settings, p->flags); - } - else { - NetlibFreeUserSettingsStruct(&settings); - CopySettingsStruct(&settings, &tempSettings[iUser]->settings); - flags = tempSettings[iUser]->flags; - } - ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0); - if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType); - if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType); - if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType); - if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION)) - AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType); - SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : ""); - if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE); - else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, ""); - CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth); - SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : ""); - SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : ""); - CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy); - CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL); - - ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP); - - NetlibFreeUserSettingsStruct(&settings); - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - break; - - case M_REFRESHENABLING: - wchar_t str[80]; - { - int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]); - SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str); - if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) { - int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1; - EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE); - if (selectedProxyType == 0) { - for (auto &p : tempSettings) { - if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING)) - continue; - - if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - enableAuth = 1; - if (p->settings.useProxyAuth) - enableUser = enablePass = 1; - } - } - } - else { - if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - if (selectedProxyType == PROXYTYPE_IE) enableServer = 0; - enableAuth = 1; - if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED) - enableUser = enablePass = 1; - } - } - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer); - } - else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE); - EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED); - EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED); - } - break; - - case WM_COMMAND: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - switch (LOWORD(wParam)) { - case IDC_NETLIBUSERS: - if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return 0; - - case IDC_LOGOPTIONS: - NetlibLogShowOptions(); - return 0; - - case IDC_PROXYTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - if (iUser == -1) { - if (newValue == 0) - return 0; - - for (auto &p : tempSettings) { - if (p->flags & NUF_NOOPTIONS) - continue; - if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS)) - p->settings.proxyType = PROXYTYPE_HTTPS; - else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION) - p->settings.proxyType = PROXYTYPE_HTTP; - else p->settings.proxyType = newValue; - } - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - } - else { - tempSettings[iUser]->settings.proxyType = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - } - break; - case IDC_USEPROXY: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy)); - break; - case IDC_PROXYAUTH: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth)); - break; - case IDC_PROXYDNS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy)); - break; - case IDC_SPECIFYPORTS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts)); - break; - case IDC_SPECIFYPORTSO: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts)); - break; - case IDC_ENABLEUPNP: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP)); - break; - case IDC_VALIDATESSL: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL)); - break; - case IDC_PROXYHOST: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer)); - break; - case IDC_PROXYPORT: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - { - int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - p->settings.wProxyPort = newValue; - } - else tempSettings[iUser]->settings.wProxyPort = newValue; - } - break; - case IDC_PROXYUSER: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser)); - break; - case IDC_PROXYPASS: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword)); - break; - case IDC_PORTSRANGE: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts)); - break; - case IDC_PORTSRANGEO: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts)); - break; - } - ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - for (auto &p : tempSettings) - NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings); - return TRUE; - } - break; - } - break; - - case WM_DESTROY: - for (auto &p : tempSettings) { - mir_free(p->szSettingsModule); - NetlibFreeUserSettingsStruct(&p->settings); - mir_free(p); - } - tempSettings.destroy(); - break; - } - return FALSE; -} - -int NetlibOptInitialise(WPARAM wParam, LPARAM) -{ - int optionsCount = 0; - { - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (!(p->user.flags & NUF_NOOPTIONS)) - ++optionsCount; - } - - if (optionsCount == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.position = 900000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); - odp.szTitle.a = LPGEN("Network"); - odp.pfnDlgProc = DlgProcNetlibOpts; - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} diff --git a/src/mir_app/src/netlibpktrecver.cpp b/src/mir_app/src/netlibpktrecver.cpp deleted file mode 100644 index 8214910dfc..0000000000 --- a/src/mir_app/src/netlibpktrecver.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); - nlpr->handleType = NLH_PACKETRECVER; - nlpr->nlc = nlc; - nlpr->packetRecver.bufferSize = iMaxSize; - nlpr->packetRecver.buffer = (PBYTE)mir_alloc(nlpr->packetRecver.bufferSize); - nlpr->packetRecver.bytesUsed = 0; - nlpr->packetRecver.bytesAvailable = 0; - return nlpr; -} - -MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam) -{ - NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver; - if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout; - if (nlprParam->bytesUsed == 0) { - if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) { - nlpr->packetRecver.bytesAvailable = 0; - Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching"); - } - } - else { - memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed); - nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed; - } - - if (nlprParam->dwTimeout != INFINITE) { - if (!sslApi.pending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) { - *nlprParam = nlpr->packetRecver; - return SOCKET_ERROR; - } - } - - INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0); - if (recvResult > 0) - nlpr->packetRecver.bytesAvailable += recvResult; - *nlprParam = nlpr->packetRecver; - return recvResult; -} diff --git a/src/mir_app/src/netlibsecurity.cpp b/src/mir_app/src/netlibsecurity.cpp deleted file mode 100644 index c48becd6c9..0000000000 --- a/src/mir_app/src/netlibsecurity.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define SECURITY_WIN32 -#include -#include - -#pragma comment(lib, "secur32.lib") - -struct NtlmHandleType -{ - CtxtHandle hClientContext; - CredHandle hClientCredential; - wchar_t* szProvider; - wchar_t* szPrincipal; - unsigned cbMaxToken; - bool hasDomain; -}; - -struct NTLM_String -{ - WORD len; - WORD allocedSpace; - DWORD offset; -}; - -struct NtlmType2packet -{ - char sign[8]; - DWORD type; // == 2 - NTLM_String targetName; - DWORD flags; - BYTE challenge[8]; - BYTE context[8]; - NTLM_String targetInfo; -}; - -static unsigned ntlmCnt = 0; -static mir_cs csSec; - -static void ReportSecError(SECURITY_STATUS scRet, int line) -{ - wchar_t szMsgBuf[256]; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - - wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0; - - Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal) -{ - HANDLE hSecurity = nullptr; - - if (mir_wstrcmpi(szProvider, L"Basic") == 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->szProvider = mir_wstrdup(szProvider); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - - return hNtlm; - } - - mir_cslock lck(csSec); - - PSecPkgInfo ntlmSecurityPackageInfo; - SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo); - if (sc == SEC_E_OK) { - NtlmHandleType* hNtlm; - - hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; - FreeContextBuffer(ntlmSecurityPackageInfo); - - hNtlm->szProvider = mir_wstrdup(szProvider); - hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L""); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - } - return hSecurity; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity) -{ - if (hSecurity == nullptr) - return; - - mir_cslock lck(csSec); - - if (ntlmCnt != 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - if (hNtlm != nullptr) { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - mir_free(hNtlm->szProvider); - mir_free(hNtlm->szPrincipal); - mir_free(hNtlm); - } - - --ntlmCnt; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) -{ - if (!szChallenge || !szChallenge[0]) return nullptr; - - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - unsigned char inDataBuffer[1024]; - - SecBuffer inBuffers[2] = - { - { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, - { chlsz, SECBUFFER_STREAM, szChallenge } - }; - - SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; - - unsigned long qop = 0; - SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - // unsigned char LayerMask = inDataBuffer[0]; - // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); - - SecPkgContext_Sizes sizes; - sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); - unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); - - unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; - - SecBuffer outBuffers[3] = - { - { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, - { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, - { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } - }; - SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; - - sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned i, ressz = 0; - for (i = 0; i < outBuffersDesc.cBuffers; i++) - ressz += outBuffersDesc.pBuffers[i].cbBuffer; - - unsigned char *response = (unsigned char*)alloca(ressz), *p = response; - for (i = 0; i < outBuffersDesc.cBuffers; i++) { - memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); - p += outBuffersDesc.pBuffers[i].cbBuffer; - } - - return mir_base64_encode(response, ressz); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete) -{ - if (hSecurity == nullptr || ntlmCnt == 0) - return nullptr; - - SecBufferDesc outputBufferDescriptor, inputBufferDescriptor; - SecBuffer outputSecurityToken, inputSecurityToken; - char *szOutputToken; - - NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity; - - Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete); - - if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) { - bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0; - bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0'; - if (hasChallenge) { - size_t tokenLen; - BYTE *token = (BYTE*)mir_base64_decode(szChallenge, &tokenLen); - if (token == nullptr) - return nullptr; - - if (isGSSAPI && complete) - return CompleteGssapi(hSecurity, token, (unsigned)tokenLen); - - inputBufferDescriptor.cBuffers = 1; - inputBufferDescriptor.pBuffers = &inputSecurityToken; - inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - inputSecurityToken.BufferType = SECBUFFER_TOKEN; - inputSecurityToken.cbBuffer = (unsigned)tokenLen; - inputSecurityToken.pvBuffer = token; - - // try to decode the domain name from the NTLM challenge - if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) { - NtlmType2packet* pkt = (NtlmType2packet*)token; - if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { - - wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset]; - int domainLen = pkt->targetName.len; - - // Negotiate ANSI? if yes, convert the ANSI name to unicode - if ((pkt->flags & 1) == 0) { - int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0); - wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t)); - domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; - buf[domainLen] = 0; - domainName = buf; - } - else domainLen /= sizeof(wchar_t); - - if (domainLen) { - CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login); - char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete); - mir_free(szChl); - } - } - } - } - else { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - - SEC_WINNT_AUTH_IDENTITY auth; - - if (login != nullptr && login[0] != '\0') { - memset(&auth, 0, sizeof(auth)); - - Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); - - const wchar_t* loginName = login; - const wchar_t* domainName = wcschr(login, '\\'); - size_t domainLen = 0; - size_t loginLen = mir_wstrlen(loginName); - if (domainName != nullptr) { - loginName = domainName + 1; - loginLen = mir_wstrlen(loginName); - domainLen = domainName - login; - domainName = login; - } - else if ((domainName = wcschr(login, '@')) != nullptr) { - loginName = login; - loginLen = domainName - login; - domainLen = mir_wstrlen(++domainName); - } - - auth.User = (PWORD)loginName; - auth.UserLength = (ULONG)loginLen; - auth.Password = (PWORD)psw; - auth.PasswordLength = (ULONG)mir_wstrlen(psw); - auth.Domain = (PWORD)domainName; - auth.DomainLength = (ULONG)domainLen; - auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - - hNtlm->hasDomain = domainLen != 0; - } - - TimeStamp tokenExpiration; - SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - } - - outputBufferDescriptor.cBuffers = 1; - outputBufferDescriptor.pBuffers = &outputSecurityToken; - outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - outputSecurityToken.BufferType = SECBUFFER_TOKEN; - outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; - outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); - - ULONG contextAttributes; - TimeStamp tokenExpiration; - SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential, - hasChallenge ? &hNtlm->hClientContext : nullptr, - hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, - hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext, - &outputBufferDescriptor, &contextAttributes, &tokenExpiration); - Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc); - - complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); - if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { - sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); - Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc); - } - - if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer); - } - else { - if (!login || !psw) - return nullptr; - - T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw)); - szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth)); - complete = true; - } - - if (szOutputToken == nullptr) - return nullptr; - - if (!http) - return szOutputToken; - - CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken); - mir_free(szOutputToken); - return szResult.Detach(); -} - -MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete) -{ - return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete); -} diff --git a/src/mir_app/src/netlibsock.cpp b/src/mir_app/src/netlibsock.cpp deleted file mode 100644 index afe705d98d..0000000000 --- a/src/mir_app/src/netlibsock.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern HANDLE hConnectionHeaderMutex, hSendEvent, hRecvEvent; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - int result; - NetlibDumpData(nlc, (PBYTE)buf, len, 1, flags); - if (nlc->hSsl) - result = sslApi.write(nlc->hSsl, buf, len); - else - result = send(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsSend); - - NETLIBNOTIFY nln = { buf, len, flags, result }; - NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return SOCKET_ERROR; - - int recvResult; - if (!nlc->foreBuf.isEmpty()) { - recvResult = min(len, (int)nlc->foreBuf.length()); - memcpy(buf, nlc->foreBuf.data(), recvResult); - nlc->foreBuf.remove(recvResult); - } - else if (nlc->hSsl) - recvResult = sslApi.read(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0); - else - recvResult = recv(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - if (recvResult <= 0) - return recvResult; - - NetlibDumpData(nlc, (PBYTE)buf, recvResult, 0, flags); - - if ((flags & MSG_PEEK) == 0) { - NETLIBNOTIFY nln = { buf, len, flags, recvResult }; - NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - } - return recvResult; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending) -{ - FD_ZERO(fd); - for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) { - NetlibConnection *nlcCheck = hConns[i]; - if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) { - SetLastError(ERROR_INVALID_DATA); - return 0; - } - FD_SET(nlcCheck->s, fd); - if (!nlcCheck->foreBuf.isEmpty() || sslApi.pending(nlcCheck->hSsl)) - pending++; - } - return 1; -} - -MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - WaitForSingleObject(hConnectionHeaderMutex, INFINITE); - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) - { - ReleaseMutex(hConnectionHeaderMutex); - return SOCKET_ERROR; - } - ReleaseMutex(hConnectionHeaderMutex); - if (pending) - return 1; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); -} - -MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - WaitForSingleObject(hConnectionHeaderMutex, INFINITE); - - int pending = 0; - fd_set readfd, writefd, exceptfd; - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) - { - ReleaseMutex(hConnectionHeaderMutex); - return SOCKET_ERROR; - } - ReleaseMutex(hConnectionHeaderMutex); - - int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); - - WaitForSingleObject(hConnectionHeaderMutex, INFINITE); - /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() - to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) - This happens for read/write/except */ - NetlibConnection *conn = nullptr; - int j; - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hReadConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - - if (sslApi.pending(conn->hSsl)) - nls->hReadStatus[j] = TRUE; - nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hWriteConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hExceptConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd); - } - ReleaseMutex(hConnectionHeaderMutex); - return rc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr) -{ - if (!str) return false; - - int len = sizeof(SOCKADDR_INET_M); - return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len); -} - -MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr) -{ - char saddr[128]; - DWORD len = sizeof(saddr); - if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len)) - return mir_strdup(saddr); - - if (addr->sin_family == AF_INET) { - char *szIp = inet_ntoa(addr->sin_addr); - if (addr->sin_port != 0) { - mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port); - return mir_strdup(saddr); - } - return mir_strdup(szIp); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN hConnection, NETLIBCONNINFO *connInfo) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (!nlc || !connInfo) - return 1; - - sockaddr_in sin = { 0 }; - int len = sizeof(sin); - if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) { - connInfo->wPort = ntohs(sin.sin_port); - connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0; - strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -inline bool IsAddrGlobal(const IN6_ADDR *a) -{ - unsigned char High = a->s6_bytes[0] & 0xf0; - return High != 0 && High != 0xf0; -} - -MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly) -{ - addrinfo *air = nullptr, *ai, hints = { 0 }; - const char *szMyHost = ""; - - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_PASSIVE; - - if (GetAddrInfoA(szMyHost, nullptr, &hints, &air)) - return nullptr; - - unsigned n = 0; - for (ai = air; ai; ai = ai->ai_next) { - SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) - ++n; - } - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - unsigned i = 0; - for (ai = air; ai; ai = ai->ai_next) { - sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) { - char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr); - if (szIp) - strncpy_s(addr->szIp[i++], szIp, _TRUNCATE); - mir_free(szIp); - } - } - FreeAddrInfoA(air); - return addr; -} - -static NETLIBIPLIST* GetMyIpv4(void) -{ - char hostname[256] = ""; - - gethostname(hostname, sizeof(hostname)); - PHOSTENT he = gethostbyname(hostname); - - unsigned n; - for (n = 0; he->h_addr_list[n]; ++n) - ; - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - for (unsigned i = 0; i < n; i++) - strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE); - - return addr; -} diff --git a/src/mir_app/src/netlibupnp.cpp b/src/mir_app/src/netlibupnp.cpp deleted file mode 100644 index 4b18050603..0000000000 --- a/src/mir_app/src/netlibupnp.cpp +++ /dev/null @@ -1,813 +0,0 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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. -*/ - -#include "stdafx.h" -#include "netlib.h" - -static const char search_request_msg[] = - "M-SEARCH * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "MAN: \"ssdp:discover\"\r\n" - "MX: 1\r\n" - "ST: urn:schemas-upnp-org:service:%s\r\n" - "\r\n"; - -static const char xml_get_hdr[] = - "GET %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "ACCEPT-LANGUAGE: *\r\n\r\n"; - -static const char soap_post_hdr[] = - "POST %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char soap_post_hdr_m[] = - "M-POST %s URL HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n" - "01-SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char search_device[] = - "%s"; - -static const char soap_action[] = - "\r\n" - "\r\n" - " \r\n" - " \r\n" - "%s" - " \r\n" - " \r\n" - "\r\n"; - -static const char soap_query[] = - "\r\n" - " \r\n" - " \r\n" - " %s\r\n" - " \r\n" - " \r\n" - "\r\n"; - -static const char add_port_mapping[] = - " \r\n" - " %i\r\n" - " %s\r\n" - " %i\r\n" - " %s\r\n" - " 1\r\n" - " Miranda\r\n" - " 0\r\n"; - -static const char delete_port_mapping[] = - " \r\n" - " %i\r\n" - " %s\r\n"; - -static const char get_port_mapping[] = - " %i\r\n"; - -static bool gatewayFound; -static SOCKADDR_IN locIP; -static time_t lastDiscTime; -static int expireTime = 120; - -static int retryCount; -static SOCKET sock = INVALID_SOCKET; -static char szConnHost[256]; -static unsigned short sConnPort; - -static WORD *portList; -static unsigned numports, numportsAlloc; -static HANDLE portListMutex; - -static char szCtlUrl[256], szDev[256]; - -typedef enum -{ - DeviceGetReq, - ControlAction, - ControlQuery -} ReqType; - -static bool txtParseParam(char* szData, char* presearch, - char* start, char* finish, char* param, size_t size) -{ - char *cp, *cp1; - size_t len; - - *param = 0; - - if (presearch != nullptr) { - cp1 = strstr(szData, presearch); - if (cp1 == nullptr) return false; - } - else - cp1 = szData; - - cp = strstr(cp1, start); - if (cp == nullptr) return false; - cp += mir_strlen(start); - while (*cp == ' ') ++cp; - - cp1 = strstr(cp, finish); - if (cp1 == nullptr) return false; - while (*(cp1-1) == ' ' && cp1 > cp) --cp1; - - len = min((size_t)(cp1 - cp), size-1); - strncpy(param, cp, len); - param[len] = 0; - - return true; -} - -void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath) -{ - char *ppath, *phost, *pport; - int sz; - - phost = strstr(szUrl, "://"); - if (phost == nullptr) phost = szUrl; - else phost += 3; - - ppath = strchr(phost, '/'); - if (ppath == nullptr) ppath = phost + mir_strlen(phost); - - pport = strchr(phost, ':'); - if (pport == nullptr) pport = ppath; - - if (szHost != nullptr) { - sz = pport - phost + 1; - if (sz > 256) sz = 256; - strncpy(szHost, phost, sz); - szHost[sz - 1] = 0; - } - - if (sPort != nullptr) { - if (pport < ppath) { - long prt = atol(pport + 1); - *sPort = prt != 0 ? (unsigned short)prt : 80; - } - else - *sPort = 80; - } - - if (szPath != nullptr) { - strncpy(szPath, ppath, 256); - szPath[255] = 0; - } -} - -static void LongLog(char* szData) -{ - Netlib_Logf(nullptr, szData); -} - -static void closeRouterConnection(void) -{ - if (sock != INVALID_SOCKET) { - closesocket(sock); - sock = INVALID_SOCKET; - } -} - -static void validateSocket(void) -{ - static const TIMEVAL tv = { 0, 0 }; - fd_set rfd; - char buf[4]; - - if (sock == INVALID_SOCKET) - return; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - bool opened = false; - switch (select(1, &rfd, nullptr, nullptr, &tv)) { - case 0: - opened = true; - break; - - case 1: - opened = recv(sock, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - closeRouterConnection(); -} - -static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype) -{ - // Parse URL - char szHost[256], szPath[256], szRes[16]; - int sz = 0, res = 0; - unsigned short sPort; - bool needClose = false; - - const char* szPostHdr = soap_post_hdr; - char* szData = (char*)mir_alloc(4096); - char* szReq = nullptr; - - parseURL(szUrl, szHost, &sPort, szPath); - - if (sPort != sConnPort || _stricmp(szHost, szConnHost)) - closeRouterConnection(); - else - validateSocket(); - - while (true) { - retryCount = 0; - switch (reqtype) { - case DeviceGetReq: - sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort); - break; - - case ControlAction: - { - char szData1[1024]; - - szReq = mir_strdup(szResult); - sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1); - } - break; - - case ControlQuery: - { - char szData1[1024]; - sz = mir_snprintf(szData1, soap_query, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1); - } - break; - } - szResult[0] = 0; - { - static const TIMEVAL tv = { 6, 0 }; - static unsigned ttl = 4; - static u_long mode = 1; - fd_set rfd, wfd, efd; - SOCKADDR_IN enetaddr; - -retrycon: - if (sock == INVALID_SOCKET) { - sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(sPort); - enetaddr.sin_addr.s_addr = inet_addr(szHost); - - // Resolve host name if needed - if (enetaddr.sin_addr.s_addr == INADDR_NONE) { - PHOSTENT he = gethostbyname(szHost); - if (he) - enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; - } - - Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort); - - FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); - FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd); - - // Limit the scope of the connection (does not work for - setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); - - // Put socket into non-blocking mode for timeout on connect - ioctlsocket(sock, FIONBIO, &mode); - - // Connect to the remote host - if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) { - int err = WSAGetLastError(); - - // Socket connection failed - if (err != WSAEWOULDBLOCK) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed %d", err); - break; - } - // Wait for socket to connect - else if (select(1, &rfd, &wfd, &efd, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect timeout"); - break; - } - else if (!FD_ISSET(sock, &wfd)) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed"); - break; - } - } - strncpy_s(szConnHost, szHost, _TRUNCATE); - sConnPort = sPort; - } - - if (send(sock, szData, sz, 0) != SOCKET_ERROR) { - char *hdrend = nullptr; - int acksz = 0, pktsz = 0; - - if (szActionName == nullptr) { - int len = sizeof(locIP); - getsockname(sock, (SOCKADDR*)&locIP, &len); - if (locIP.sin_addr.S_un.S_addr == 0x0100007f) { - struct hostent *he; - - gethostname(szPath, sizeof(szPath)); - he = gethostbyname(szPath); - if (he != nullptr) - locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0]; - } - } - - LongLog(szData); - sz = 0; - while (true) { - int bytesRecv; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - // Wait for the next packet - if (select(1, &rfd, nullptr, nullptr, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP recieve timeout"); - break; - } - - // - bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0); - - // Connection closed or aborted, all data received - if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) { - closeRouterConnection(); - if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) { - ++retryCount; - goto retrycon; - } - break; - } - - sz += bytesRecv; - - // Insert null terminator to use string functions - if (sz >= (resSize - 1)) { - szResult[resSize - 1] = 0; - break; - } - else - szResult[sz] = 0; - - // HTTP header found? - if (hdrend == nullptr) { - // Find HTTP header end - hdrend = strstr(szResult, "\r\n\r\n"); - if (hdrend == nullptr) { - hdrend = strstr(szResult, "\n\n"); - if (hdrend) hdrend += 2; - } - - else - hdrend += 4; - - if (hdrend != nullptr) { - // Get packet size if provided - if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) || - txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) { - // Add size of HTTP header to the packet size to compute full transmission size - pktsz = atol(ltrimp(szRes)) + (hdrend - szResult); - } - // Get encoding type if provided - else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) { - if (_stricmp(lrtrimp(szRes), "Chunked") == 0) - acksz = hdrend - szResult; - } - if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) { - needClose = (_stricmp(lrtrimp(szRes), "close") == 0); - } - } - } - - // Content-Length bytes reached, all data received - if (sz >= pktsz && pktsz != 0) { - szResult[pktsz] = 0; - break; - } - - // Chunked encoding processing - if (sz > acksz && acksz != 0) { -retry: - // Parse out chunk size - char* data = szResult + acksz; - char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n'); - if (peol1 != nullptr) { - char *peol2 = strchr(++peol1, '\n'); - if (peol2 != nullptr) { - // Get chunk size - int chunkBytes = strtol(peol1, nullptr, 16); - acksz += chunkBytes; - peol2++; - - memmove(data, peol2, mir_strlen(peol2) + 1); - sz -= peol2 - data; - - // Last chunk, all data received - if (chunkBytes == 0) break; - if (sz > acksz) goto retry; - } - } - } - } - LongLog(szResult); - } - else { - if (retryCount < 2) { - closeRouterConnection(); - ++retryCount; - goto retrycon; - } - else - Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError()); - } - } - txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); - res = atol(szRes); - if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr) - szPostHdr = soap_post_hdr_m; - else - break; - } - - if (needClose) - closeRouterConnection(); - - mir_free(szData); - mir_free(szReq); - return res; -} - -static unsigned getExtIP(void) -{ - char szExtIP[30]; - char* szData = (char*)mir_alloc(4096); szData[0] = 0; - - unsigned extip = 0; - int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction); - if (res == 200 && txtParseParam(szData, "", "<", szExtIP, sizeof(szExtIP))) - extip = ntohl(inet_addr(szExtIP)); - - mir_free(szData); - return extip; -} - -static bool getUPnPURLs(char* szUrl, size_t sizeUrl) -{ - char* szData = (char*)mir_alloc(8192); - - gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200; - if (gatewayFound) { - char szTemp[256], *rpth; - size_t ctlLen; - - txtParseParam(szData, nullptr, "", "", szTemp, sizeof(szTemp)); - strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl)); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - - mir_snprintf(szTemp, search_device, szDev); - txtParseParam(szData, szTemp, "", "", szUrl, sizeUrl); - - // URL combining per RFC 2396 - if (szUrl[0] != 0) { - if (strstr(szUrl, "://") != nullptr) // absolute URI - rpth = szCtlUrl; - else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path - { - rpth = strstr(szCtlUrl, "//"); - if (rpth == nullptr) rpth = szCtlUrl; - } - else if (szUrl[0] == '/') // relative URI abs_path - { - rpth = strstr(szCtlUrl, "//"); - rpth = rpth ? rpth + 2 : szCtlUrl; - - rpth = strchr(rpth, '/'); - if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl); - } - else { // relative URI rel_path - size_t ctlCLen = mir_strlen(szCtlUrl); - rpth = szCtlUrl + ctlCLen; - if (ctlCLen != 0 && *(rpth - 1) != '/') - strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen); - } - - ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl); - strncpy(rpth, szUrl, ctlLen); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - } - else { - szCtlUrl[0] = 0; - gatewayFound = false; - } - } - mir_free(szData); - - return gatewayFound; -} - -static void discoverUPnP(void) -{ - char* buf; - int buflen; - unsigned i, j, nip = 0; - unsigned* ips = nullptr; - - static const unsigned any = INADDR_ANY; - static const TIMEVAL tv = { 1, 600000 }; - - char szUrl[256] = ""; - char hostname[256]; - PHOSTENT he; - fd_set readfd; - - SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - SOCKADDR_IN enetaddr; - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(1900); - enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); - - gethostname(hostname, sizeof(hostname)); - he = gethostbyname(hostname); - - if (he) { - while (he->h_addr_list[nip]) ++nip; - - ips = (unsigned*)mir_alloc(nip * sizeof(unsigned)); - - for (j = 0; j < nip; j++) - ips[j] = *(unsigned*)he->h_addr_list[j]; - } - - buf = (char*)mir_alloc(1500); - - for (i = 3; --i && szUrl[0] == 0;) { - for (j = 0; j < nip; j++) { - if (ips) - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned)); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - } - - if (Miranda_IsTerminated()) break; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) { - buflen = recv(s, buf, 1500, 0); - if (buflen != SOCKET_ERROR) { - buf[buflen] = 0; - LongLog(buf); - - if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) || - txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) { - char age[30]; - char szHostNew[256], szHostExist[256]; - - lrtrim(szUrl); - - parseURL(szUrl, szHostNew, nullptr, nullptr); - parseURL(szCtlUrl, szHostExist, nullptr, nullptr); - if (mir_strcmp(szHostNew, szHostExist) == 0) { - gatewayFound = true; - break; - } - - txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev)); - txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age)); - expireTime = atoi(lrtrimp(age)); - lrtrim(szDev); - - if (getUPnPURLs(szUrl, sizeof(szUrl))) { - gatewayFound = getExtIP() != 0; - if (gatewayFound) break; - } - } - } - FD_ZERO(&readfd); - FD_SET(s, &readfd); - } - } - - mir_free(buf); - mir_free(ips); - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned)); - closesocket(s); -} - -static bool findUPnPGateway(void) -{ - if ((time(0) - lastDiscTime) >= expireTime) { - WaitForSingleObject(portListMutex, INFINITE); - - time_t curTime = time(0); - - if ((curTime - lastDiscTime) >= expireTime) { - gatewayFound = false; - - discoverUPnP(); - lastDiscTime = curTime; - - Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl); - } - - ReleaseMutex(portListMutex); - } - - return gatewayFound; -} - -bool NetlibUPnPAddPortMapping(WORD intport, char *proto, WORD *extport, DWORD *extip, bool search) -{ - int res = 0, i = 5; - - if (findUPnPGateway()) { - char* szData = (char*)mir_alloc(4096); - char szExtIP[30]; - - *extport = intport - 1; - *extip = ntohl(locIP.sin_addr.S_un.S_addr); - - WaitForSingleObject(portListMutex, INFINITE); - - do { - ++*extport; - mir_snprintf(szData, 4096, add_port_mapping, - *extport, proto, intport, inet_ntoa(locIP.sin_addr)); - res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction); - txtParseParam(szData, nullptr, "", "", szExtIP, sizeof(szExtIP)); - - } while (search && res == 500 && atol(szExtIP) == 718 && --i); - - mir_free(szData); - - if (res == 200) { - unsigned ip = getExtIP(); - if (ip) *extip = ip; - - if (numports >= numportsAlloc) - mir_realloc(portList, sizeof(WORD)*(numportsAlloc += 10)); - portList[numports++] = *extport; - } - - ReleaseMutex(portListMutex); - } - - return res == 200; -} - -void NetlibUPnPDeletePortMapping(WORD extport, char* proto) -{ - if (extport == 0) - return; - - // findUPnPGateway(); - - if (gatewayFound) { - unsigned i; - char* szData = (char*)mir_alloc(4096); - - WaitForSingleObject(portListMutex, INFINITE); - mir_snprintf(szData, 4096, delete_port_mapping, extport, proto); - httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction); - - for (i = 0; i < numports; i++) - if (portList[i] == extport && --numports > 0) - memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(WORD)); - - mir_free(szData); - ReleaseMutex(portListMutex); - } -} - -void NetlibUPnPCleanup(void*) -{ - // upnp is disabled globally, no need for a cleanup - if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0) - return; - - { - int incoming = 0; - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (p->user.flags & NUF_INCOMING) { - incoming = 1; - break; - } - - if (!incoming) - return; - } - - if (findUPnPGateway()) { - char *szData = (char*)alloca(4096); - char buf[50], lip[50]; - unsigned j = 0, k, num = 100; - - strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE); - - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 && - txtParseParam(szData, "QueryStateVariableResponse", "", "<", buf, sizeof(buf))) - num = atol(buf); - - WORD ports[30]; - for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) { - mir_snprintf(szData, 4096, get_port_mapping, i); - - ReleaseMutex(portListMutex); - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200) - break; - - if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0) - continue; - - if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0) - continue; - - if (txtParseParam(szData, "", "<", buf, sizeof(buf))) { - WORD mport = (WORD)atol(buf); - - if (j >= _countof(ports)) - break; - - for (k = 0; k < numports; ++k) - if (portList[k] == mport) - break; - - if (k >= numports) - ports[j++] = mport; - } - } - - ReleaseMutex(portListMutex); - - for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++) - NetlibUPnPDeletePortMapping(ports[i], "TCP"); - } -} - -void NetlibUPnPInit(void) -{ - numports = 0; - numportsAlloc = 10; - portList = (WORD*)mir_alloc(sizeof(WORD)*numportsAlloc); - - portListMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUPnPDestroy(void) -{ - mir_free(portList); - CloseHandle(portListMutex); -} -- cgit v1.2.3