diff options
author | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-05-15 10:38:20 +0000 |
---|---|---|
committer | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-05-15 10:38:20 +0000 |
commit | 48540940b6c28bb4378abfeb500ec45a625b37b6 (patch) | |
tree | 2ef294c0763e802f91d868bdef4229b6868527de /src/modules/netlib | |
parent | 5c350913f011e119127baeb32a6aedeb4f0d33bc (diff) |
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'src/modules/netlib')
-rw-r--r-- | src/modules/netlib/netlib.cpp | 640 | ||||
-rw-r--r-- | src/modules/netlib/netlib.h | 209 | ||||
-rw-r--r-- | src/modules/netlib/netlibautoproxy.cpp | 460 | ||||
-rw-r--r-- | src/modules/netlib/netlibbind.cpp | 287 | ||||
-rw-r--r-- | src/modules/netlib/netlibhttp.cpp | 1331 | ||||
-rw-r--r-- | src/modules/netlib/netlibhttpproxy.cpp | 522 | ||||
-rw-r--r-- | src/modules/netlib/netliblog.cpp | 637 | ||||
-rw-r--r-- | src/modules/netlib/netlibopenconn.cpp | 944 | ||||
-rw-r--r-- | src/modules/netlib/netlibopts.cpp | 556 | ||||
-rw-r--r-- | src/modules/netlib/netlibpktrecver.cpp | 85 | ||||
-rw-r--r-- | src/modules/netlib/netlibsecurity.cpp | 570 | ||||
-rw-r--r-- | src/modules/netlib/netlibsock.cpp | 203 | ||||
-rw-r--r-- | src/modules/netlib/netlibssl.cpp | 981 | ||||
-rw-r--r-- | src/modules/netlib/netlibupnp.cpp | 885 |
14 files changed, 8310 insertions, 0 deletions
diff --git a/src/modules/netlib/netlib.cpp b/src/modules/netlib/netlib.cpp new file mode 100644 index 0000000000..000639474f --- /dev/null +++ b/src/modules/netlib/netlib.cpp @@ -0,0 +1,640 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+static BOOL bModuleInitialized = FALSE;
+
+HANDLE hConnectionHeaderMutex, hConnectionOpenMutex;
+DWORD g_LastConnectionTick;
+int connectionTimeout;
+HANDLE hSendEvent=NULL, hRecvEvent=NULL;
+
+typedef BOOL (WINAPI *tGetProductInfo)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+
+static int CompareNetlibUser(const NetlibUser* p1, const NetlibUser* p2)
+{
+ return strcmp(p1->user.szSettingsModule, p2->user.szSettingsModule);
+}
+
+LIST<NetlibUser> netlibUser(5, CompareNetlibUser);
+CRITICAL_SECTION csNetlibUser;
+
+SSL_API si;
+
+void NetlibFreeUserSettingsStruct(NETLIBUSERSETTINGS *settings)
+{
+ mir_free(settings->szIncomingPorts);
+ mir_free(settings->szOutgoingPorts);
+ mir_free(settings->szProxyAuthPassword);
+ mir_free(settings->szProxyAuthUser);
+ mir_free(settings->szProxyServer);
+}
+
+void NetlibInitializeNestedCS(struct NetlibNestedCriticalSection *nlncs)
+{
+ nlncs->dwOwningThreadId= 0;
+ nlncs->lockCount=0;
+ nlncs->hMutex=CreateMutex(NULL,FALSE,NULL);
+}
+
+void NetlibDeleteNestedCS(struct NetlibNestedCriticalSection *nlncs)
+{
+ CloseHandle(nlncs->hMutex);
+}
+
+int NetlibEnterNestedCS(struct NetlibConnection *nlc,int which)
+{
+ struct NetlibNestedCriticalSection *nlncs;
+ DWORD dwCurrentThreadId=GetCurrentThreadId();
+
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ if(nlc==NULL || nlc->handleType!=NLH_CONNECTION) {
+ ReleaseMutex(hConnectionHeaderMutex);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ nlncs = (which == NLNCS_SEND) ? &nlc->ncsSend : &nlc->ncsRecv;
+ if(nlncs->lockCount && nlncs->dwOwningThreadId==dwCurrentThreadId) {
+ nlncs->lockCount++;
+ ReleaseMutex(hConnectionHeaderMutex);
+ return 1;
+ }
+ InterlockedIncrement(&nlc->dontCloseNow);
+ ResetEvent(nlc->hOkToCloseEvent);
+ ReleaseMutex(hConnectionHeaderMutex);
+ WaitForSingleObject(nlncs->hMutex,INFINITE);
+ nlncs->dwOwningThreadId=dwCurrentThreadId;
+ nlncs->lockCount=1;
+ if(InterlockedDecrement(&nlc->dontCloseNow)==0)
+ SetEvent(nlc->hOkToCloseEvent);
+ return 1;
+}
+
+void NetlibLeaveNestedCS(struct NetlibNestedCriticalSection *nlncs)
+{
+ if(--nlncs->lockCount==0) {
+ nlncs->dwOwningThreadId=0;
+ ReleaseMutex(nlncs->hMutex);
+ }
+}
+
+static INT_PTR GetNetlibUserSettingInt(const char *szUserModule,const char *szSetting,int defValue)
+{
+ DBVARIANT dbv;
+ if(DBGetContactSetting(NULL,szUserModule,szSetting,&dbv)
+ && DBGetContactSetting(NULL,"Netlib",szSetting,&dbv))
+ return defValue;
+ if(dbv.type==DBVT_BYTE) return dbv.bVal;
+ if(dbv.type==DBVT_WORD) return dbv.wVal;
+ return dbv.dVal;
+}
+
+static char *GetNetlibUserSettingString(const char *szUserModule,const char *szSetting,int decode)
+{
+ DBVARIANT dbv;
+ if(DBGetContactSettingString(NULL,szUserModule,szSetting,&dbv)
+ && DBGetContactSettingString(NULL,"Netlib",szSetting,&dbv)) {
+ return NULL;
+ }
+ else {
+ char *szRet;
+ if(decode) CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ szRet=mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ if(szRet==NULL) SetLastError(ERROR_OUTOFMEMORY);
+ return szRet;
+ }
+}
+
+static INT_PTR NetlibRegisterUser(WPARAM,LPARAM lParam)
+{
+ NETLIBUSER *nlu=(NETLIBUSER*)lParam;
+ struct NetlibUser *thisUser;
+
+ if(nlu==NULL || nlu->cbSize!=sizeof(NETLIBUSER) || nlu->szSettingsModule==NULL
+ || (!(nlu->flags&NUF_NOOPTIONS) && nlu->szDescriptiveName==NULL)
+ || (nlu->flags&NUF_HTTPGATEWAY && (nlu->pfnHttpGatewayInit==NULL))) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ thisUser = (struct NetlibUser*)mir_calloc(sizeof(struct NetlibUser));
+ thisUser->handleType = NLH_USER;
+ thisUser->user = *nlu;
+
+ EnterCriticalSection(&csNetlibUser);
+ if (netlibUser.getIndex(thisUser) >= 0)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ mir_free(thisUser);
+ SetLastError(ERROR_DUP_NAME);
+ return 0;
+ }
+ LeaveCriticalSection(&csNetlibUser);
+
+ if (nlu->szDescriptiveName) {
+ thisUser->user.ptszDescriptiveName = (thisUser->user.flags&NUF_UNICODE ? mir_u2t((WCHAR*)nlu->ptszDescriptiveName) : mir_a2t(nlu->szDescriptiveName));
+ }
+ if((thisUser->user.szSettingsModule=mir_strdup(nlu->szSettingsModule))==NULL
+ || (nlu->szDescriptiveName && thisUser->user.ptszDescriptiveName ==NULL)
+ || (nlu->szHttpGatewayUserAgent && (thisUser->user.szHttpGatewayUserAgent=mir_strdup(nlu->szHttpGatewayUserAgent))==NULL))
+ {
+ mir_free(thisUser);
+ SetLastError(ERROR_OUTOFMEMORY);
+ return 0;
+ }
+ if (nlu->szHttpGatewayHello)
+ thisUser->user.szHttpGatewayHello=mir_strdup(nlu->szHttpGatewayHello);
+ else
+ thisUser->user.szHttpGatewayHello=NULL;
+
+ thisUser->settings.cbSize=sizeof(NETLIBUSERSETTINGS);
+ thisUser->settings.useProxy=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxy",0);
+ thisUser->settings.proxyType=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLProxyType",PROXYTYPE_SOCKS5);
+ if(thisUser->user.flags&NUF_NOHTTPSOPTION && thisUser->settings.proxyType==PROXYTYPE_HTTPS)
+ thisUser->settings.proxyType=PROXYTYPE_HTTP;
+ if(!(thisUser->user.flags&(NUF_HTTPCONNS|NUF_HTTPGATEWAY)) && thisUser->settings.proxyType==PROXYTYPE_HTTP) {
+ thisUser->settings.useProxy=0;
+ thisUser->settings.proxyType=PROXYTYPE_SOCKS5;
+ }
+ thisUser->settings.szProxyServer=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyServer",0);
+ thisUser->settings.wProxyPort=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLProxyPort",1080);
+ thisUser->settings.useProxyAuth=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxyAuth",0);
+ thisUser->settings.szProxyAuthUser=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyAuthUser",0);
+ thisUser->settings.szProxyAuthPassword=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyAuthPassword",1);
+ thisUser->settings.dnsThroughProxy=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLDnsThroughProxy",1);
+ thisUser->settings.specifyIncomingPorts=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLSpecifyIncomingPorts",0);
+ thisUser->settings.szIncomingPorts=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLIncomingPorts",0);
+ thisUser->settings.specifyOutgoingPorts=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLSpecifyOutgoingPorts",0);
+ thisUser->settings.szOutgoingPorts=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLOutgoingPorts",0);
+ thisUser->settings.enableUPnP=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLEnableUPnP",1); //default to on
+ thisUser->settings.validateSSL=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLValidateSSL",0);
+
+ thisUser->toLog=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLlog",1);
+
+ EnterCriticalSection(&csNetlibUser);
+ netlibUser.insert(thisUser);
+ LeaveCriticalSection(&csNetlibUser);
+ return (INT_PTR)thisUser;
+}
+
+static INT_PTR NetlibGetUserSettings(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBUSERSETTINGS *nlus=(NETLIBUSERSETTINGS*)lParam;
+ struct NetlibUser *nlu=(struct NetlibUser*)wParam;
+
+ if(GetNetlibHandleType(nlu)!=NLH_USER || nlus==NULL || nlus->cbSize!=sizeof(NETLIBUSERSETTINGS)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ *nlus=nlu->settings;
+ return 1;
+}
+
+static INT_PTR NetlibSetUserSettings(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBUSERSETTINGS *nlus=(NETLIBUSERSETTINGS*)lParam;
+ struct NetlibUser *nlu=(struct NetlibUser*)wParam;
+
+ if(GetNetlibHandleType(nlu)!=NLH_USER || nlus==NULL || nlus->cbSize!=sizeof(NETLIBUSERSETTINGS)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ NetlibSaveUserSettingsStruct(nlu->user.szSettingsModule,nlus);
+ return 1;
+}
+
+void NetlibDoClose(NetlibConnection *nlc, bool noShutdown)
+{
+ if (nlc->s == INVALID_SOCKET) return;
+
+ NetlibLogf(nlc->nlu, "(%p:%u) Connection closed internal", nlc, nlc->s);
+ if (nlc->hSsl)
+ {
+ if (!noShutdown) si.shutdown(nlc->hSsl);
+ si.sfree(nlc->hSsl);
+ nlc->hSsl = NULL;
+ }
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+}
+
+INT_PTR NetlibCloseHandle(WPARAM wParam, LPARAM)
+{
+ switch(GetNetlibHandleType(wParam)) {
+ case NLH_USER:
+ { struct NetlibUser *nlu=(struct NetlibUser*)wParam;
+ int i;
+
+ EnterCriticalSection(&csNetlibUser);
+ i = netlibUser.getIndex(nlu);
+ if (i >= 0) netlibUser.remove(i);
+ LeaveCriticalSection(&csNetlibUser);
+
+ NetlibFreeUserSettingsStruct(&nlu->settings);
+ mir_free(nlu->user.szSettingsModule);
+ mir_free(nlu->user.szDescriptiveName);
+ mir_free(nlu->user.szHttpGatewayHello);
+ mir_free(nlu->user.szHttpGatewayUserAgent);
+ mir_free(nlu->szStickyHeaders);
+ break;
+ }
+ case NLH_CONNECTION:
+ { struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ HANDLE waitHandles[4];
+ DWORD waitResult;
+
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ if (nlc->usingHttpGateway)
+ {
+ HttpGatewayRemovePacket(nlc, -1);
+ }
+ else
+ {
+ if(nlc->s != INVALID_SOCKET) {
+ NetlibDoClose(nlc);
+ }
+ if (nlc->s2 != INVALID_SOCKET) closesocket(nlc->s2);
+ nlc->s2 = INVALID_SOCKET;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ waitHandles[0]=hConnectionHeaderMutex;
+ waitHandles[1]=nlc->hOkToCloseEvent;
+ waitHandles[2]=nlc->ncsRecv.hMutex;
+ waitHandles[3]=nlc->ncsSend.hMutex;
+ waitResult=WaitForMultipleObjects( SIZEOF(waitHandles),waitHandles,TRUE,INFINITE);
+ if(waitResult<WAIT_OBJECT_0 || waitResult >= WAIT_OBJECT_0 + SIZEOF(waitHandles)) {
+ ReleaseMutex(hConnectionHeaderMutex);
+ SetLastError(ERROR_INVALID_PARAMETER); //already been closed
+ return 0;
+ }
+ nlc->handleType=0;
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->dataBuffer);
+ mir_free((char*)nlc->nloc.szHost);
+ mir_free(nlc->szNewUrl);
+ mir_free(nlc->szProxyServer);
+ NetlibDeleteNestedCS(&nlc->ncsRecv);
+ NetlibDeleteNestedCS(&nlc->ncsSend);
+ CloseHandle(nlc->hOkToCloseEvent);
+ DeleteCriticalSection(&nlc->csHttpSequenceNums);
+ ReleaseMutex(hConnectionHeaderMutex);
+ NetlibLogf(nlc->nlu,"(%p:%u) Connection closed",nlc,nlc->s);
+ break;
+ }
+ case NLH_BOUNDPORT:
+ return NetlibFreeBoundPort((struct NetlibBoundPort*)wParam);
+ case NLH_PACKETRECVER:
+ { struct NetlibPacketRecver *nlpr=(struct NetlibPacketRecver*)wParam;
+ mir_free(nlpr->packetRecver.buffer);
+ break;
+ }
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ mir_free((void*)wParam);
+ return 1;
+}
+
+static INT_PTR NetlibGetSocket(WPARAM wParam, LPARAM)
+{
+ SOCKET s;
+ if(wParam==0) {
+ s=INVALID_SOCKET;
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else {
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ switch(GetNetlibHandleType(wParam)) {
+ case NLH_CONNECTION:
+ s=((struct NetlibConnection*)wParam)->s;
+ break;
+ case NLH_BOUNDPORT:
+ s=((struct NetlibBoundPort*)wParam)->s;
+ break;
+ default:
+ s=INVALID_SOCKET;
+ SetLastError(ERROR_INVALID_PARAMETER);
+ break;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ }
+ return s;
+}
+
+INT_PTR NetlibShutdown(WPARAM wParam, LPARAM)
+{
+ if (wParam)
+ {
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ switch(GetNetlibHandleType(wParam)) {
+ case NLH_CONNECTION:
+ {
+ struct NetlibConnection* nlc = (struct NetlibConnection*)wParam;
+ if (nlc->hSsl) si.shutdown(nlc->hSsl);
+ if (nlc->s != INVALID_SOCKET) shutdown(nlc->s, 2);
+ if (nlc->s2 != INVALID_SOCKET) shutdown(nlc->s2, 2);
+ nlc->termRequested = true;
+ }
+ break;
+ case NLH_BOUNDPORT:
+ {
+ struct NetlibBoundPort* nlb = (struct NetlibBoundPort*)wParam;
+ if (nlb->s != INVALID_SOCKET) shutdown(nlb->s, 2);
+ }
+ break;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ }
+ return 0;
+}
+
+static const char szHexDigits[]="0123456789ABCDEF";
+INT_PTR NetlibHttpUrlEncode(WPARAM,LPARAM lParam)
+{
+ unsigned char *szOutput,*szInput=(unsigned char*)lParam;
+ unsigned char *pszIn,*pszOut;
+ int outputLen;
+
+ if(szInput==NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (INT_PTR)(char*)NULL;
+ }
+ for(outputLen=0,pszIn=szInput;*pszIn;pszIn++) {
+ if ( (48 <= *pszIn && *pszIn <= 57) ||//0-9
+ (65 <= *pszIn && *pszIn <= 90) ||//ABC...XYZ
+ (97 <= *pszIn && *pszIn <= 122) ||//abc...xyz
+ *pszIn == '-' || *pszIn == '_' || *pszIn == '.' || *pszIn == ' ') outputLen++;
+ else outputLen+=3;
+ }
+ szOutput=(unsigned char*)HeapAlloc(GetProcessHeap(),0,outputLen+1);
+ if(szOutput==NULL) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return (INT_PTR)(unsigned char*)NULL;
+ }
+ for(pszOut=szOutput,pszIn=szInput;*pszIn;pszIn++) {
+ if ( (48 <= *pszIn && *pszIn <= 57) ||
+ (65 <= *pszIn && *pszIn <= 90) ||
+ (97 <= *pszIn && *pszIn <= 122) ||
+ *pszIn == '-' || *pszIn == '_' || *pszIn == '.') *pszOut++=*pszIn;
+ else if(*pszIn==' ') *pszOut++='+';
+ else {
+ *pszOut++='%';
+ *pszOut++=szHexDigits[*pszIn>>4];
+ *pszOut++=szHexDigits[*pszIn&0xF];
+ }
+ }
+ *pszOut='\0';
+ return (INT_PTR)szOutput;
+}
+
+static const char base64chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+INT_PTR NetlibBase64Encode(WPARAM, LPARAM lParam)
+{
+ NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam;
+ int iIn;
+ char *pszOut;
+ PBYTE pbIn;
+
+ if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if(nlb64->cchEncoded<Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded)) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ nlb64->cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded);
+ for(iIn=0,pbIn=nlb64->pbDecoded,pszOut=nlb64->pszEncoded;iIn<nlb64->cbDecoded;iIn+=3,pbIn+=3,pszOut+=4) {
+ pszOut[0]=base64chars[pbIn[0]>>2];
+ if(nlb64->cbDecoded-iIn==1) {
+ pszOut[1]=base64chars[(pbIn[0]&3)<<4];
+ pszOut[2]='=';
+ pszOut[3]='=';
+ pszOut+=4;
+ break;
+ }
+ pszOut[1]=base64chars[((pbIn[0]&3)<<4)|(pbIn[1]>>4)];
+ if(nlb64->cbDecoded-iIn==2) {
+ pszOut[2]=base64chars[(pbIn[1]&0xF)<<2];
+ pszOut[3]='=';
+ pszOut+=4;
+ break;
+ }
+ pszOut[2]=base64chars[((pbIn[1]&0xF)<<2)|(pbIn[2]>>6)];
+ pszOut[3]=base64chars[pbIn[2]&0x3F];
+ }
+ pszOut[0]='\0';
+ return 1;
+}
+
+static BYTE Base64CharToInt(char c)
+{
+ if(c>='A' && c<='Z') return c-'A';
+ if(c>='a' && c<='z') return c-'a'+26;
+ if(c>='0' && c<='9') return c-'0'+52;
+ if(c=='+') return 62;
+ if(c=='/') return 63;
+ if(c=='=') return 64;
+ return 255;
+}
+
+INT_PTR NetlibBase64Decode(WPARAM, LPARAM lParam)
+{
+ NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam;
+ char *pszIn;
+ PBYTE pbOut;
+ BYTE b1,b2,b3,b4;
+ int iIn;
+
+ if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if(nlb64->cchEncoded&3) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ if(nlb64->cbDecoded<Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded)) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ nlb64->cbDecoded=Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded);
+ for(iIn=0,pszIn=nlb64->pszEncoded,pbOut=nlb64->pbDecoded;iIn<nlb64->cchEncoded;iIn+=4,pszIn+=4,pbOut+=3) {
+ b1=Base64CharToInt(pszIn[0]);
+ b2=Base64CharToInt(pszIn[1]);
+ b3=Base64CharToInt(pszIn[2]);
+ b4=Base64CharToInt(pszIn[3]);
+ if(b1==255 || b1==64 || b2==255 || b2==64 || b3==255 || b4==255) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ pbOut[0]=(b1<<2)|(b2>>4);
+ if(b3==64) {nlb64->cbDecoded-=2; break;}
+ pbOut[1]=(b2<<4)|(b3>>2);
+ if(b4==64) {nlb64->cbDecoded--; break;}
+ pbOut[2]=b4|(b3<<6);
+ }
+ return 1;
+}
+
+void UnloadNetlibModule(void)
+{
+ if (!bModuleInitialized) return;
+
+ if (hConnectionHeaderMutex != NULL)
+ {
+ int i;
+
+ NetlibUnloadIeProxy();
+ NetlibSecurityDestroy();
+ NetlibUPnPDestroy();
+ NetlibLogShutdown();
+
+ DestroyHookableEvent(hRecvEvent); hRecvEvent = NULL;
+ DestroyHookableEvent(hSendEvent); hSendEvent = NULL;
+
+ for (i = netlibUser.getCount(); i > 0; i--)
+ NetlibCloseHandle((WPARAM)netlibUser[i-1], 0);
+
+ netlibUser.destroy();
+
+ CloseHandle(hConnectionHeaderMutex);
+ if (hConnectionOpenMutex) CloseHandle(hConnectionOpenMutex);
+ DeleteCriticalSection(&csNetlibUser);
+ WSACleanup();
+ }
+}
+
+int LoadNetlibModule(void)
+{
+ WSADATA wsadata;
+
+ bModuleInitialized = TRUE;
+
+ WSAStartup(MAKEWORD(2,2), &wsadata);
+
+ HookEvent(ME_OPT_INITIALISE,NetlibOptInitialise);
+
+ InitializeCriticalSection(&csNetlibUser);
+ hConnectionHeaderMutex=CreateMutex(NULL,FALSE,NULL);
+ NetlibLogInit();
+
+ connectionTimeout = 0;
+
+ OSVERSIONINFOEX osvi = {0};
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (GetVersionEx((LPOSVERSIONINFO)&osvi))
+ {
+ // Connection limiting was introduced in Windows XP SP2 and later and set to 10 / sec
+ if (osvi.dwMajorVersion == 5 && ((osvi.dwMinorVersion == 1 && osvi.wServicePackMajor >= 2) || osvi.dwMinorVersion > 1))
+ connectionTimeout = 150;
+ // Connection limiting has limits based on addition Windows Vista pre SP2
+ else if (osvi.dwMajorVersion == 6 && osvi.wServicePackMajor < 2)
+ {
+ DWORD dwType = 0;
+ tGetProductInfo pGetProductInfo = (tGetProductInfo) GetProcAddress(GetModuleHandleA("kernel32"), "GetProductInfo");
+ if (pGetProductInfo != NULL) pGetProductInfo(6, 0, 0, 0, &dwType);
+ switch( dwType )
+ {
+ case 0x01: // Vista Ultimate edition have connection limit of 25 / sec - plenty for Miranda
+ case 0x1c:
+ break;
+
+ case 0x02: // Vista Home Basic edition have connection limit of 2 / sec
+ case 0x05:
+ connectionTimeout = 1000;
+ break;
+
+ default: // all other editions have connection limit of 10 / sec
+ connectionTimeout = 150;
+ break;
+ }
+ }
+ // Connection limiting is disabled by default and is controlled by registry setting in Windows Vista SP2 and later
+ else if (osvi.dwMajorVersion >= 6)
+ {
+ static const char keyn[] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
+ static const char valn[] = "EnableConnectionRateLimiting";
+
+ HKEY hSettings;
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyn, 0, KEY_QUERY_VALUE, &hSettings) == ERROR_SUCCESS)
+ {
+ DWORD tValueLen, enabled;
+ tValueLen = sizeof(enabled);
+ if (RegQueryValueExA(hSettings, valn, NULL, NULL, (BYTE*)&enabled, &tValueLen) == ERROR_SUCCESS && enabled)
+ connectionTimeout = 150; // if enabled limit is set to 10 / sec
+ RegCloseKey(hSettings);
+ }
+
+ }
+ }
+
+ hConnectionOpenMutex = connectionTimeout ? CreateMutex(NULL,FALSE,NULL) : NULL;
+ g_LastConnectionTick = GetTickCount();
+
+ CreateServiceFunction(MS_NETLIB_REGISTERUSER,NetlibRegisterUser);
+ CreateServiceFunction(MS_NETLIB_GETUSERSETTINGS,NetlibGetUserSettings);
+ CreateServiceFunction(MS_NETLIB_SETUSERSETTINGS,NetlibSetUserSettings);
+ CreateServiceFunction(MS_NETLIB_CLOSEHANDLE,NetlibCloseHandle);
+ CreateServiceFunction(MS_NETLIB_BINDPORT,NetlibBindPort);
+ CreateServiceFunction(MS_NETLIB_OPENCONNECTION,NetlibOpenConnection);
+ CreateServiceFunction(MS_NETLIB_SETHTTPPROXYINFO,NetlibHttpGatewaySetInfo);
+ CreateServiceFunction(MS_NETLIB_SETSTICKYHEADERS,NetlibHttpSetSticky);
+ CreateServiceFunction(MS_NETLIB_GETSOCKET,NetlibGetSocket);
+ CreateServiceFunction(MS_NETLIB_URLENCODE,NetlibHttpUrlEncode);
+ CreateServiceFunction(MS_NETLIB_BASE64ENCODE,NetlibBase64Encode);
+ CreateServiceFunction(MS_NETLIB_BASE64DECODE,NetlibBase64Decode);
+ CreateServiceFunction(MS_NETLIB_SENDHTTPREQUEST,NetlibHttpSendRequest);
+ CreateServiceFunction(MS_NETLIB_RECVHTTPHEADERS,NetlibHttpRecvHeaders);
+ CreateServiceFunction(MS_NETLIB_FREEHTTPREQUESTSTRUCT,NetlibHttpFreeRequestStruct);
+ CreateServiceFunction(MS_NETLIB_HTTPTRANSACTION,NetlibHttpTransaction);
+ CreateServiceFunction(MS_NETLIB_SEND,NetlibSend);
+ CreateServiceFunction(MS_NETLIB_RECV,NetlibRecv);
+ CreateServiceFunction(MS_NETLIB_SELECT,NetlibSelect);
+ CreateServiceFunction(MS_NETLIB_SELECTEX,NetlibSelectEx);
+ CreateServiceFunction(MS_NETLIB_SHUTDOWN,NetlibShutdown);
+ CreateServiceFunction(MS_NETLIB_CREATEPACKETRECVER,NetlibPacketRecverCreate);
+ CreateServiceFunction(MS_NETLIB_GETMOREPACKETS,NetlibPacketRecverGetMore);
+ CreateServiceFunction(MS_NETLIB_SETPOLLINGTIMEOUT,NetlibHttpSetPollingTimeout);
+ CreateServiceFunction(MS_NETLIB_STARTSSL,NetlibStartSsl);
+
+ hRecvEvent = CreateHookableEvent(ME_NETLIB_FASTRECV);
+ hSendEvent = CreateHookableEvent(ME_NETLIB_FASTSEND);
+
+ NetlibUPnPInit();
+ NetlibSecurityInit();
+ NetlibLoadIeProxy();
+
+ return 0;
+}
+
+void NetlibInitSsl(void)
+{
+ mir_getSI(&si);
+}
diff --git a/src/modules/netlib/netlib.h b/src/modules/netlib/netlib.h new file mode 100644 index 0000000000..575e77f18c --- /dev/null +++ b/src/modules/netlib/netlib.h @@ -0,0 +1,209 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/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.
+*/
+#define GetNetlibHandleType(h) (h?*(int*)h:NLH_INVALID)
+#define NLH_INVALID 0
+#define NLH_USER 'USER'
+#define NLH_CONNECTION 'CONN'
+#define NLH_BOUNDPORT 'BIND'
+#define NLH_PACKETRECVER 'PCKT'
+
+struct NetlibUser
+{
+ int handleType;
+ NETLIBUSER user;
+ NETLIBUSERSETTINGS settings;
+ char * szStickyHeaders;
+ int toLog;
+ int inportnum;
+ int outportnum;
+};
+
+struct NetlibNestedCriticalSection
+{
+ HANDLE hMutex;
+ DWORD dwOwningThreadId;
+ int lockCount;
+};
+
+struct NetlibHTTPProxyPacketQueue
+{
+ struct NetlibHTTPProxyPacketQueue *next;
+ PBYTE dataBuffer;
+ int dataBufferLen;
+};
+
+struct NetlibConnection
+{
+ int handleType;
+ SOCKET s, s2;
+ bool usingHttpGateway;
+ bool usingDirectHttpGateway;
+ bool proxyAuthNeeded;
+ bool dnsThroughProxy;
+ bool termRequested;
+ struct NetlibUser *nlu;
+ NETLIBHTTPPROXYINFO nlhpi;
+ PBYTE dataBuffer;
+ int dataBufferLen;
+ CRITICAL_SECTION csHttpSequenceNums;
+ HANDLE hOkToCloseEvent;
+ LONG dontCloseNow;
+ struct NetlibNestedCriticalSection ncsSend,ncsRecv;
+ HSSL hSsl;
+ struct NetlibHTTPProxyPacketQueue * pHttpProxyPacketQueue;
+ char *szNewUrl;
+ char *szProxyServer;
+ WORD wProxyPort;
+ int proxyType;
+ int pollingTimeout;
+ unsigned lastPost;
+ NETLIBOPENCONNECTION nloc;
+};
+
+struct NetlibBoundPort {
+ int handleType;
+ SOCKET s;
+ WORD wPort;
+ WORD wExPort;
+ struct NetlibUser *nlu;
+ NETLIBNEWCONNECTIONPROC_V2 pfnNewConnectionV2;
+ HANDLE hThread;
+ void *pExtra;
+};
+
+struct NetlibPacketRecver {
+ int handleType;
+ struct NetlibConnection *nlc;
+ NETLIBPACKETRECVER packetRecver;
+};
+
+//netlib.c
+void NetlibFreeUserSettingsStruct(NETLIBUSERSETTINGS *settings);
+void NetlibDoClose(NetlibConnection *nlc, bool noShutdown = false);
+INT_PTR NetlibCloseHandle(WPARAM wParam,LPARAM lParam);
+void NetlibInitializeNestedCS(struct NetlibNestedCriticalSection *nlncs);
+void NetlibDeleteNestedCS(struct NetlibNestedCriticalSection *nlncs);
+#define NLNCS_SEND 0
+#define NLNCS_RECV 1
+int NetlibEnterNestedCS(struct NetlibConnection *nlc,int which);
+void NetlibLeaveNestedCS(struct NetlibNestedCriticalSection *nlncs);
+INT_PTR NetlibBase64Encode(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibBase64Decode(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpUrlEncode(WPARAM wParam,LPARAM lParam);
+
+extern CRITICAL_SECTION csNetlibUser;
+extern LIST<NetlibUser> netlibUser;
+
+//netlibautoproxy.c
+void NetlibLoadIeProxy(void);
+void NetlibUnloadIeProxy(void);
+char* NetlibGetIeProxy(char *szUrl);
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps);
+
+//netlibbind.c
+int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp);
+INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam);
+bool BindSocketToPort(const char *szPorts, SOCKET s, int* portn);
+
+//netlibhttp.c
+INT_PTR NetlibHttpSendRequest(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpRecvHeaders(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpFreeRequestStruct(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpTransaction(WPARAM wParam,LPARAM lParam);
+void NetlibHttpSetLastErrorUsingHttpResult(int result);
+NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection* nlc, DWORD hflags, DWORD dflags, bool isConnect = false);
+void NetlibConnFromUrl(const char* szUrl, bool secur, NETLIBOPENCONNECTION &nloc);
+
+//netlibhttpproxy.c
+int NetlibInitHttpConnection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc);
+int NetlibHttpGatewayRecv(struct NetlibConnection *nlc,char *buf,int len,int flags);
+int NetlibHttpGatewayPost(struct NetlibConnection *nlc,const char *buf,int len,int flags);
+void HttpGatewayRemovePacket(NetlibConnection *nlc, int pck);
+
+INT_PTR NetlibHttpGatewaySetInfo(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpSetPollingTimeout(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpSetSticky(WPARAM wParam, LPARAM lParam);
+
+//netliblog.c
+void NetlibLogShowOptions(void);
+void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags);
+void NetlibLogf(NetlibUser* nlu, const char *fmt, ...);
+void NetlibLogInit(void);
+void NetlibLogShutdown(void);
+
+//netlibopenconn.c
+DWORD DnsLookup(struct NetlibUser *nlu,const char *szHost);
+int WaitUntilReadable(SOCKET s,DWORD dwTimeout, bool check = false);
+int WaitUntilWritable(SOCKET s,DWORD dwTimeout);
+bool NetlibDoConnect(NetlibConnection *nlc);
+bool NetlibReconnect(NetlibConnection *nlc);
+INT_PTR NetlibOpenConnection(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibStartSsl(WPARAM wParam, LPARAM lParam);
+
+//netlibopts.c
+int NetlibOptInitialise(WPARAM wParam,LPARAM lParam);
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule,NETLIBUSERSETTINGS *settings);
+
+//netlibpktrecver.c
+INT_PTR NetlibPacketRecverCreate(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibPacketRecverGetMore(WPARAM wParam,LPARAM lParam);
+
+//netlibsock.c
+#define NL_SELECT_READ 0x0001
+#define NL_SELECT_WRITE 0x0002
+#define NL_SELECT_ALL (NL_SELECT_READ+NL_SELECT_WRITE)
+
+INT_PTR NetlibSend(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibRecv(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibSelect(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibSelectEx(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibShutdown(WPARAM wParam,LPARAM lParam);
+
+//netlibupnp.c
+bool NetlibUPnPAddPortMapping(WORD intport, char *proto,
+ WORD *extport, DWORD *extip, bool search);
+void NetlibUPnPDeletePortMapping(WORD extport, char* proto);
+void NetlibUPnPCleanup(void*);
+void NetlibUPnPInit(void);
+void NetlibUPnPDestroy(void);
+
+//netlibsecurity.c
+void NetlibSecurityInit(void);
+void NetlibSecurityDestroy(void);
+void NetlibDestroySecurityProvider(HANDLE hSecurity);
+HANDLE NetlibInitSecurityProvider(const TCHAR* szProvider, const TCHAR* szPrincipal);
+#ifdef UNICODE
+HANDLE NetlibInitSecurityProvider(const char* szProvider, const char* szPrincipal);
+#endif
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const TCHAR* login, const TCHAR* psw,
+ bool http, unsigned& complete);
+
+
+static __inline INT_PTR NLSend(struct NetlibConnection *nlc,const char *buf,int len,int flags) {
+ NETLIBBUFFER nlb={(char*)buf,len,flags};
+ return NetlibSend((WPARAM)nlc,(LPARAM)&nlb);
+}
+static __inline INT_PTR NLRecv(struct NetlibConnection *nlc,char *buf,int len,int flags) {
+ NETLIBBUFFER nlb={buf,len,flags};
+ return NetlibRecv((WPARAM)nlc,(LPARAM)&nlb);
+}
diff --git a/src/modules/netlib/netlibautoproxy.cpp b/src/modules/netlib/netlibautoproxy.cpp new file mode 100644 index 0000000000..e169cf9c89 --- /dev/null +++ b/src/modules/netlib/netlibautoproxy.cpp @@ -0,0 +1,460 @@ +/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2010-2011 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+#include <wininet.h>
+/*
+/////////////////////////////////////////////////////////////////////
+// ResolveHostName (a helper function)
+/////////////////////////////////////////////////////////////////////
+DWORD __stdcall ResolveHostName(LPSTR lpszHostName,
+ LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
+{
+ if (*lpdwIPAddressSize < 17 || lpszIPAddress == NULL)
+ {
+ *lpdwIPAddressSize = 17;
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ IN_ADDR ip;
+ ip.s_addr = inet_addr(lpszHostName);
+ if (ip.s_addr == INADDR_NONE)
+ {
+ PHOSTENT myhost = gethostbyname(lpszHostName);
+ if (myhost != NULL)
+ ip = *(PIN_ADDR)myhost->h_addr;
+ else
+ return SOCKET_ERROR;
+ }
+ mir_snprintf(lpszIPAddress, *lpdwIPAddressSize, "%u.%u.%u.%u",
+ ip.s_net, ip.s_host, ip.s_lh, ip.s_impno);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// IsResolvable (a helper function)
+/////////////////////////////////////////////////////////////////////
+BOOL __stdcall IsResolvable(LPSTR lpszHost)
+{
+ char szDummy[255];
+ DWORD dwDummySize = sizeof (szDummy) - 1;
+
+ if (ResolveHostName(lpszHost, szDummy, &dwDummySize))
+ return FALSE;
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////
+// GetIPAddress (a helper function)
+/////////////////////////////////////////////////////////////////////
+DWORD __stdcall GetIPAddress(LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
+{
+ char szHostBuffer[255];
+
+ if (gethostname(szHostBuffer, sizeof (szHostBuffer) - 1) != ERROR_SUCCESS)
+ return (ERROR_INTERNET_INTERNAL_ERROR);
+ return (ResolveHostName(szHostBuffer, lpszIPAddress, lpdwIPAddressSize));
+}
+
+/////////////////////////////////////////////////////////////////////
+// IsInNet (a helper function)
+/////////////////////////////////////////////////////////////////////
+BOOL __stdcall IsInNet(LPSTR lpszIPAddress, LPSTR lpszDest, LPSTR lpszMask)
+{
+ DWORD dwDest;
+ DWORD dwIpAddr;
+ DWORD dwMask;
+
+ dwIpAddr = inet_addr(lpszIPAddress);
+ dwDest = inet_addr(lpszDest);
+ dwMask = inet_addr(lpszMask);
+
+ if ((dwDest == INADDR_NONE) ||
+ (dwIpAddr == INADDR_NONE) || ((dwIpAddr & dwMask) != dwDest))
+ return (FALSE);
+
+ return (TRUE);
+}
+
+static const AutoProxyHelperVtbl OurVtbl =
+{
+ IsResolvable,
+ GetIPAddress,
+ ResolveHostName,
+ IsInNet,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static AutoProxyHelperFunctions HelperFunctions = { &OurVtbl };
+*/
+
+static char *szProxyHost[3];
+static LIST<char> 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 = {0};
+ NETLIBHTTPREQUEST nlhr = {0};
+
+ nlu.handleType = NLH_USER;
+ nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS;
+ nlu.user.szSettingsModule = "(NULL)";
+ nlu.toLog = 1;
+
+ // initialize the netlib request
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT;
+ nlhr.szUrl = szUrl;
+
+ // download the page
+ NETLIBHTTPREQUEST *nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpTransaction((WPARAM)&nlu, (LPARAM)&nlhr);
+
+ if (nlhrReply)
+ {
+ if (nlhrReply->resultCode == 200)
+ {
+ buf.lpszScriptBuffer = nlhrReply->pData;
+ buf.dwScriptBufferSize = nlhrReply->dataLength + 1;
+
+ nlhrReply->dataLength = 0;
+ nlhrReply->pData = NULL;
+ }
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+ }
+}
+
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps)
+{
+ bool noHttp = false;
+ bool usingSsl = false;
+ char szUrl[256] = "";
+
+ if ((nlc->nloc.flags & (NLOCF_HTTP | NLOCF_HTTPGATEWAY) && nlc->nloc.flags & NLOCF_SSL) ||
+ nlc->nloc.wPort == 443 || forceHttps)
+ {
+ mir_snprintf(szUrl, sizeof(szUrl), "https://%s", nlc->nloc.szHost);
+ usingSsl = true;
+ }
+ else if (nlc->nloc.flags & (NLOCF_HTTPGATEWAY | NLOCF_HTTP) || nlc->usingHttpGateway)
+ mir_snprintf(szUrl, sizeof(szUrl), "http://%s", nlc->nloc.szHost);
+ else
+ {
+ mir_snprintf(szUrl, sizeof(szUrl), "%s", nlc->nloc.szHost);
+ noHttp = true;
+ }
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = NULL;
+ nlc->wProxyPort = 0;
+ nlc->proxyType = 0;
+
+ char *mt = NetlibGetIeProxy(szUrl);
+ char *m = NEWSTR_ALLOCA(mt);
+ mir_free(mt);
+
+ if (m == NULL) 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) { *h = 0; ++h; } else return false;
+
+ // find proxy port
+ 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://") == NULL && strstr(szAutoUrlStr, "://") != NULL)
+ {
+ abuf.dwStructSize = sizeof(abuf);
+ GetFile(szAutoUrlStr, abuf);
+ }
+ bAutoProxyInit = true;
+}
+
+struct IeProxyParam
+{
+ char *szUrl;
+ char *szHost;
+ char *szProxy;
+};
+
+static unsigned __stdcall NetlibIeProxyThread(void * arg)
+{
+ IeProxyParam *param = (IeProxyParam*)arg;
+ param->szProxy = NULL;
+
+ if (!bAutoProxyInit)
+ {
+ WaitForSingleObject(hIeProxyMutex, INFINITE);
+ NetlibInitAutoProxy();
+ ReleaseMutex(hIeProxyMutex);
+ }
+
+ BOOL res;
+ char *loc = strstr(szAutoUrlStr, "file://");
+ if (loc || strstr(szAutoUrlStr, "://") == NULL)
+ {
+ NetlibLogf(NULL, "Autoproxy Init file: %s", loc);
+ loc = loc ? loc + 7 : szAutoUrlStr;
+ res = pInternetInitializeAutoProxyDll(0, loc, NULL, NULL /*&HelperFunctions*/, NULL);
+ }
+ else
+ {
+ NetlibLogf(NULL, "Autoproxy Init %d", abuf.dwScriptBufferSize);
+ if (abuf.dwScriptBufferSize)
+ res = pInternetInitializeAutoProxyDll(0, NULL, NULL, NULL /*&HelperFunctions*/, &abuf);
+ else
+ res = false;
+ }
+
+ if (res)
+ {
+ char proxyBuffer[1024];
+ char *proxy = proxyBuffer;
+ DWORD dwProxyLen = sizeof(proxyBuffer);
+
+ if (pInternetGetProxyInfo(param->szUrl, (DWORD)strlen(param->szUrl),
+ param->szHost, (DWORD)strlen(param->szHost), &proxy, &dwProxyLen))
+ param->szProxy = mir_strdup(lrtrim(proxy));
+
+ NetlibLogf(NULL, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost);
+ pInternetDeInitializeAutoProxyDll(NULL, 0);
+ }
+ else
+ NetlibLogf(NULL, "Autoproxy init failed");
+
+ return 0;
+}
+
+char* NetlibGetIeProxy(char *szUrl)
+{
+ char *res = NULL;
+ char* p = strstr(szUrl, "://");
+ if (p) p += 3; else p = szUrl;
+
+ char *szHost = NEWSTR_ALLOCA(p);
+ p = strchr(szHost, '/'); if (p) *p = 0;
+ p = strchr(szHost, ':'); if (p) *p = 0;
+ _strlwr(szHost);
+
+ if (bEnabled)
+ {
+ for (int i = 0; i < proxyBypass.getCount(); ++i)
+ {
+ if (strcmp(proxyBypass[i], "<local>") == 0)
+ {
+ if (strchr(szHost, '.') == NULL) return NULL;
+ }
+ else if (wildcmp(szHost, proxyBypass[i])) return NULL;
+ }
+
+ 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 NULL;
+
+ size_t len = 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])
+ {
+ unsigned dwThreadId;
+ IeProxyParam param = { szUrl, szHost, NULL };
+ HANDLE hThread = (HANDLE)forkthreadex(NULL, 0, NetlibIeProxyThread, 0, ¶m, &dwThreadId);
+ WaitForSingleObject(hThread, INFINITE);
+ CloseHandle(hThread);
+ 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", NULL, NULL, (BYTE*)&enabled, &tValueLen);
+ bEnabled = enabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = SIZEOF(szHostStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyServer", NULL, NULL, (BYTE*)szHostStr, &tValueLen);
+ bEnabled = bEnabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = SIZEOF(szAutoUrlStr);
+ tResult = RegQueryValueExA(hSettings, "AutoConfigUrl", NULL, NULL, (BYTE*)szAutoUrlStr, &tValueLen);
+
+ tValueLen = SIZEOF(szProxyBypassStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyOverride", NULL, NULL, (BYTE*)szProxyBypassStr, &tValueLen);
+
+ RegCloseKey(hSettings);
+
+ if (bEnabled)
+ {
+ char* szProxy = ltrim(szHostStr);
+ if (szProxy[0] == 0) { enabled = false; return; }
+
+ for (;;)
+ {
+ 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 = 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 == NULL) break;
+ szProxy = szProxyEnd + 1;
+ }
+
+ char* szProxyBypass = szProxyBypassStr;
+ for(;;)
+ {
+ char *szProxyBypassEnd = strchr(szProxyBypass, ';');
+ if (szProxyBypassEnd) *szProxyBypassEnd = 0;
+
+ lrtrim(szProxyBypass);
+
+ proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass)));
+ if (szProxyBypassEnd == NULL) break;
+
+ szProxyBypass = szProxyBypassEnd + 1;
+ }
+ }
+
+ if (bEnabled || szAutoUrlStr[0])
+ hIeProxyMutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+void NetlibUnloadIeProxy(void)
+{
+ int i;
+
+ for (i = 0; i < 3; ++i)
+ mir_free(szProxyHost[i]);
+
+ for (i = 0; i < proxyBypass.getCount(); ++i)
+ mir_free(proxyBypass[i]);
+
+ proxyBypass.destroy();
+ mir_free(abuf.lpszScriptBuffer);
+
+ CloseHandle(hIeProxyMutex);
+}
diff --git a/src/modules/netlib/netlibbind.cpp b/src/modules/netlib/netlibbind.cpp new file mode 100644 index 0000000000..6c0c4c19e9 --- /dev/null +++ b/src/modules/netlib/netlibbind.cpp @@ -0,0 +1,287 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+bool BindSocketToPort(const char *szPorts, SOCKET s, int* portn)
+{
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ EnterCriticalSection(&csNetlibUser);
+
+ if (--*portn < 0 && s != INVALID_SOCKET)
+ {
+ BindSocketToPort(szPorts, INVALID_SOCKET, portn);
+ if (*portn == 0)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ return false;
+ }
+ WORD num;
+ CallService(MS_UTILS_GETRANDOM, sizeof(WORD), (LPARAM)&num);
+ *portn = num % *portn;
+ }
+
+ bool before=false;
+ for (;;)
+ {
+ 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)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ return false;
+ }
+
+ sin.sin_port = htons((WORD)port);
+ if (bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ *portn = portnum + 1;
+ return true;
+ }
+ }
+ }
+ psz = pszEnd;
+ }
+ if (*portn < 0)
+ {
+ *portn = portnum;
+ LeaveCriticalSection(&csNetlibUser);
+ return true;
+ }
+ else if (*portn >= portnum)
+ *portn = 0;
+ else
+ before = true;
+ }
+}
+
+
+int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp)
+{
+ closesocket(nlbp->s);
+ WaitForSingleObject(nlbp->hThread,INFINITE);
+ CloseHandle(nlbp->hThread);
+ NetlibLogf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort);
+ mir_free(nlbp);
+ return 1;
+}
+
+static unsigned __stdcall NetlibBindAcceptThread(void* param)
+{
+ SOCKET s;
+ SOCKADDR_IN sin;
+ int sinLen;
+ struct NetlibConnection *nlc;
+ struct NetlibBoundPort *nlbp = (NetlibBoundPort*)param;
+
+ NetlibLogf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort);
+ for(;;)
+ {
+ sinLen = sizeof(sin);
+ s = accept(nlbp->s, (struct sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) break;
+ NetlibLogf(nlbp->nlu, "New incoming connection on port %u from %s (%d)", nlbp->wPort, inet_ntoa(sin.sin_addr), s);
+ nlc = (NetlibConnection*)mir_calloc(sizeof(NetlibConnection));
+ nlc->handleType = NLH_CONNECTION;
+ nlc->nlu = nlbp->nlu;
+ nlc->s = s;
+ nlc->s2 = INVALID_SOCKET;
+ InitializeCriticalSection(&nlc->csHttpSequenceNums);
+ nlc->hOkToCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ nlc->dontCloseNow = 0;
+ NetlibInitializeNestedCS(&nlc->ncsSend);
+ NetlibInitializeNestedCS(&nlc->ncsRecv);
+ nlbp->pfnNewConnectionV2((HANDLE)nlc,ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra);
+ }
+ NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP");
+ return 0;
+}
+
+INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBBIND *nlb = (NETLIBBIND*)lParam;
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibBoundPort *nlbp;
+ SOCKADDR_IN sin;
+ int foundPort = 0;
+ UINT dwThreadId;
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) ||
+ nlb == NULL || nlb->pfnNewConnection == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if (nlb->cbSize != sizeof(NETLIBBIND) &&
+ nlb->cbSize != NETLIBBIND_SIZEOF_V2 &&
+ nlb->cbSize != NETLIBBIND_SIZEOF_V1)
+ {
+ return 0;
+ }
+ nlbp = (NetlibBoundPort*)mir_calloc(sizeof(NetlibBoundPort));
+ nlbp->handleType = NLH_BOUNDPORT;
+ nlbp->nlu = nlu;
+ nlbp->pfnNewConnectionV2 = nlb->pfnNewConnectionV2;
+ nlbp->s = socket(AF_INET, SOCK_STREAM, 0);
+ nlbp->pExtra = (nlb->cbSize != NETLIBBIND_SIZEOF_V1) ? nlb->pExtra : NULL;
+ if (nlbp->s == INVALID_SOCKET)
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"socket",WSAGetLastError());
+ mir_free(nlbp);
+ return 0;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = 0;
+
+ /* if the netlib user wanted a free port given in the range, then
+ they better have given wPort==0, let's hope so */
+ if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0)
+ {
+ if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, &nlu->outportnum))
+ {
+ NetlibLogf(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)
+ {
+ NetlibLogf(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);
+ }
+ if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0)
+ foundPort = 1;
+ }
+ if (!foundPort)
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"bind",WSAGetLastError());
+ closesocket(nlbp->s);
+ mir_free(nlbp);
+ return 0;
+ }
+
+ if (listen(nlbp->s, 5))
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"listen",WSAGetLastError());
+ closesocket(nlbp->s);
+ mir_free(nlbp);
+ return 0;
+ }
+
+ { int len;
+ DWORD extIP;
+
+ ZeroMemory(&sin,sizeof(sin));
+ len = sizeof(sin);
+ if (getsockname(nlbp->s,(SOCKADDR *)&sin,&len))
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"getsockname",WSAGetLastError());
+ closesocket(nlbp->s);
+ mir_free(nlbp);
+ return 0;
+ }
+ nlb->wPort = ntohs(sin.sin_port);
+ nlbp->wPort = nlb->wPort;
+ nlb->dwInternalIP = ntohl(sin.sin_addr.S_un.S_addr);
+
+ if (nlb->dwInternalIP == 0)
+ {
+ char hostname[64];
+ struct hostent *he;
+
+ gethostname(hostname, SIZEOF(hostname));
+ he = gethostbyname(hostname);
+ if (he && he->h_addr_list[0])
+ nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr_list[0]);
+ }
+ if (nlu->settings.enableUPnP &&
+ NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, nlb->cbSize > NETLIBBIND_SIZEOF_V2))
+ {
+ NetlibLogf(NULL, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n",
+ nlb->wPort, nlbp->wExPort);
+ if (nlb->cbSize > NETLIBBIND_SIZEOF_V2)
+ {
+ nlb->wExPort = nlbp->wExPort;
+ nlb->dwExternalIP = extIP;
+ }
+ }
+ else
+ {
+ if (nlu->settings.enableUPnP)
+ NetlibLogf(NULL, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort);
+ else
+ NetlibLogf(NULL, "UPnP disabled. Internal Port: %u\n", nlb->wPort);
+
+ nlbp->wExPort = 0;
+ if (nlb->cbSize > NETLIBBIND_SIZEOF_V2)
+ {
+ nlb->wExPort = nlb->wPort;
+ nlb->dwExternalIP = nlb->dwInternalIP;
+ }
+ }
+ }
+ nlbp->hThread = (HANDLE)forkthreadex(NULL, 0, NetlibBindAcceptThread, 0, nlbp, &dwThreadId);
+ return (INT_PTR)nlbp;
+}
diff --git a/src/modules/netlib/netlibhttp.cpp b/src/modules/netlib/netlibhttp.cpp new file mode 100644 index 0000000000..970a28cb10 --- /dev/null +++ b/src/modules/netlib/netlibhttp.cpp @@ -0,0 +1,1331 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "../plugins/zlib/zlib.h"
+#include "netlib.h"
+
+#define HTTPRECVHEADERSTIMEOUT 30000 //in ms
+#define HTTPRECVDATATIMEOUT 20000
+
+struct ResizableCharBuffer
+{
+ char *sz;
+ int iEnd, cbAlloced;
+};
+
+struct ProxyAuth
+{
+ char *szServer;
+ char *szMethod;
+// char *szUserName;
+// char *szPassword;
+
+ 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 lstrcmpiA(p1->szServer, p2->szServer); }
+};
+
+struct ProxyAuthList : OBJLIST<ProxyAuth>
+{
+ ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {}
+
+ void add(const char *szServer, const char *szMethod)
+ {
+ if (szServer == NULL) return;
+ int i = getIndex((ProxyAuth*)&szServer);
+ if (i >= 0)
+ {
+ ProxyAuth &rec = (*this)[i];
+ if (szMethod == NULL)
+ 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<ProxyAuth>::find((ProxyAuth*)&szServer) : NULL;
+ return rec ? rec->szMethod : NULL;
+ }
+};
+
+ProxyAuthList proxyAuthList;
+
+static void AppendToCharBuffer(struct ResizableCharBuffer *rcb, const char *fmt, ...)
+{
+ va_list va;
+ int charsDone;
+
+ if (rcb->cbAlloced == 0)
+ {
+ rcb->cbAlloced = 512;
+ rcb->sz = (char*)mir_alloc(rcb->cbAlloced);
+ }
+ va_start(va, fmt);
+ for (;;)
+ {
+ charsDone = mir_vsnprintf(rcb->sz + rcb->iEnd, rcb->cbAlloced-rcb->iEnd, fmt, va);
+ if(charsDone >= 0) break;
+ rcb->cbAlloced += 512;
+ rcb->sz = (char*)mir_realloc(rcb->sz, rcb->cbAlloced);
+ }
+ va_end(va);
+ rcb->iEnd += charsDone;
+}
+
+static int RecvWithTimeoutTime(struct NetlibConnection *nlc, unsigned dwTimeoutTime, char *buf, int len, int flags)
+{
+ DWORD dwTimeNow;
+
+ if (!si.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 NLRecv(nlc, buf, len, flags);
+ }
+
+ if (nlc->termRequested || Miranda_Terminated()) return 0;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ return NLRecv(nlc, buf, len, flags);
+}
+
+static char* NetlibHttpFindHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr)
+{
+ for (int i = 0; i < nlhrReply->headersCount; i++)
+ {
+ if (_stricmp(nlhrReply->headers[i].szName, hdr) == 0)
+ {
+ return nlhrReply->headers[i].szValue;
+ }
+ }
+ return NULL;
+}
+
+static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider)
+{
+ char *szBasicHdr = NULL;
+ char *szNegoHdr = NULL;
+ char *szNtlmHdr = NULL;
+
+ for (int i = 0; i < nlhrReply->headersCount; i++)
+ {
+ if (_stricmp(nlhrReply->headers[i].szName, hdr) == 0)
+ {
+ if (_strnicmp(nlhrReply->headers[i].szValue, "Negotiate", 9) == 0)
+ szNegoHdr = nlhrReply->headers[i].szValue;
+ else if (_strnicmp(nlhrReply->headers[i].szValue, "NTLM", 4) == 0)
+ szNtlmHdr = nlhrReply->headers[i].szValue;
+ else if (_strnicmp(nlhrReply->headers[i].szValue, "Basic", 5) == 0)
+ szBasicHdr = nlhrReply->headers[i].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 NULL;
+}
+
+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.cbSize = sizeof(nloc);
+ nloc.szHost = szHost;
+
+ char* pcolon = strrchr(szHost, ':');
+ if (pcolon)
+ {
+ *pcolon = '\0';
+ nloc.wPort = (WORD)strtol(pcolon+1, NULL, 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 = NULL)
+{
+ NETLIBOPENCONNECTION nloc;
+
+ if (szUrl == NULL)
+ 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 != NULL)
+ {
+ bool httpProxy = !(nloc.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP;
+ bool sameHost = lstrcmpA(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort;
+
+ if (!httpProxy && !sameHost)
+ {
+ NetlibDoClose(nlc);
+
+ mir_free((char*)nlc->nloc.szHost);
+ nlc->nloc = nloc;
+ return NetlibDoConnect(nlc) ? nlc : NULL;
+ }
+ }
+ else
+ nlc = (NetlibConnection*)NetlibOpenConnection((WPARAM)nlu, (LPARAM)&nloc);
+
+ mir_free((char*)nloc.szHost);
+
+ return nlc;
+}
+
+struct HttpSecurityContext
+{
+ HANDLE m_hNtlmSecurity;
+ char *m_szHost;
+ char *m_szProvider;
+
+ HttpSecurityContext()
+ { m_hNtlmSecurity = NULL; m_szHost = NULL; m_szProvider = NULL; }
+
+ ~HttpSecurityContext() { Destroy(); }
+
+ void Destroy(void)
+ {
+ if (!m_hNtlmSecurity) return;
+
+ NetlibDestroySecurityProvider(m_hNtlmSecurity);
+ m_hNtlmSecurity = NULL;
+ mir_free(m_szHost); m_szHost = NULL;
+ mir_free(m_szProvider); m_szProvider = NULL;
+ }
+
+ 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 = NULL;
+ bool justCreated = false;
+
+ 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 == NULL)
+ {
+ char szSpnStr[256] = "";
+ if (szHost && _stricmp(szProvider, "Basic"))
+ {
+ unsigned long ip = inet_addr(szHost);
+ PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET);
+ mir_snprintf(szSpnStr, SIZEOF(szSpnStr), "HTTP/%s", host && host->h_name ? host->h_name : szHost);
+ _strlwr(szSpnStr + 5);
+ NetlibLogf(nlc->nlu, "Host SPN: %s", szSpnStr);
+ }
+ m_hNtlmSecurity = NetlibInitSecurityProvider(szProvider, szSpnStr[0] ? szSpnStr : NULL);
+ if (m_hNtlmSecurity)
+ {
+ m_szProvider = mir_strdup(szProvider);
+ m_szHost = mir_strdup(szHost);
+ justCreated = true;
+ }
+ }
+
+ if (m_hNtlmSecurity)
+ {
+ TCHAR *szLogin = NULL, *szPassw = NULL;
+
+ if (nlc->nlu->settings.useProxyAuth)
+ {
+ EnterCriticalSection(&csNetlibUser);
+ szLogin = mir_a2t(nlc->nlu->settings.szProxyAuthUser);
+ szPassw = mir_a2t(nlc->nlu->settings.szProxyAuthPassword);
+ LeaveCriticalSection(&csNetlibUser);
+ }
+
+ szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity,
+ szChallenge, szLogin, szPassw, true, complete);
+
+ if (!szAuthHdr)
+ {
+ NetlibLogf(NULL, "Security login %s failed, user: " TCHAR_STR_PARAM " pssw: " TCHAR_STR_PARAM,
+ szProvider, szLogin ? szLogin : _T("(no user)"), szPassw ? _T("(exist)") : _T("(no psw)"));
+ }
+ else if (justCreated)
+ proxyAuthList.add(m_szHost, m_szProvider);
+
+ mir_free(szLogin);
+ mir_free(szPassw);
+ }
+ 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];
+ char *peol;
+
+ for(;;)
+ {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, SIZEOF(buffer) - 1,
+ MSG_PEEK | recvFlags);
+
+ if (bytesPeeked == 0)
+ {
+ SetLastError(ERROR_HANDLE_EOF);
+ return 0;
+ }
+ if (bytesPeeked == SOCKET_ERROR)
+ return 0;
+
+ buffer[bytesPeeked] = '\0';
+ peol = strchr(buffer, '\n');
+ if (peol == NULL)
+ {
+ if ((int)strlen(buffer) < bytesPeeked)
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+ if (bytesPeeked == SIZEOF(buffer) - 1)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ if (Miranda_Terminated()) return 0;
+ Sleep(10);
+ }
+ else
+ break;
+ }
+
+ 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(struct NetlibConnection *nlc,struct ResizableCharBuffer *httpRequest,NETLIBHTTPREQUEST *nlhr,int sendContentLengthHeader)
+{
+ bool sendData = (nlhr->requestType==REQUEST_POST || nlhr->requestType==REQUEST_PUT);
+
+ if(sendContentLengthHeader && sendData)
+ AppendToCharBuffer(httpRequest,"Content-Length: %d\r\n\r\n", nlhr->dataLength);
+ else
+ AppendToCharBuffer(httpRequest,"\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 = NLSend(nlc, httpRequest->sz, httpRequest->iEnd, hflags);
+ if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength)
+ {
+ DWORD sflags = (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 = NLSend(nlc,nlhr->pData,nlhr->dataLength, sflags);
+
+ bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR;
+ }
+ mir_free(httpRequest->sz);
+ memset(httpRequest, 0, sizeof(*httpRequest));
+
+ return bytesSent;
+}
+
+INT_PTR NetlibHttpSendRequest(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam;
+ NETLIBHTTPREQUEST *nlhrReply = NULL;
+ HttpSecurityContext httpSecurity;
+
+ struct ResizableCharBuffer httpRequest={0};
+ const char *pszRequest, *pszUrl, *pszFullUrl;
+ char *szHost = NULL, *szNewUrl = NULL;
+ char *pszProxyAuthHdr = NULL, *pszAuthHdr = NULL;
+ int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader;
+ int bytesSent;
+ bool lastFirstLineFail = false;
+
+ if (nlhr == NULL || nlhr->cbSize < NETLIBHTTPREQUEST_V1_SIZE || nlhr->szUrl == NULL || nlhr->szUrl[0] == '\0')
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int hdrTimeout = nlhr->cbSize > NETLIBHTTPREQUEST_V1_SIZE && nlhr->timeout ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT;
+
+ 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;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if (!nlc->usingHttpGateway)
+ {
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+ }
+
+ pszFullUrl = nlhr->szUrl;
+ pszUrl = NULL;
+
+ unsigned complete = false;
+ int count = 11;
+ while (--count)
+ {
+ if (!NetlibReconnect(nlc))
+ {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!pszUrl)
+ {
+ pszUrl = pszFullUrl;
+ if (nlhr->flags & (NLHRF_SMARTREMOVEHOST | NLHRF_REMOVEHOST | NLHRF_GENERATEHOST))
+ {
+ bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL);
+
+ mir_free(szHost);
+ szHost = NULL;
+
+ const char *ppath, *phost;
+ phost = strstr(pszUrl, "://");
+ if (phost == NULL) phost = pszUrl;
+ else phost += 3;
+ ppath = strchr(phost, '/');
+ if (ppath == phost) phost = NULL;
+
+ if (nlhr->flags & NLHRF_GENERATEHOST)
+ {
+ szHost = mir_strdup(phost);
+ if (ppath && phost) szHost[ppath - phost] = 0;
+ }
+
+ if (nlhr->flags & NLHRF_REMOVEHOST || (nlhr->flags & NLHRF_SMARTREMOVEHOST && !usingProxy))
+ {
+ pszUrl = ppath ? ppath : "/";
+ }
+
+ if (usingProxy && phost && !nlc->dnsThroughProxy)
+ {
+ char* tszHost = mir_strdup(phost);
+ if (ppath && phost) tszHost[ppath - phost] = 0;
+ char* cln = strchr(tszHost, ':'); if (cln) *cln = 0;
+
+ if (inet_addr(tszHost) == INADDR_NONE)
+ {
+ DWORD ip = DnsLookup(nlc->nlu, tszHost);
+ if (ip && szHost)
+ {
+ mir_free(szHost);
+ szHost = (char*)mir_alloc(30);
+ if (cln) *cln = ':';
+ mir_snprintf(szHost, 30, "%s%s", inet_ntoa(*(PIN_ADDR)&ip), cln ? cln : "");
+ }
+/*
+ if (ip && pszUrl[0] != '/')
+ {
+ mir_free(szNewUrl);
+ szNewUrl = (char*)mir_alloc(strlen(pszUrl) + 60);
+ szNewUrl[0] = 0;
+
+ phost = strstr(pszUrl, "://");
+ if (phost)
+ {
+ phost += 3;
+ size_t len = phost - pszUrl;
+ memcpy(szNewUrl, pszUrl, len);
+ szNewUrl[len] = 0;
+ }
+ strcat(szNewUrl, inet_ntoa(*(PIN_ADDR)&ip));
+ ppath = strchr(phost, '/');
+ if (ppath) strcat(szNewUrl, ppath);
+ pszUrl = szNewUrl;
+ }
+*/
+ }
+ mir_free(tszHost);
+ }
+ }
+ }
+
+ if (nlc->proxyAuthNeeded && proxyAuthList.getCount())
+ {
+ if (httpSecurity.m_szProvider == NULL && 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;
+
+ AppendToCharBuffer(&httpRequest, "%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++)
+ {
+ if (!lstrcmpiA(nlhr->headers[i].szName, "Host")) doneHostHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Content-Length")) doneContentLengthHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Proxy-Authorization")) doneProxyAuthHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Authorization")) doneAuthHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Connection")) continue;
+ if (nlhr->headers[i].szValue == NULL) continue;
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", nlhr->headers[i].szName, nlhr->headers[i].szValue);
+ }
+ if (szHost && !doneHostHeader)
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Host", szHost);
+ if (pszProxyAuthHdr && !doneProxyAuthHeader)
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr);
+ if (pszAuthHdr && !doneAuthHeader)
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Authorization", pszAuthHdr);
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Connection", "Keep-Alive");
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Proxy-Connection", "Keep-Alive");
+
+ // Add Sticky Headers
+ if (nlc->nlu->szStickyHeaders != NULL)
+ AppendToCharBuffer(&httpRequest, "%s\r\n", nlc->nlu->szStickyHeaders);
+
+ //send it
+ bytesSent = SendHttpRequestAndData(nlc, &httpRequest, nlhr, !doneContentLengthHeader);
+ if (bytesSent == SOCKET_ERROR) break;
+
+ //ntlm reply
+ if (!doneContentLengthHeader || nlhr->requestType == REQUEST_HEAD)
+ {
+ int resultCode = 0;
+
+ 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, &resultCode, NULL, NULL))
+ {
+ NetlibLogf(nlc->nlu, "%s %d: %s Failed (%u %u)",__FILE__,__LINE__,"HttpPeekFirstResponseLine",GetLastError(), count);
+ DWORD err = GetLastError();
+ if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW ||
+ lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT)
+ {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ else
+ {
+ lastFirstLineFail = true;
+ continue;
+ }
+ }
+ 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*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ }
+ else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) // redirect
+ && (nlhr->flags & NLHRF_REDIRECT)))
+ {
+ pszUrl = NULL;
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)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 : strlen(pszFullUrl);
+ }
+
+ nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + strlen(tmpUrl) * 3 + 1);
+
+ strncpy(nlc->szNewUrl, pszFullUrl, rlen);
+ strcpy(nlc->szNewUrl + rlen, tmpUrl);
+ pszFullUrl = nlc->szNewUrl;
+ pszUrl = NULL;
+
+ if (NetlibHttpProcessUrl(nlhr, nlc->nlu, nlc, pszFullUrl) == NULL)
+ {
+ 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*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszAuthHdr); pszAuthHdr = NULL;
+ if (nlhrReply)
+ {
+ char *szAuthStr = NULL;
+ 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") : NULL;
+ }
+
+ if (szAuthStr)
+ {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszAuthHdr == NULL)
+ {
+ proxyAuthList.add(szHost, NULL);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 407 && !doneProxyAuthHeader) //proxy auth required
+ {
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = NULL;
+ if (nlhrReply)
+ {
+ char *szAuthStr = NULL;
+ 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") : NULL;
+ }
+
+ 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 == NULL)
+ {
+ proxyAuthList.add(nlc->szProxyServer, NULL);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else
+ break;
+
+ if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader)
+ {
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = NULL;
+ }
+ if (pszAuthHdr && resultCode != 401 && !doneAuthHeader)
+ {
+ mir_free(pszAuthHdr); pszAuthHdr = NULL;
+ }
+
+ if (nlhrReply)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ nlhrReply = NULL;
+ }
+ }
+ else
+ break;
+ }
+ if (count == 0) bytesSent = SOCKET_ERROR;
+ if (nlhrReply) NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ //clean up
+ mir_free(pszProxyAuthHdr);
+ mir_free(pszAuthHdr);
+ mir_free(szHost);
+ mir_free(szNewUrl);
+
+ if (!nlc->usingHttpGateway)
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ return bytesSent;
+}
+
+INT_PTR NetlibHttpFreeRequestStruct(WPARAM, LPARAM lParam)
+{
+ NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam;
+
+ if (nlhr == NULL || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if(nlhr->headers)
+ {
+ int i;
+ for(i=0; i<nlhr->headersCount; i++)
+ {
+ mir_free(nlhr->headers[i].szName);
+ mir_free(nlhr->headers[i].szValue);
+ }
+ mir_free(nlhr->headers);
+ }
+ mir_free(nlhr->pData);
+ mir_free(nlhr->szResultDescr);
+ mir_free(nlhr->szUrl);
+ mir_free(nlhr);
+ return 1;
+}
+
+INT_PTR NetlibHttpRecvHeaders(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc = (struct NetlibConnection*)wParam;
+ NETLIBHTTPREQUEST *nlhr;
+ char *peol, *pbuffer;
+ char *buffer = NULL;
+ DWORD dwRequestTimeoutTime;
+ int bytesPeeked, firstLineLength = 0;
+ int headersCount = 0, bufferSize = 8192;
+ bool headersCompleted = false;
+
+ if(!NetlibEnterNestedCS(nlc,NLNCS_RECV))
+ return 0;
+
+ dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT;
+ 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;
+
+ if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, lParam | MSG_PEEK,
+ &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength))
+ {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr);
+ return 0;
+ }
+
+ buffer = (char*)mir_alloc(bufferSize + 1);
+ bytesPeeked = NLRecv(nlc, buffer, min(firstLineLength, bufferSize), lParam | MSG_DUMPASTEXT);
+ if (bytesPeeked != firstLineLength)
+ {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr);
+ if (bytesPeeked != SOCKET_ERROR) SetLastError(ERROR_HANDLE_EOF);
+ mir_free(buffer);
+ return 0;
+ }
+
+ // Make sure all headers arrived
+ bytesPeeked = 0;
+ while (!headersCompleted)
+ {
+ if (bytesPeeked >= bufferSize)
+ {
+ bufferSize += 8192;
+ mir_free(buffer);
+ if (bufferSize > 32 * 1024)
+ {
+ bytesPeeked = 0;
+ break;
+ }
+ buffer = (char*)mir_alloc(bufferSize + 1);
+ }
+
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, bufferSize,
+ MSG_PEEK | MSG_NODUMP | lParam);
+ if (bytesPeeked == 0) break;
+
+ if (bytesPeeked == SOCKET_ERROR)
+ {
+ bytesPeeked = 0;
+ break;
+ }
+ buffer[bytesPeeked] = 0;
+
+ for (pbuffer = buffer, headersCount = 0; ; pbuffer = peol + 1, ++headersCount)
+ {
+ peol = strchr(pbuffer, '\n');
+ if (peol == NULL) break;
+ if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r'))
+ {
+ bytesPeeked = peol - buffer + 1;
+ headersCompleted = true;
+ break;
+ }
+ }
+ }
+
+ // Recieve headers
+ if (bytesPeeked > 0)
+ bytesPeeked = NLRecv(nlc, buffer, bytesPeeked, lParam | MSG_DUMPASTEXT);
+ if (bytesPeeked <= 0)
+ {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr);
+ mir_free(buffer);
+ return 0;
+ }
+ buffer[bytesPeeked] = 0;
+
+ nlhr->headersCount = headersCount;
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount);
+
+ for (pbuffer = buffer, headersCount = 0; ; pbuffer = peol + 1, ++headersCount)
+ {
+ peol = strchr(pbuffer, '\n');
+ if (peol == NULL || peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) break;
+ *peol = 0;
+
+ char *pColon = strchr(pbuffer, ':');
+ if (pColon == NULL)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr); nlhr = NULL;
+ SetLastError(ERROR_INVALID_DATA);
+ break;
+ }
+
+ *(pColon++) = 0;
+ nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer));
+ nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon));
+ }
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ mir_free(buffer);
+ return (INT_PTR)nlhr;
+}
+
+INT_PTR NetlibHttpTransaction(WPARAM wParam, LPARAM lParam)
+{
+ NetlibUser *nlu = (NetlibUser*)wParam;
+ NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)lParam, *nlhrReply;
+ DWORD dflags, hflags;
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) ||
+ nlhr == NULL || nlhr->cbSize < NETLIBHTTPREQUEST_V1_SIZE ||
+ nlhr->szUrl == NULL || nlhr->szUrl[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ NetlibConnection* nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc);
+ if (nlc == NULL) return 0;
+
+ {
+ NETLIBHTTPREQUEST nlhrSend;
+ char szUserAgent[64];
+
+ nlhrSend = *nlhr;
+ nlhrSend.flags &= ~NLHRF_REMOVEHOST;
+ nlhrSend.flags |= NLHRF_GENERATEHOST | NLHRF_SMARTREMOVEHOST | NLHRF_SMARTAUTHHEADER;
+
+ bool doneUserAgentHeader = NetlibHttpFindHeader(nlhr, "User-Agent") != NULL;
+ bool doneAcceptEncoding = NetlibHttpFindHeader(nlhr, "Accept-Encoding") != NULL;
+
+ if (!doneUserAgentHeader || !doneAcceptEncoding)
+ {
+ nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2));
+ memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount);
+ }
+ if (!doneUserAgentHeader)
+ {
+ char *pspace,szMirandaVer[64];
+
+ nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent;
+ ++nlhrSend.headersCount;
+ CallService(MS_SYSTEM_GETVERSIONTEXT,SIZEOF(szMirandaVer),(LPARAM)szMirandaVer);
+ pspace=strchr(szMirandaVer,' ');
+ if(pspace)
+ {
+ *pspace++ = '\0';
+ mir_snprintf(szUserAgent, SIZEOF(szUserAgent), "Miranda/%s (%s)", szMirandaVer, pspace);
+ }
+ else
+ mir_snprintf(szUserAgent, SIZEOF(szUserAgent), "Miranda/%s", szMirandaVer);
+ }
+ if (!doneAcceptEncoding)
+ {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip";
+ ++nlhrSend.headersCount;
+ }
+ if (NetlibHttpSendRequest((WPARAM)nlc, (LPARAM)&nlhrSend) == SOCKET_ERROR)
+ {
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ NetlibCloseHandle((WPARAM)nlc, 0);
+ return 0;
+ }
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ }
+
+ 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);
+
+ hflags =
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, 0);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply)
+ {
+ nlhrReply->szUrl = nlc->szNewUrl;
+ nlc->szNewUrl = NULL;
+ }
+
+ if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == NULL)
+ {
+ NetlibCloseHandle((WPARAM)nlc, 0);
+ if (nlhrReply) nlhrReply->nlc = NULL;
+ }
+ else
+ nlhrReply->nlc = nlc;
+
+ return (INT_PTR)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 NULL;
+
+ int gzip_len = *len_ptr * 5;
+ char* output_data = NULL;
+
+ 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 = NULL;
+ }
+ else
+ output_data[gzip_len] = 0;
+
+ *len_ptr = gzip_len;
+ return output_data;
+}
+
+static int NetlibHttpRecvChunkHeader(NetlibConnection* nlc, bool first, DWORD flags)
+{
+ char data[64], *peol1;
+
+ for (;;)
+ {
+ int recvResult = NLRecv(nlc, data, 31, MSG_RAW | MSG_PEEK);
+ if (recvResult <= 0) return SOCKET_ERROR;
+
+ data[recvResult] = 0;
+
+ peol1 = strchr(data, '\n');
+ if (peol1 != NULL)
+ {
+ char *peol2 = first ? peol1 : strchr(peol1 + 1, '\n');
+ if (peol2 != NULL)
+ {
+ int sz = peol2 - data + 1;
+ int r = strtol(first ? data : peol1 + 1, NULL, 16);
+ if (r == 0)
+ {
+ char *peol3 = strchr(peol2 + 1, '\n');
+ if (peol3 == NULL) continue;
+ sz = peol3 - data + 1;
+ }
+ NLRecv(nlc, data, sz, MSG_RAW | flags);
+ return r;
+ }
+ else
+ if (recvResult >= 31) return SOCKET_ERROR;
+ }
+ }
+}
+
+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 = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ if (nlhrReply == NULL)
+ return NULL;
+
+ if (nlhrReply->resultCode == 100)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ goto next;
+ }
+
+ for (i=0; i<nlhrReply->headersCount; i++)
+ {
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Content-Length"))
+ dataLen = atoi(nlhrReply->headers[i].szValue);
+
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Content-Encoding"))
+ {
+ cenc = i;
+ if (strstr(nlhrReply->headers[i].szValue, "gzip"))
+ cenctype = 1;
+ else if (strstr(nlhrReply->headers[i].szValue, "deflate"))
+ cenctype = 2;
+ }
+
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Connection"))
+ close = !lstrcmpiA(nlhrReply->headers[i].szValue, "close");
+
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Transfer-Encoding") &&
+ !lstrcmpiA(nlhrReply->headers[i].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);
+ if (chunksz == SOCKET_ERROR)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return NULL;
+ }
+ dataLen = chunksz;
+ }
+ dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+
+ while (chunksz != 0)
+ {
+ for(;;)
+ {
+ 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)
+ {
+ NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply);
+ return NULL;
+ }
+ 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 == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return NULL;
+ }
+ }
+ }
+ Sleep(10);
+ }
+
+ if (chunked)
+ {
+ chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags);
+ if (chunksz == SOCKET_ERROR)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return NULL;
+ }
+ dataLen += chunksz;
+ dataBufferAlloced += chunksz;
+
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ }
+ else
+ break;
+ }
+
+ nlhrReply->pData[nlhrReply->dataLength] = '\0';
+ }
+
+ if (chunked)
+ {
+ nlhrReply->headers[chunkhdr].szName = ( char* )mir_realloc(nlhrReply->headers[chunkhdr].szName, 16);
+ lstrcpyA(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 = NULL;
+
+ 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);
+ 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 = NULL;
+ nlhrReply->dataLength = 0;
+ }
+ }
+
+ if (close &&
+ (nlc->proxyType != PROXYTYPE_HTTP || nlc->nloc.flags & NLOCF_SSL) &&
+ (!isConnect || nlhrReply->resultCode != 200))
+ NetlibDoClose(nlc);
+
+ return nlhrReply;
+}
diff --git a/src/modules/netlib/netlibhttpproxy.cpp b/src/modules/netlib/netlibhttpproxy.cpp new file mode 100644 index 0000000000..153d53660f --- /dev/null +++ b/src/modules/netlib/netlibhttpproxy.cpp @@ -0,0 +1,522 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+typedef enum
+{
+ reqHelloGet,
+ reqOldGet,
+ reqOldPost,
+ reqNewPost,
+}
+RequestType;
+
+
+static int HttpGatewayReadSetResult(NetlibConnection *nlc, char *buf, int num, int peek)
+{
+ if (nlc->dataBufferLen == 0) return 0;
+
+ int bytes = min(num, nlc->dataBufferLen);
+ int rbytes = nlc->dataBufferLen - bytes;
+
+ memcpy(buf, nlc->dataBuffer, bytes);
+ if (!peek)
+ {
+ memmove(nlc->dataBuffer, nlc->dataBuffer + bytes, rbytes);
+ nlc->dataBufferLen = rbytes;
+ }
+
+ return bytes;
+}
+
+void HttpGatewayRemovePacket(NetlibConnection *nlc, int pck)
+{
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+ while (pck-- && nlc->pHttpProxyPacketQueue != NULL)
+ {
+ NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue;
+ nlc->pHttpProxyPacketQueue = nlc->pHttpProxyPacketQueue->next;
+
+ mir_free(p->dataBuffer);
+ mir_free(p);
+ }
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+}
+
+
+static bool NetlibHttpGatewaySend(struct NetlibConnection *nlc, RequestType reqType, const char *buf, int len)
+{
+ NETLIBHTTPREQUEST nlhrSend = {0};
+ char szUrl[512];
+
+ nlhrSend.cbSize = sizeof(nlhrSend);
+ nlhrSend.nlc = nlc;
+
+ nlhrSend.pData = (char*)buf;
+ nlhrSend.dataLength = len;
+
+ nlhrSend.flags = NLHRF_GENERATEHOST | NLHRF_DUMPPROXY | NLHRF_SMARTAUTHHEADER | NLHRF_NOPROXY | NLHRF_REDIRECT;
+ if (nlc->nlhpi.flags & NLHPIF_HTTP11) nlhrSend.flags |= NLHRF_HTTP11;
+
+ switch (reqType)
+ {
+ case reqHelloGet:
+ nlhrSend.requestType = REQUEST_GET;
+ nlhrSend.szUrl=nlc->nlu->user.szHttpGatewayHello;
+ break;
+
+ case reqOldGet:
+ nlhrSend.requestType = REQUEST_GET;
+ nlhrSend.timeout = -1;
+ if ((nlc->nlhpi.flags & NLHPIF_USEGETSEQUENCE) && (nlc->nlhpi.szHttpGetUrl != NULL))
+ {
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s%u", nlc->nlhpi.szHttpGetUrl, nlc->nlhpi.firstGetSequence++);
+ if (nlc->nlhpi.flags & NLHPIF_GETPOSTSAMESEQUENCE) nlc->nlhpi.firstPostSequence++;
+
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+ nlhrSend.szUrl = szUrl;
+ }
+ else
+ nlhrSend.szUrl = nlc->nlhpi.szHttpGetUrl;
+ break;
+
+ case reqOldPost:
+ nlhrSend.requestType = REQUEST_POST;
+ if ((nlc->nlhpi.flags & NLHPIF_USEPOSTSEQUENCE) && (nlc->nlhpi.szHttpPostUrl != NULL))
+ {
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s%u", nlc->nlhpi.szHttpPostUrl, nlc->nlhpi.firstPostSequence);
+ nlhrSend.szUrl = szUrl;
+ }
+ else
+ nlhrSend.szUrl = nlc->nlhpi.szHttpPostUrl;
+ break;
+
+ case reqNewPost:
+ nlhrSend.requestType = REQUEST_POST;
+ nlhrSend.szUrl = nlc->nlhpi.szHttpPostUrl;
+ break;
+ }
+
+ if (nlc->usingDirectHttpGateway)
+ {
+ NETLIBOPENCONNECTION nloc;
+ NetlibConnFromUrl(nlhrSend.szUrl, false, nloc);
+
+ bool sameHost = lstrcmpA(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort;
+
+ if (!sameHost)
+ {
+ NetlibDoClose(nlc);
+
+ mir_free((char*)nlc->nloc.szHost);
+ nlc->nloc = nloc;
+ if (!NetlibDoConnect(nlc))
+ return false;
+ }
+ else
+ mir_free((char*)nloc.szHost);
+ }
+
+ nlhrSend.headersCount = 3;
+ nlhrSend.headers = (NETLIBHTTPHEADER*)alloca(sizeof(NETLIBHTTPHEADER) * nlhrSend.headersCount);
+ nlhrSend.headers[0].szName = "User-Agent";
+ nlhrSend.headers[0].szValue = nlc->nlu->user.szHttpGatewayUserAgent;
+ nlhrSend.headers[1].szName = "Cache-Control";
+ nlhrSend.headers[1].szValue = "no-cache, no-store ";
+ nlhrSend.headers[2].szName = "Pragma";
+ nlhrSend.headers[2].szValue = "no-cache";
+// nlhrSend.headers[3].szName = "Accept-Encoding";
+// nlhrSend.headers[3].szValue = "deflate, gzip";
+
+ return NetlibHttpSendRequest((WPARAM)nlc,(LPARAM)&nlhrSend) != SOCKET_ERROR;
+}
+
+static bool NetlibHttpGatewayStdPost(NetlibConnection *nlc, int& numPackets)
+{
+ int np = 0, len = 0;
+
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+
+ NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue;
+ while (p != NULL && np < nlc->nlhpi.combinePackets) { ++np; len += p->dataBufferLen; p = p->next;}
+
+ char *buf = (char*)alloca(len);
+
+ numPackets = np;
+ int dlen = 0;
+
+ p = nlc->pHttpProxyPacketQueue;
+ while (np--)
+ {
+ memcpy(buf + dlen, p->dataBuffer, p->dataBufferLen);
+ dlen += p->dataBufferLen;
+ p = p->next;
+ }
+
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+
+ return NetlibHttpGatewaySend(nlc, reqNewPost, buf, len);
+}
+
+static bool NetlibHttpGatewayOscarPost(NetlibConnection *nlc, const char *buf, int len, int flags)
+{
+ NETLIBHTTPREQUEST *nlhrReply = NULL;
+ NetlibConnection nlcSend = {0};
+
+ nlcSend.handleType = NLH_CONNECTION;
+ nlcSend.nlu = nlc->nlu;
+ nlcSend.nlhpi = nlc->nlhpi;
+ nlcSend.s = nlc->s2;
+ nlcSend.usingHttpGateway = nlc->usingHttpGateway;
+ nlcSend.szProxyServer = nlc->szProxyServer;
+ nlcSend.wProxyPort = nlc->wProxyPort;
+ nlcSend.proxyType = nlc->proxyType;
+
+ if (!NetlibReconnect(&nlcSend)) return false;
+ nlc->s2 = nlcSend.s;
+
+ nlcSend.hOkToCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ NetlibInitializeNestedCS(&nlcSend.ncsRecv);
+ NetlibInitializeNestedCS(&nlcSend.ncsSend);
+
+ bool res = NetlibHttpGatewaySend(&nlcSend, reqOldPost, buf, len);
+ if (res)
+ {
+ NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(&nlcSend, flags | MSG_RAW | MSG_DUMPPROXY, MSG_RAW | MSG_DUMPPROXY);
+ if (nlhrReply != NULL)
+ {
+ if (nlhrReply->resultCode != 200)
+ {
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ res = false;
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ }
+ else
+ res = false;
+ }
+
+ NetlibDeleteNestedCS(&nlcSend.ncsSend);
+ NetlibDeleteNestedCS(&nlcSend.ncsRecv);
+ CloseHandle(nlcSend.hOkToCloseEvent);
+
+ nlc->s2 = nlcSend.s;
+ mir_free((char*)nlcSend.nloc.szHost);
+
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+
+ nlc->nlhpi.firstPostSequence++;
+ if (nlc->nlhpi.flags & NLHPIF_GETPOSTSAMESEQUENCE) nlc->nlhpi.firstGetSequence++;
+
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+
+ return res;
+}
+
+ int NetlibHttpGatewayPost(struct NetlibConnection *nlc, const char *buf, int len, int flags)
+{
+ struct NetlibHTTPProxyPacketQueue *p;
+
+ if (nlc->nlhpi.szHttpGetUrl != NULL)
+ {
+ return NetlibHttpGatewayOscarPost(nlc, buf, len, flags) ? len : SOCKET_ERROR;
+ }
+
+ /*
+ * Gena01 - many changes here, do compare against the other version.
+ *
+ * Change #1: simplify to use similar code to GET
+ * Change #2: we need to allow to parse POST reply if szHttpGetUrl is NULL
+ * Change #3: Keep connection open if we need to.
+ *
+ * Impact: NONE! Since currently miranda doesn't allow szHttpGetUrl to be NULL, it will not connect
+ * with the new plugins that use this code.
+ */
+
+ p = ( NetlibHTTPProxyPacketQueue* )mir_alloc(sizeof(struct NetlibHTTPProxyPacketQueue));
+ p->dataBuffer = ( PBYTE )mir_alloc(len);
+ memcpy(p->dataBuffer, buf, len);
+ p->dataBufferLen = len;
+ p->next = NULL;
+
+ /*
+ * Now check to see where to insert this in our queue
+ */
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+ if (nlc->pHttpProxyPacketQueue == NULL)
+ {
+ nlc->pHttpProxyPacketQueue = p;
+ }
+ else
+ {
+ struct NetlibHTTPProxyPacketQueue *t = nlc->pHttpProxyPacketQueue;
+
+ while (t->next != NULL) t = t->next;
+ t->next = p;
+ }
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+
+ /*
+ * Gena01 - fake a Send!! tell 'em all is ok. We catch errors in Recv.
+ */
+ return len;
+}
+
+#define NETLIBHTTP_RETRYCOUNT 3
+#define NETLIBHTTP_RETRYTIMEOUT 2000
+
+int NetlibHttpGatewayRecv(struct NetlibConnection *nlc, char *buf, int len, int flags)
+{
+ bool peek = (flags & MSG_PEEK) != 0;
+
+ if (nlc->dataBufferLen != 0 && (!peek || nlc->dataBufferLen >= len))
+ {
+ return HttpGatewayReadSetResult(nlc, buf, len, peek);
+ }
+
+ for (int retryCount = 0; retryCount < NETLIBHTTP_RETRYCOUNT; )
+ {
+ if (nlc->nlhpi.szHttpGetUrl == NULL && retryCount == 0)
+ {
+ if (nlc->pollingTimeout == 0) nlc->pollingTimeout = 30;
+
+ /* We Need to sleep/wait for the data to send before we do receive */
+ for (int pollCount = nlc->pollingTimeout; pollCount--; )
+ {
+ if (nlc->pHttpProxyPacketQueue != NULL && GetTickCount() - nlc->lastPost > 1000)
+ break;
+
+ if (nlc->termRequested || (SleepEx(1000, TRUE) && Miranda_Terminated()))
+ return SOCKET_ERROR;
+ }
+
+ nlc->lastPost = GetTickCount();
+ if (nlc->pHttpProxyPacketQueue == NULL && nlc->nlu->user.pfnHttpGatewayWrapSend != NULL)
+ {
+ if (nlc->nlu->user.pfnHttpGatewayWrapSend(nlc, (PBYTE)"", 0, MSG_NOHTTPGATEWAYWRAP, NetlibSend) == SOCKET_ERROR)
+ return SOCKET_ERROR;
+ }
+ }
+
+ int numPackets = 0;
+ if (nlc->nlhpi.szHttpGetUrl)
+ {
+ if (!NetlibHttpGatewaySend(nlc, reqOldGet, NULL, 0))
+ {
+ if (GetLastError() == ERROR_ACCESS_DENIED || nlc->termRequested)
+ break;
+
+ ++retryCount;
+ continue;
+ }
+ }
+ else
+ {
+ if (!NetlibHttpGatewayStdPost(nlc, numPackets))
+ {
+ if (GetLastError() == ERROR_ACCESS_DENIED || nlc->termRequested)
+ break;
+
+ ++retryCount;
+ continue;
+ }
+ }
+ NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, flags | MSG_RAW | MSG_DUMPPROXY, MSG_RAW | MSG_DUMPPROXY);
+ if (nlhrReply == NULL) return SOCKET_ERROR;
+
+ if (nlc->nlu->user.pfnHttpGatewayUnwrapRecv && !(flags & MSG_NOHTTPGATEWAYWRAP))
+ {
+ nlhrReply->pData = (char*)nlc->nlu->user.pfnHttpGatewayUnwrapRecv(nlhrReply,
+ (PBYTE)nlhrReply->pData, nlhrReply->dataLength, &nlhrReply->dataLength, mir_realloc);
+/*
+ if (newBuffer == NULL)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return SOCKET_ERROR;
+ }
+ else
+ nlhrReply->pData = (char*)newBuffer;
+*/
+ }
+
+ if (nlhrReply->resultCode >= 300)
+ {
+ int resultCode = nlhrReply->resultCode;
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ if (nlc->nlhpi.szHttpGetUrl && resultCode != 404)
+ {
+ NetlibLogf(nlc->nlu, "Error received from proxy, retrying");
+ continue;
+ }
+ else
+ {
+ NetlibLogf(nlc->nlu, "Error received from proxy, retry attempts exceeded (%u)", retryCount);
+ SetLastError(ERROR_GEN_FAILURE);
+ return SOCKET_ERROR;
+ }
+ }
+ else
+ {
+ retryCount = 0;
+ HttpGatewayRemovePacket(nlc, numPackets);
+ }
+
+ if (nlhrReply->dataLength)
+ {
+ if (peek)
+ {
+ int rbytes = nlc->dataBufferLen + nlhrReply->dataLength;
+
+ nlc->dataBuffer = (PBYTE)mir_realloc(nlc->dataBuffer, rbytes);
+ memcpy(nlc->dataBuffer + nlc->dataBufferLen, nlhrReply->pData, nlhrReply->dataLength);
+ nlc->dataBufferLen = rbytes;
+
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ return HttpGatewayReadSetResult(nlc, buf, len, peek);
+ }
+ else
+ {
+ int bytes = min(len, nlhrReply->dataLength);
+ int rbytes = nlhrReply->dataLength - bytes;
+
+ memcpy(buf, nlhrReply->pData, bytes);
+
+ nlc->dataBuffer = (PBYTE)mir_realloc(nlc->dataBuffer, rbytes);
+ if (rbytes) memcpy(nlc->dataBuffer, nlhrReply->pData + bytes, rbytes);
+ nlc->dataBufferLen = rbytes;
+
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return bytes;
+ }
+ }
+ else
+ {
+ if ((peek && nlc->dataBufferLen != 0) || nlhrReply->pData)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return HttpGatewayReadSetResult(nlc, buf, len, peek);
+ }
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ }
+
+ SetLastError(ERROR_GEN_FAILURE);
+ return SOCKET_ERROR;
+}
+
+int NetlibInitHttpConnection(struct NetlibConnection *nlc, struct NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{
+ NETLIBHTTPREQUEST *nlhrReply = NULL;
+
+ nlc->nlhpi.firstGetSequence = 1;
+ nlc->nlhpi.firstPostSequence = 1;
+
+ if (nlu->user.szHttpGatewayHello != NULL)
+ {
+ nlc->usingHttpGateway = true;
+ if (NetlibHttpGatewaySend(nlc, reqHelloGet, NULL, 0))
+ nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW);
+ nlc->usingHttpGateway = false;
+ if (nlhrReply == NULL) return 0;
+
+ if (nlhrReply->resultCode != 200)
+ {
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return 0;
+ }
+ }
+ if (!nlu->user.pfnHttpGatewayInit(nlc, nloc, nlhrReply))
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return 0;
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ /*
+ * Gena01 - Ok, we should be able to use just POST. Needed for Yahoo, NO GET requests
+ */
+ if(nlc->nlhpi.szHttpPostUrl == NULL)
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ nlc->usingHttpGateway = true;
+
+ //now properly connected
+ if (nlu->user.pfnHttpGatewayBegin && !nlu->user.pfnHttpGatewayBegin(nlc, nloc))
+ return 0;
+
+ return 1;
+}
+
+INT_PTR NetlibHttpGatewaySetInfo(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBHTTPPROXYINFO *nlhpi=(NETLIBHTTPPROXYINFO*)lParam;
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == NULL ||
+ nlhpi->cbSize < (sizeof(NETLIBHTTPPROXYINFO) - sizeof(int)) ||
+ nlhpi->szHttpPostUrl == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+
+ nlc->nlhpi.combinePackets = 1;
+ memcpy(&nlc->nlhpi, nlhpi, min(nlhpi->cbSize, 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;
+}
+
+INT_PTR NetlibHttpSetSticky(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibUser * nu = (struct NetlibUser*)wParam;
+ if (GetNetlibHandleType(nu)!=NLH_USER) return ERROR_INVALID_PARAMETER;
+ mir_free(nu->szStickyHeaders);
+ nu->szStickyHeaders = mir_strdup((char*)lParam); // pointer is ours
+ return 0;
+}
+
+INT_PTR NetlibHttpSetPollingTimeout(WPARAM wParam, LPARAM lParam)
+{
+ int oldTimeout;
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ if (GetNetlibHandleType(nlc)!=NLH_CONNECTION) return -1;
+ oldTimeout = nlc->pollingTimeout;
+ nlc->pollingTimeout = lParam;
+ return oldTimeout;
+}
diff --git a/src/modules/netlib/netliblog.cpp b/src/modules/netlib/netliblog.cpp new file mode 100644 index 0000000000..fb3fe1d5f2 --- /dev/null +++ b/src/modules/netlib/netliblog.cpp @@ -0,0 +1,637 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+#include "../srfile/file.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;
+ TCHAR* szFile;
+ TCHAR* szUserFile;
+ int timeFormat;
+ int showUser;
+ int dumpSent,dumpRecv,dumpProxy,dumpSsl;
+ int textDumps,autoDetectText;
+ CRITICAL_SECTION cs;
+ int save;
+} logOptions = {0};
+
+typedef struct {
+ const char* pszHead;
+ const char* pszMsg;
+} LOGMSG;
+
+static __int64 mirandaStartTime,perfCounterFreq;
+static int bIsActive = TRUE;
+static HANDLE hLogEvent = NULL;
+
+static const TCHAR* szTimeFormats[] =
+{
+ _T( "No times" ),
+ _T( "Standard hh:mm:ss times" ),
+ _T( "Times in milliseconds" ),
+ _T( "Times in microseconds" )
+};
+
+static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ 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);
+ { int i;
+ for( i=0; i < SIZEOF(szTimeFormats); i++ )
+ SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_ADDSTRING,0,(LPARAM)TranslateTS( szTimeFormats[i] ));
+ }
+ 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.szUserFile);
+ SetDlgItemText(hwndDlg,IDC_PATH,logOptions.szFile);
+ CheckDlgButton(hwndDlg,IDC_SHOWTHISDLGATSTART,DBGetContactSettingByte(NULL, "Netlib", "ShowLogOptsAtStart",0)?BST_CHECKED:BST_UNCHECKED);
+ { DBVARIANT dbv;
+ if(!DBGetContactSettingString(NULL, "Netlib", "RunAtStart",&dbv)) {
+ SetDlgItemTextA(hwndDlg,IDC_RUNATSTART,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ logOptions.save = 0;
+ {
+ TVINSERTSTRUCT tvis = {0};
+ int i;
+ HWND hwndFilter = GetDlgItem(hwndDlg,IDC_FILTER);
+
+ SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ tvis.hParent=NULL;
+ tvis.hInsertAfter=TVI_SORT;
+ tvis.item.mask=TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
+ tvis.item.stateMask=TVIS_STATEIMAGEMASK;
+
+ for (i = 0; i < netlibUser.getCount(); ++i)
+ {
+ tvis.item.pszText=netlibUser[i]->user.ptszDescriptiveName;
+ tvis.item.lParam=i;
+ tvis.item.state=INDEXTOSTATEIMAGEMASK( (netlibUser[i]->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_DUMPRECV:
+ case IDC_DUMPSENT:
+ case IDC_DUMPPROXY:
+ case IDC_TEXTDUMPS:
+ case IDC_AUTODETECTTEXT:
+ case IDC_TIMEFORMAT:
+ case IDC_SHOWNAMES:
+ case IDC_TOOUTPUTDEBUGSTRING:
+ case IDC_TOFILE:
+ case IDC_SHOWTHISDLGATSTART:
+ case IDC_RUNATSTART:
+ break;
+*/
+ case IDC_FILENAME:
+ if(HIWORD(wParam)!=EN_CHANGE) break;
+ if((HWND)lParam==GetFocus())
+ CheckDlgButton(hwndDlg,IDC_TOFILE,BST_CHECKED);
+
+ {
+ TCHAR path[MAX_PATH];
+ GetWindowText((HWND)lParam, path, MAX_PATH);
+
+ TCHAR *pszNewPath = Utils_ReplaceVarsT(path);
+ pathToAbsoluteT(pszNewPath, path, NULL);
+ SetDlgItemText(hwndDlg, IDC_PATH, path);
+ mir_free(pszNewPath);
+ }
+ break;
+ case IDC_FILENAMEBROWSE:
+ case IDC_RUNATSTARTBROWSE:
+ { TCHAR str[MAX_PATH+2];
+ OPENFILENAME ofn={0};
+ TCHAR filter[512],*pfilter;
+
+ GetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str,SIZEOF(str));
+ 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");
+ }
+ _tcscpy(filter,TranslateT("All Files"));
+ _tcscat(filter,_T(" (*)"));
+ pfilter=filter+lstrlen(filter)+1;
+ _tcscpy(pfilter,_T("*"));
+ pfilter=pfilter+lstrlen(pfilter)+1;
+ *pfilter='\0';
+ ofn.lpstrFilter=filter;
+ ofn.lpstrFile=str;
+ ofn.nMaxFile=SIZEOF(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 && _tcschr(str,' ')!=NULL) {
+ MoveMemory(str+1,str,SIZEOF(str)-2);
+ str[0]='"';
+ lstrcat(str,_T("\""));
+ }
+ SetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str);
+ break;
+ }
+ case IDC_RUNNOW:
+ { TCHAR str[MAX_PATH+1];
+ STARTUPINFO si={0};
+ PROCESS_INFORMATION pi;
+ GetDlgItemText(hwndDlg,IDC_RUNATSTART,str,MAX_PATH);
+ si.cb=sizeof(si);
+ if(str[0]) CreateProcess(NULL,str,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
+ }
+ break;
+ case IDC_SAVE:
+ logOptions.save = 1;
+ //
+ case IDOK:
+ {
+ TCHAR str[MAX_PATH];
+
+ GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, MAX_PATH);
+ DBWriteContactSettingTString(NULL, "Netlib", "RunAtStart",str);
+ DBWriteContactSettingByte(NULL, "Netlib", "ShowLogOptsAtStart",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_SHOWTHISDLGATSTART));
+
+ EnterCriticalSection(&logOptions.cs);
+
+ mir_free(logOptions.szUserFile);
+ GetWindowText(GetDlgItem(hwndDlg,IDC_FILENAME), str, MAX_PATH );
+ logOptions.szUserFile = mir_tstrdup(str);
+
+ mir_free(logOptions.szFile);
+ GetWindowText(GetDlgItem(hwndDlg,IDC_PATH), str, MAX_PATH );
+ logOptions.szFile = mir_tstrdup(str);
+
+ logOptions.dumpRecv=IsDlgButtonChecked(hwndDlg,IDC_DUMPRECV);
+ logOptions.dumpSent=IsDlgButtonChecked(hwndDlg,IDC_DUMPSENT);
+ logOptions.dumpProxy=IsDlgButtonChecked(hwndDlg,IDC_DUMPPROXY);
+ logOptions.dumpSsl=IsDlgButtonChecked(hwndDlg,IDC_DUMPSSL);
+ logOptions.textDumps=IsDlgButtonChecked(hwndDlg,IDC_TEXTDUMPS);
+ logOptions.autoDetectText=IsDlgButtonChecked(hwndDlg,IDC_AUTODETECTTEXT);
+ logOptions.timeFormat=SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_GETCURSEL,0,0);
+ logOptions.showUser=IsDlgButtonChecked(hwndDlg,IDC_SHOWNAMES);
+ logOptions.toOutputDebugString=IsDlgButtonChecked(hwndDlg,IDC_TOOUTPUTDEBUGSTRING);
+ logOptions.toFile=IsDlgButtonChecked(hwndDlg,IDC_TOFILE);
+
+ LeaveCriticalSection(&logOptions.cs);
+ }
+ {
+ 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;
+ if ( logOptions.save )
+ DBWriteContactSettingDword(NULL, "Netlib", "NLlog",checked);
+ }
+ else
+ if (tvi.lParam < netlibUser.getCount()) {
+ netlibUser[tvi.lParam]->toLog = checked;
+ if ( logOptions.save )
+ DBWriteContactSettingDword(NULL,netlibUser[tvi.lParam]->user.szSettingsModule,"NLlog",checked);
+ }
+
+ tvi.hItem=TreeView_GetNextSibling(hwndFilter,tvi.hItem);
+ }
+ }
+
+ if ( logOptions.save ) {
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpRecv",(BYTE)logOptions.dumpRecv);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpSent",(BYTE)logOptions.dumpSent);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpProxy",(BYTE)logOptions.dumpProxy);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpSsl",(BYTE)logOptions.dumpSsl);
+ DBWriteContactSettingByte(NULL, "Netlib", "TextDumps",(BYTE)logOptions.textDumps);
+ DBWriteContactSettingByte(NULL, "Netlib", "AutoDetectText",(BYTE)logOptions.autoDetectText);
+ DBWriteContactSettingByte(NULL, "Netlib", "TimeFormat",(BYTE)logOptions.timeFormat);
+ DBWriteContactSettingByte(NULL, "Netlib", "ShowUser",(BYTE)logOptions.showUser);
+ DBWriteContactSettingByte(NULL, "Netlib", "ToOutputDebugString",(BYTE)logOptions.toOutputDebugString);
+ DBWriteContactSettingByte(NULL, "Netlib", "ToFile",(BYTE)logOptions.toFile);
+ DBWriteContactSettingTString(NULL, "Netlib", "File", logOptions.szFile ? logOptions.szUserFile: _T(""));
+ logOptions.save = 0;
+ }
+ else
+ DestroyWindow(hwndDlg);
+
+ break;
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE));
+ logOptions.hwndOpts=NULL;
+ break;
+ }
+ return FALSE;
+}
+
+void NetlibLogShowOptions(void)
+{
+ if(logOptions.hwndOpts==NULL)
+ logOptions.hwndOpts=CreateDialog(hMirandaInst,MAKEINTRESOURCE(IDD_NETLIBLOGOPTS),NULL,LogOptionsDlgProc);
+ SetForegroundWindow(logOptions.hwndOpts);
+}
+
+static INT_PTR ShowOptions(WPARAM, LPARAM)
+{
+ NetlibLogShowOptions();
+ return 0;
+}
+
+static INT_PTR NetlibLog(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibUser nludummy;
+ const char *pszMsg = (const char*)lParam;
+ char szTime[32], szHead[128];
+ LARGE_INTEGER liTimeNow;
+ DWORD dwOriginalLastError;
+
+ if (!bIsActive)
+ return 0;
+
+ if ((nlu != NULL && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if (nlu == NULL) /* if the Netlib user handle is NULL, just pretend its not */
+ {
+ if (!logOptions.toLog)
+ return 1;
+ nlu = &nludummy;
+ nlu->user.szSettingsModule = "(NULL)";
+ }
+ else if (!nlu->toLog)
+ return 1;
+
+ dwOriginalLastError = GetLastError();
+ switch (logOptions.timeFormat)
+ {
+ case TIMEFORMAT_HHMMSS:
+ GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER,
+ NULL, NULL, szTime, SIZEOF(szTime));
+ break;
+
+ case TIMEFORMAT_MILLISECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, SIZEOF(szTime), "%I64u.%03I64u", liTimeNow.QuadPart / perfCounterFreq,
+ 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ case TIMEFORMAT_MICROSECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, SIZEOF(szTime), "%I64u.%06I64u", liTimeNow.QuadPart / perfCounterFreq,
+ 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ default:
+ szTime[0] = '\0';
+ break;
+ }
+ if(logOptions.timeFormat || logOptions.showUser)
+ mir_snprintf(szHead, SIZEOF(szHead) - 1, "[%s%s%s] ", szTime,
+ (logOptions.showUser && logOptions.timeFormat) ? " " : "",
+ logOptions.showUser ? nlu->user.szSettingsModule : "");
+ else
+ szHead[0]=0;
+
+ if(logOptions.toOutputDebugString)
+ {
+ if (szHead[0])
+ OutputDebugStringA(szHead);
+ OutputDebugStringA(pszMsg);
+ OutputDebugStringA("\n");
+ }
+
+ if (logOptions.toFile && logOptions.szFile[0])
+ {
+ EnterCriticalSection(&logOptions.cs);
+
+ FILE *fp;
+ fp = _tfopen(logOptions.szFile, _T("ab"));
+ if (!fp)
+ {
+ CreatePathToFileT(logOptions.szFile);
+ fp = _tfopen(logOptions.szFile, _T("at"));
+ }
+ if (fp)
+ {
+ size_t len = strlen(pszMsg);
+ fprintf(fp,"%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
+ fclose(fp);
+ }
+ LeaveCriticalSection(&logOptions.cs);
+ }
+
+ if (((THook*)hLogEvent)->subscriberCount)
+ {
+ LOGMSG logMsg = { szHead, pszMsg };
+ CallHookSubscribers(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
+ }
+
+ SetLastError(dwOriginalLastError);
+ return 1;
+}
+
+static INT_PTR NetlibLogW(WPARAM wParam, LPARAM lParam)
+{
+ const wchar_t *pszMsg = (const wchar_t*)lParam;
+ char* szMsg = Utf8EncodeUcs2(pszMsg);
+ INT_PTR res = NetlibLog(wParam, (LPARAM)szMsg);
+ mir_free(szMsg);
+ return res;
+}
+
+void NetlibLogf(NetlibUser* nlu, const char *fmt, ...)
+{
+ if (nlu == NULL)
+ {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ va_list va;
+ char szText[1024];
+
+ va_start(va,fmt);
+ mir_vsnprintf(szText, sizeof(szText), fmt, va);
+ va_end(va);
+
+ NetlibLog((WPARAM)nlu, (LPARAM)szText);
+}
+
+
+void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags)
+{
+ int isText=1;
+ char szTitleLine[128];
+ char *szBuf;
+ int titleLineLen;
+ struct NetlibUser *nlu;
+ bool useStack = false;
+
+ // 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 ||
+ ((THook*)hLogEvent)->subscriberCount ||
+ (logOptions.toFile && logOptions.szFile[0])))
+ return;
+ if ((sent && !logOptions.dumpSent) ||
+ (!sent && !logOptions.dumpRecv))
+ return;
+ if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
+ return;
+ if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
+ return;
+
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+ nlu = nlc ? nlc->nlu : NULL;
+ titleLineLen = mir_snprintf(szTitleLine, SIZEOF(szTitleLine), "(%p:%u) Data %s%s\r\n",
+ nlc, nlc ? nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ // check filter settings
+ if (nlu == NULL)
+ {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ if (!logOptions.textDumps)
+ isText = 0;
+ else if (!(flags&MSG_DUMPASTEXT))
+ {
+ if (logOptions.autoDetectText)
+ {
+ int i;
+ for(i = 0; i<len; i++)
+ {
+ if ((buf[i]<' ' && buf[i]!='\t' && buf[i]!='\r' && buf[i]!='\n') || buf[i]>=0x80)
+ {
+ isText = 0;
+ break;
+ }
+ }
+ }
+ else
+ isText = 0;
+ }
+
+ // Text data
+ if ( isText ) {
+ int sz = titleLineLen + len + 1;
+ useStack = sz <= 8192;
+ szBuf = (char*)(useStack ? alloca(sz) : mir_alloc(sz));
+ CopyMemory( szBuf, szTitleLine, titleLineLen );
+ CopyMemory( szBuf + titleLineLen, (const char*)buf, len );
+ szBuf[titleLineLen + len] = '\0';
+ }
+ // Binary data
+ else {
+ int line, col, colsInLine;
+ char *pszBuf;
+ int sz = titleLineLen + ((len+16)>>4) * 78 + 1;
+ useStack = sz <= 8192;
+
+ szBuf = (char*)(useStack ? alloca(sz) : mir_alloc(sz));
+ CopyMemory(szBuf, szTitleLine, titleLineLen);
+ pszBuf = szBuf + titleLineLen;
+ for ( line = 0; ; line += 16 ) {
+ colsInLine = min(16, len - line);
+
+ if (colsInLine == 16) {
+ PBYTE p = buf + line;
+ pszBuf += wsprintfA(
+ pszBuf, "%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 {
+ pszBuf += wsprintfA(pszBuf, "%08X: ", line);
+ // Dump data as hex
+ for (col = 0; col < colsInLine; col++)
+ pszBuf += wsprintfA(pszBuf, "%02X%c", buf[line + col], ((col&3)==3 && col != 15)?'-':' ');
+ // Fill out last line with blanks
+ for ( ; col<16; col++)
+ {
+ lstrcpyA(pszBuf, " ");
+ pszBuf += 3;
+ }
+ *pszBuf++ = ' ';
+ }
+
+ for (col = 0; col < colsInLine; col++)
+ *pszBuf++ = buf[line+col]<' '?'.':(char)buf[line+col];
+
+ if (len-line<=16)
+ break;
+
+ *pszBuf++ = '\r'; // End each line with a break
+ *pszBuf++ = '\n'; // End each line with a break
+ }
+ *pszBuf = '\0';
+ }
+
+ NetlibLog((WPARAM)nlu,(LPARAM)szBuf);
+ if (!useStack) mir_free(szBuf);
+}
+
+void NetlibLogInit(void)
+{
+ DBVARIANT dbv;
+ LARGE_INTEGER li;
+
+ QueryPerformanceFrequency( &li );
+ perfCounterFreq = li.QuadPart;
+ QueryPerformanceCounter( &li );
+ mirandaStartTime = li.QuadPart;
+
+ CreateServiceFunction( MS_NETLIB_LOGWIN, ShowOptions );
+ CreateServiceFunction( MS_NETLIB_LOG, NetlibLog );
+ CreateServiceFunction( MS_NETLIB_LOGW, NetlibLogW );
+ hLogEvent = CreateHookableEvent( ME_NETLIB_FASTDUMP );
+
+ InitializeCriticalSection(&logOptions.cs);
+ logOptions.dumpRecv = DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", 1 );
+ logOptions.dumpSent = DBGetContactSettingByte( NULL, "Netlib", "DumpSent", 1 );
+ logOptions.dumpProxy = DBGetContactSettingByte( NULL, "Netlib", "DumpProxy", 1 );
+ logOptions.dumpSsl = DBGetContactSettingByte( NULL, "Netlib", "DumpSsl", 0 );
+ logOptions.textDumps = DBGetContactSettingByte( NULL, "Netlib", "TextDumps", 1 );
+ logOptions.autoDetectText = DBGetContactSettingByte( NULL, "Netlib", "AutoDetectText", 1 );
+ logOptions.timeFormat = DBGetContactSettingByte( NULL, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS );
+ logOptions.showUser = DBGetContactSettingByte( NULL, "Netlib", "ShowUser", 1 );
+ logOptions.toOutputDebugString = DBGetContactSettingByte( NULL, "Netlib", "ToOutputDebugString", 0 );
+ logOptions.toFile = DBGetContactSettingByte( NULL, "Netlib", "ToFile", 0 );
+ logOptions.toLog = DBGetContactSettingDword( NULL, "Netlib", "NLlog", 1 );
+
+ if (!DBGetContactSettingTString(NULL, "Netlib", "File", &dbv))
+ {
+ logOptions.szUserFile = mir_tstrdup(dbv.ptszVal);
+ TCHAR *pszNewPath = Utils_ReplaceVarsT(dbv.ptszVal);
+
+ TCHAR path[MAX_PATH];
+ pathToAbsoluteT(pszNewPath, path, NULL);
+ logOptions.szFile = mir_tstrdup(path);
+
+ mir_free(pszNewPath);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ logOptions.szUserFile = mir_tstrdup(_T("%miranda_logpath%\\netlog.txt"));
+ logOptions.szFile = Utils_ReplaceVarsT(logOptions.szUserFile);
+ }
+
+ if ( logOptions.toFile && logOptions.szFile[0] ) {
+ FILE *fp;
+ fp = _tfopen( logOptions.szFile, _T("wt"));
+ if ( fp )
+ fclose(fp);
+ }
+
+ if ( DBGetContactSettingByte( NULL, "Netlib", "ShowLogOptsAtStart", 0 ))
+ NetlibLogShowOptions();
+
+ if ( !DBGetContactSettingTString( NULL, "Netlib", "RunAtStart", &dbv )) {
+ STARTUPINFO si = { 0 };
+ PROCESS_INFORMATION pi;
+ si.cb = sizeof( si );
+ if ( dbv.ptszVal[0] )
+ CreateProcess( NULL, dbv.ptszVal, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
+ DBFreeVariant( &dbv );
+ }
+}
+
+void NetlibLogShutdown(void)
+{
+ bIsActive = FALSE;
+ DestroyHookableEvent( hLogEvent ); hLogEvent = NULL;
+ if ( IsWindow( logOptions.hwndOpts ))
+ DestroyWindow( logOptions.hwndOpts );
+ DeleteCriticalSection( &logOptions.cs );
+ mir_free( logOptions.szFile );
+ mir_free( logOptions.szUserFile );
+}
diff --git a/src/modules/netlib/netlibopenconn.cpp b/src/modules/netlib/netlibopenconn.cpp new file mode 100644 index 0000000000..b9d4753b9a --- /dev/null +++ b/src/modules/netlib/netlibopenconn.cpp @@ -0,0 +1,944 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+extern CRITICAL_SECTION 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(struct 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];
+
+ NetlibLogf(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, 0, 0, &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, 0, &writefd, 0, &tv))
+ {
+ case 0:
+ SetLastError(ERROR_TIMEOUT);
+ case SOCKET_ERROR:
+ return 0;
+ }
+ return 1;
+}
+
+bool RecvUntilTimeout(struct 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 = NLRecv(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->szHost || !nloc->szHost[0]) return 0;
+
+ size_t nHostLen = strlen(nloc->szHost) + 1;
+ size_t nUserLen = nlu->settings.szProxyAuthUser ? 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 (NLSend(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR)
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ return 0;
+ }
+
+ char reply[8];
+ if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT))
+ {
+ NetlibLogf(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;
+ }
+ NetlibLogf(nlu,"%s %d: Proxy connection failed (%x %u)",__FILE__,__LINE__, (BYTE)reply[1], GetLastError());
+ return 0;
+}
+
+static int NetlibInitSocks5Connection(struct NetlibConnection *nlc,struct 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(NLSend(nlc,(char*)buf,3,MSG_DUMPPROXY)==SOCKET_ERROR) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ return 0;
+ }
+
+ //confirmation of auth method
+ if (!RecvUntilTimeout(nlc,(char*)buf,2,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ if((buf[1]!=0 && buf[1]!=2)) {
+ SetLastError(ERROR_INVALID_ID_AUTHORITY);
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLRecv",GetLastError());
+ return 0;
+ }
+
+ if(buf[1]==2) { //rfc1929
+ int nUserLen,nPassLen;
+ PBYTE pAuthBuf;
+
+ nUserLen=lstrlenA(nlu->settings.szProxyAuthUser);
+ nPassLen=lstrlenA(nlu->settings.szProxyAuthPassword);
+ pAuthBuf=(PBYTE)mir_alloc(3+nUserLen+nPassLen);
+ pAuthBuf[0]=1; //auth version
+ pAuthBuf[1]=nUserLen;
+ memcpy(pAuthBuf+2,nlu->settings.szProxyAuthUser,nUserLen);
+ pAuthBuf[2+nUserLen]=nPassLen;
+ memcpy(pAuthBuf+3+nUserLen,nlu->settings.szProxyAuthPassword,nPassLen);
+ if(NLSend(nlc,(char*)pAuthBuf,3+nUserLen+nPassLen,MSG_DUMPPROXY)==SOCKET_ERROR) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ mir_free(pAuthBuf);
+ return 0;
+ }
+ mir_free(pAuthBuf);
+
+ if (!RecvUntilTimeout(nlc,(char*)buf,2,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ if(buf[1]) {
+ SetLastError(ERROR_ACCESS_DENIED);
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ }
+
+ {
+ PBYTE pInit;
+ int nHostLen;
+ DWORD hostIP;
+
+ if(nlc->dnsThroughProxy) {
+ if((hostIP=inet_addr(nloc->szHost))==INADDR_NONE)
+ nHostLen=lstrlenA(nloc->szHost)+1;
+ else nHostLen=4;
+ }
+ else {
+ if((hostIP=DnsLookup(nlu,nloc->szHost))==0)
+ return 0;
+ nHostLen=4;
+ }
+ 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]=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(NLSend(nlc,(char*)pInit,6+nHostLen,MSG_DUMPPROXY)==SOCKET_ERROR) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ mir_free(pInit);
+ return 0;
+ }
+ mir_free(pInit);
+ }
+
+ if (!RecvUntilTimeout(nlc,(char*)buf,5,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(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;
+ }
+ }
+ NetlibLogf(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:
+ NetlibLogf(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)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ }
+
+ //connected
+ return 1;
+}
+
+static bool NetlibInitHttpsConnection(struct NetlibConnection *nlc, struct NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{ //rfc2817
+ NETLIBHTTPREQUEST nlhrSend = {0}, *nlhrReply;
+ char szUrl[512];
+
+ nlhrSend.cbSize = sizeof(nlhrSend);
+ nlhrSend.requestType = REQUEST_CONNECT;
+ nlhrSend.flags = NLHRF_GENERATEHOST | NLHRF_DUMPPROXY | NLHRF_SMARTAUTHHEADER | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT;
+ if (nlc->dnsThroughProxy)
+ {
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s:%u", nloc->szHost, nloc->wPort);
+ }
+ else
+ {
+ DWORD ip = DnsLookup(nlu, nloc->szHost);
+ if (ip == 0) return false;
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s:%u", inet_ntoa(*(PIN_ADDR)&ip), nloc->wPort);
+ }
+ nlhrSend.szUrl = szUrl;
+
+ nlc->usingHttpGateway = true;
+
+ if (NetlibHttpSendRequest((WPARAM)nlc, (LPARAM)&nlhrSend) == SOCKET_ERROR)
+ {
+ nlc->usingHttpGateway = false;
+ return 0;
+ }
+ nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true);
+ nlc->usingHttpGateway = false;
+ if (nlhrReply == NULL) return false;
+ if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300)
+ {
+ if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ nlc->dnsThroughProxy = 0;
+ return NetlibInitHttpsConnection(nlc, nlu, nloc);
+ }
+
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ NetlibLogf(nlu,"%s %d: %s request failed (%u %s)",__FILE__,__LINE__,nlu->settings.proxyType==PROXYTYPE_HTTP?"HTTP":"HTTPS",nlhrReply->resultCode,nlhrReply->szResultDescr);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return 0;
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ //connected
+ return true;
+}
+
+static void FreePartiallyInitedConnection(struct NetlibConnection *nlc)
+{
+ DWORD dwOriginalLastError=GetLastError();
+
+ if (nlc->s!=INVALID_SOCKET) closesocket(nlc->s);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free((char*)nlc->nloc.szHost);
+ mir_free(nlc->szProxyServer);
+ NetlibDeleteNestedCS(&nlc->ncsSend);
+ NetlibDeleteNestedCS(&nlc->ncsRecv);
+ CloseHandle(nlc->hOkToCloseEvent);
+ DeleteCriticalSection(&nlc->csHttpSequenceNums);
+ mir_free(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 };
+
+ unsigned int dwTimeout = (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && 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_Terminated()) return false;
+ }
+
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+
+ if (nlc->proxyType)
+ {
+ if (!nlc->szProxyServer) return false;
+
+ if (nloc)
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort);
+ else
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort);
+
+ sin.sin_port = htons(nlc->wProxyPort);
+ sin.sin_addr.s_addr = DnsLookup(nlc->nlu, nlc->szProxyServer);
+ }
+ else
+ {
+ if (!nloc || !nloc->szHost) return false;
+ NetlibLogf(nlc->nlu,"(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort);
+
+ sin.sin_port = htons(nloc->wPort);
+ sin.sin_addr.s_addr = DnsLookup(nlc->nlu, nloc->szHost);
+ }
+
+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 (nlc->nlu->settings.specifyOutgoingPorts && nlc->nlu->settings.szOutgoingPorts && nlc->nlu->settings.szOutgoingPorts[0])
+ {
+ if (!BindSocketToPort(nlc->nlu->settings.szOutgoingPorts, nlc->s, &nlc->nlu->inportnum))
+ NetlibLogf(nlc->nlu,"Netlib connect: Not enough ports for outgoing connections specified");
+ }
+
+ // try a connect
+ if (connect(nlc->s, (LPSOCKADDR)&sin, sizeof(sin)) == 0)
+ {
+ goto unblock;
+ }
+
+ // didn't work, was it cos of nonblocking?
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ rc = SOCKET_ERROR;
+ goto unblock;
+ }
+
+ for (;;)
+ {
+ 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_Terminated())
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ else if (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2 &&
+ nloc->waitcallback != NULL && nloc->waitcallback(&dwTimeout) == 0)
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ if (--dwTimeout == 0)
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+unblock:
+ notblocking = 0;
+ ioctlsocket(nlc->s, FIONBIO, ¬blocking);
+ if (lasterr) SetLastError(lasterr);
+ return rc == 0;
+}
+
+static bool my_connectIPv6(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc)
+{
+ int rc = SOCKET_ERROR, retrycnt = 0;
+ u_long notblocking = 1;
+ DWORD lasterr = 0;
+ static const TIMEVAL tv = { 1, 0 };
+
+ unsigned int dwTimeout = (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && 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_Terminated()) return false;
+ }
+
+ char szPort[6];
+ addrinfo *air = NULL, *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;
+
+ if (nloc)
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort);
+ else
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort);
+
+ _itoa(nlc->wProxyPort, szPort, 10);
+ if (MyGetaddrinfo(nlc->szProxyServer, szPort, &hints, &air))
+ {
+ NetlibLogf(nlc->nlu,"%s %d: %s() for host %s failed (%u)",__FILE__,__LINE__,"getaddrinfo", nlc->szProxyServer, WSAGetLastError());
+ return false;
+ }
+ }
+ else
+ {
+ if (!nloc || !nloc->szHost) return false;
+ NetlibLogf(nlc->nlu,"(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort);
+
+ _itoa(nlc->nloc.wPort, szPort, 10);
+
+ if (MyGetaddrinfo(nlc->nloc.szHost, szPort, &hints, &air))
+ {
+ NetlibLogf(nlc->nlu,"%s %d: %s() for host %s failed (%u)",__FILE__,__LINE__,"getaddrinfo", nlc->nloc.szHost, WSAGetLastError());
+ return false;
+ }
+ }
+
+ for (ai = air; ai && !Miranda_Terminated(); ai = ai->ai_next)
+ {
+retry:
+ nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (nlc->s == INVALID_SOCKET) return false;
+
+ // return the socket to non blocking
+ if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) return false;
+
+ if (nlc->nlu->settings.specifyOutgoingPorts && nlc->nlu->settings.szOutgoingPorts && nlc->nlu->settings.szOutgoingPorts[0])
+ {
+ if (!BindSocketToPort(nlc->nlu->settings.szOutgoingPorts, nlc->s, &nlc->nlu->inportnum))
+ NetlibLogf(nlc->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;
+ break;
+ }
+
+ for (;;) // 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_Terminated())
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ else if (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2 &&
+ nloc->waitcallback != NULL && 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;
+ }
+
+ MyFreeaddrinfo(air);
+
+ notblocking = 0;
+ if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking);
+ if (rc && lasterr) SetLastError(lasterr);
+ return rc == 0;
+}
+
+static bool my_connect(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc)
+{
+ return MyGetaddrinfo && MyFreeaddrinfo ? my_connectIPv6(nlc, nloc) : my_connectIPv4(nlc, nloc);
+}
+
+static int NetlibHttpFallbackToDirect(struct NetlibConnection *nlc, struct NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{
+ NetlibDoClose(nlc, true);
+
+ NetlibLogf(nlu,"Fallback to direct connection");
+
+ nlc->proxyAuthNeeded = false;
+ nlc->proxyType = 0;
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = NULL;
+ if (!my_connect(nlc, nloc))
+ {
+ NetlibLogf(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;
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = NULL;
+
+ 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;
+ }
+ }
+ }
+
+retry:
+ if (usingProxy)
+ {
+ if (!my_connect(nlc, nloc))
+ {
+ usingProxy = false;
+ nlc->proxyType = 0;
+ }
+ }
+ if (!usingProxy)
+ {
+ my_connect(nlc, nloc);
+ }
+
+ if (nlc->s == INVALID_SOCKET)
+ {
+ if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP))
+ {
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc))
+ {
+ NetlibLogf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+ }
+ else
+ {
+ if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps)
+ {
+ forceHttps = true;
+ usingProxy = NetlibGetIeProxyConn(nlc, true);
+ if (usingProxy) goto retry;
+ }
+ NetlibLogf(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:
+ nlc->proxyAuthNeeded = true;
+ if (!NetlibInitHttpsConnection(nlc, nlu, nloc))
+ {
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc))
+ return false;
+ }
+ break;
+
+ case PROXYTYPE_HTTP:
+ nlc->proxyAuthNeeded = true;
+ if (!(nlu->user.flags & NUF_HTTPGATEWAY || nloc->flags & NLOCF_HTTPGATEWAY) || nloc->flags & NLOCF_SSL)
+ {
+ //NLOCF_HTTP not specified and no HTTP gateway available: try HTTPS
+ if (!NetlibInitHttpsConnection(nlc, nlu, nloc))
+ {
+ //can't do HTTPS: try direct
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc))
+ return false;
+ }
+ }
+ else
+ {
+ if (!NetlibInitHttpConnection(nlc, nlu, nloc)) return false;
+ }
+ break;
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ FreePartiallyInitedConnection(nlc);
+ return false;
+ }
+ }
+ else if (nloc->flags & NLOCF_HTTPGATEWAY)
+ {
+ if (!NetlibInitHttpConnection(nlc, nlu, nloc)) return false;
+ nlc->usingDirectHttpGateway = true;
+ }
+
+ NetlibLogf(nlu,"(%d) Connected to %s:%d", nlc->s, nloc->szHost, nloc->wPort);
+
+ if (NLOCF_SSL & nloc->flags)
+ {
+ return NetlibStartSsl((WPARAM)nlc, 0) != 0;
+ }
+
+ return true;
+}
+
+bool NetlibReconnect(NetlibConnection *nlc)
+{
+ 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)
+ NetlibDoClose(nlc, true);
+ }
+
+ if (!opened)
+ {
+ if (Miranda_Terminated()) return false;
+ if (nlc->usingHttpGateway)
+ {
+ nlc->proxyAuthNeeded = true;
+ return my_connect(nlc, &nlc->nloc);
+ }
+ else
+ return NetlibDoConnect(nlc);
+ }
+ return true;
+}
+
+INT_PTR NetlibOpenConnection(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBOPENCONNECTION *nloc = (NETLIBOPENCONNECTION*)lParam;
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibConnection *nlc;
+
+ NetlibLogf(nlu,"Connection request to %s:%d (Flags %x)....", nloc->szHost, nloc->wPort, nloc->flags);
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || nloc == NULL ||
+ (nloc->cbSize != NETLIBOPENCONNECTION_V1_SIZE && nloc->cbSize != sizeof(NETLIBOPENCONNECTION)) ||
+ nloc->szHost == NULL || nloc->wPort == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ nlc = (struct NetlibConnection*)mir_calloc(sizeof(struct NetlibConnection));
+ nlc->handleType = NLH_CONNECTION;
+ nlc->nlu = nlu;
+ nlc->nloc = *nloc;
+ nlc->nloc.szHost = mir_strdup(nloc->szHost);
+ nlc->s = INVALID_SOCKET;
+ nlc->s2 = INVALID_SOCKET;
+ nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0;
+
+ InitializeCriticalSection(&nlc->csHttpSequenceNums);
+ nlc->hOkToCloseEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
+ nlc->dontCloseNow = 0;
+ NetlibInitializeNestedCS(&nlc->ncsSend);
+ NetlibInitializeNestedCS(&nlc->ncsRecv);
+
+ if (!NetlibDoConnect(nlc))
+ {
+ FreePartiallyInitedConnection(nlc);
+ return 0;
+ }
+
+ if (iUPnPCleanup == 0)
+ {
+ EnterCriticalSection(&csNetlibUser);
+ if (iUPnPCleanup == 0)
+ {
+ iUPnPCleanup = 1;
+ forkthread(NetlibUPnPCleanup, 0, NULL);
+ }
+ LeaveCriticalSection(&csNetlibUser);
+ }
+
+ return (INT_PTR)nlc;
+}
+
+INT_PTR NetlibStartSsl(WPARAM wParam, LPARAM lParam)
+{
+ NetlibConnection *nlc = (NetlibConnection*)wParam;
+ if (nlc == NULL) return 0;
+
+ NETLIBSSL *sp = (NETLIBSSL*)lParam;
+ const char *szHost = sp ? sp->host : nlc->nloc.szHost;
+
+ NetlibLogf(nlc->nlu, "(%d %s) Starting SSL negotiation", nlc->s, szHost);
+ nlc->hSsl = si.connect(nlc->s, szHost, nlc->nlu->settings.validateSSL);
+
+ if (nlc->hSsl == NULL)
+ NetlibLogf(nlc->nlu,"(%d %s) Failure to negotiate SSL connection", nlc->s, szHost);
+ else
+ NetlibLogf(nlc->nlu, "(%d %s) SSL negotiation successful", nlc->s, szHost);
+
+ return nlc->hSsl != NULL;
+}
diff --git a/src/modules/netlib/netlibopts.cpp b/src/modules/netlib/netlibopts.cpp new file mode 100644 index 0000000000..0c562a5cff --- /dev/null +++ b/src/modules/netlib/netlibopts.cpp @@ -0,0 +1,556 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+struct NetlibTempSettings
+{
+ DWORD flags;
+ char *szSettingsModule;
+ NETLIBUSERSETTINGS settings;
+};
+
+static LIST <NetlibTempSettings> 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 TCHAR* szProxyTypes[]={_T("<mixed>"),_T("SOCKS4"),_T("SOCKS5"),_T("HTTP"),_T("HTTPS"),_T("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)
+{
+ int i;
+ for(i=0;i<cControls;i++) ShowWindow(GetDlgItem(hwndDlg,controls[i]),state);
+}
+
+static void EnableMultipleControls(HWND hwndDlg,const UINT *controls,int cControls,int state)
+{
+ int i;
+ for(i=0;i<cControls;i++) EnableWindow(GetDlgItem(hwndDlg,controls[i]),state);
+}
+
+static void AddProxyTypeItem(HWND hwndDlg,int type,int selectType)
+{
+ int i;
+ i = SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_ADDSTRING,0,(LPARAM)(type==0?TranslateTS(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,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!=NULL && (*source==NULL || lstrcmpiA(*dest,*source))) {mir_free(*dest); *dest=NULL;}
+}
+
+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,i;
+
+ newValue=IsDlgButtonChecked(hwndDlg,ctrlId)!=BST_CHECKED;
+ CheckDlgButton(hwndDlg,ctrlId,newValue?BST_CHECKED:BST_UNCHECKED);
+ if (iUser == -1)
+ {
+ for (i=0; i<tempSettings.getCount(); i++)
+ if (!(tempSettings[i]->flags & NUF_NOOPTIONS))
+ *(int*)(((PBYTE)&tempSettings[i]->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 i,newValueLen;
+ char *szNewValue,**ppszNew;
+
+ newValueLen=GetWindowTextLength(GetDlgItem(hwndDlg,ctrlId));
+ szNewValue=(char*)mir_alloc(newValueLen+1);
+ GetDlgItemTextA(hwndDlg,ctrlId,szNewValue,newValueLen+1);
+ if (iUser == -1)
+ {
+ for (i=0; i<tempSettings.getCount(); ++i)
+ if (!(tempSettings[i]->flags & NUF_NOOPTIONS))
+ {
+ ppszNew=(char**)(((PBYTE)&tempSettings[i]->settings)+memberOffset);
+ if(*ppszNew) mir_free(*ppszNew);
+ *ppszNew=mir_strdup(szNewValue);
+ }
+ mir_free(szNewValue);
+ }
+ else {
+ ppszNew=(char**)(((PBYTE)&tempSettings[iUser]->settings)+memberOffset);
+ if(*ppszNew) mir_free(*ppszNew);
+ *ppszNew=szNewValue;
+ }
+}
+
+static void WriteSettingsStructToDb(const char *szSettingsModule,NETLIBUSERSETTINGS *settings,DWORD flags)
+{
+ if(flags&NUF_OUTGOING) {
+ char szEncodedPassword[512];
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLValidateSSL",(BYTE)settings->validateSSL);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLUseProxy",(BYTE)settings->useProxy);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLProxyType",(BYTE)settings->proxyType);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyServer",settings->szProxyServer?settings->szProxyServer:"");
+ DBWriteContactSettingWord(NULL,szSettingsModule,"NLProxyPort",(WORD)settings->wProxyPort);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLUseProxyAuth",(BYTE)settings->useProxyAuth);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyAuthUser",settings->szProxyAuthUser?settings->szProxyAuthUser:"");
+ lstrcpynA(szEncodedPassword,settings->szProxyAuthPassword?settings->szProxyAuthPassword:"",SIZEOF(szEncodedPassword));
+ CallService(MS_DB_CRYPT_ENCODESTRING,SIZEOF(szEncodedPassword),(LPARAM)szEncodedPassword);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyAuthPassword",szEncodedPassword);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLDnsThroughProxy",(BYTE)settings->dnsThroughProxy);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLSpecifyOutgoingPorts",(BYTE)settings->specifyOutgoingPorts);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLOutgoingPorts",settings->szOutgoingPorts?settings->szOutgoingPorts:"");
+ }
+ if(flags&NUF_INCOMING) {
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLEnableUPnP",(BYTE)settings->enableUPnP);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLSpecifyIncomingPorts",(BYTE)settings->specifyIncomingPorts);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLIncomingPorts",settings->szIncomingPorts?settings->szIncomingPorts:"");
+ }
+}
+
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule,NETLIBUSERSETTINGS *settings)
+{
+ int i;
+ NETLIBUSERSETTINGS combinedSettings={0};
+ DWORD flags;
+
+ EnterCriticalSection(&csNetlibUser);
+
+ NetlibUser *thisUser, tUser;
+ tUser.user.szSettingsModule = (char*)szSettingsModule;
+ thisUser = netlibUser.find(&tUser);
+
+ if (thisUser == NULL)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ return;
+ }
+
+ NetlibFreeUserSettingsStruct(&thisUser->settings);
+ CopySettingsStruct(&thisUser->settings, settings);
+ WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags);
+ combinedSettings.cbSize = sizeof(combinedSettings);
+ for (i=0, flags=0; i < netlibUser.getCount(); ++i)
+ {
+ if (thisUser->user.flags & NUF_NOOPTIONS) continue;
+ CombineSettingsStructs(&combinedSettings, &flags, &thisUser->settings, thisUser->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);
+ LeaveCriticalSection(&csNetlibUser);
+}
+
+static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ { int iUser,iItem;
+
+ TranslateDialogDefault(hwndDlg);
+ iItem=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_ADDSTRING,0,(LPARAM)TranslateT("<All connections>"));
+ SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETITEMDATA,iItem,(LPARAM)-1);
+ SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETCURSEL,iItem,0);
+
+ EnterCriticalSection(&csNetlibUser);
+ for (iUser = 0; iUser < netlibUser.getCount(); ++iUser)
+ {
+ NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings));
+ thisSettings->flags = netlibUser[iUser]->user.flags;
+ thisSettings->szSettingsModule = mir_strdup(netlibUser[iUser]->user.szSettingsModule);
+ CopySettingsStruct(&thisSettings->settings, &netlibUser[iUser]->settings);
+ tempSettings.insert(thisSettings);
+
+ if (netlibUser[iUser]->user.flags & NUF_NOOPTIONS) continue;
+ iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0,
+ (LPARAM)netlibUser[iUser]->user.ptszDescriptiveName);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS,CB_SETITEMDATA, iItem, iUser);
+ }
+ LeaveCriticalSection(&csNetlibUser);
+
+ SendMessage(hwndDlg,M_REFRESHALL,0,0);
+ return TRUE;
+ }
+ case M_REFRESHALL:
+ { int iUser=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETCURSEL,0,0),0);
+ NETLIBUSERSETTINGS settings = {0};
+ DWORD flags;
+
+ if (iUser == -1)
+ {
+ int i;
+ settings.cbSize=sizeof(settings);
+ for (i = 0, flags = 0; i < tempSettings.getCount(); ++i)
+ {
+ if (tempSettings[i]->flags & NUF_NOOPTIONS) continue;
+ CombineSettingsStructs(&settings, &flags, &tempSettings[i]->settings, tempSettings[i]->flags);
+ }
+ }
+ else
+ {
+ NetlibFreeUserSettingsStruct(&settings);
+ CopySettingsStruct(&settings, &tempSettings[iUser]->settings);
+ flags = tempSettings[iUser]->flags;
+ }
+ ShowMultipleControls(hwndDlg,outgoingConnectionsControls,SIZEOF(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 | NUF_HTTPGATEWAY)) AddProxyTypeItem(hwndDlg,PROXYTYPE_HTTP,settings.proxyType);
+ if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg,PROXYTYPE_HTTPS,settings.proxyType);
+ if (flags & (NUF_HTTPCONNS | NUF_HTTPGATEWAY) || !(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,SIZEOF(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:
+ { int selectedProxyType;
+ TCHAR str[80];
+
+ selectedProxyType=SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_GETCURSEL,0,0),0);
+ mir_sntprintf(str, SIZEOF(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, SIZEOF(useProxyControls), TRUE);
+ if (selectedProxyType == 0)
+ {
+ int i;
+ for (i = 0; i < tempSettings.getCount(); ++i)
+ {
+ if (!tempSettings[i]->settings.useProxy ||
+ tempSettings[i]->flags & NUF_NOOPTIONS || !(tempSettings[i]->flags & NUF_OUTGOING))
+ continue;
+
+ if (tempSettings[i]->settings.proxyType==PROXYTYPE_SOCKS4) enableUser=1;
+ else
+ {
+ enableAuth=1;
+ if (tempSettings[i]->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,SIZEOF(useProxyControls),FALSE);
+ EnableMultipleControls(hwndDlg,specifyPortsControls,SIZEOF(specifyPortsControls),IsDlgButtonChecked(hwndDlg,IDC_SPECIFYPORTS)!=BST_UNCHECKED);
+ EnableMultipleControls(hwndDlg,specifyOPortsControls,SIZEOF(specifyOPortsControls),IsDlgButtonChecked(hwndDlg,IDC_SPECIFYPORTSO)!=BST_UNCHECKED);
+ break;
+ }
+ case WM_COMMAND:
+ { int 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) return 0;
+ { int newValue,i;
+ 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 (i = 0; i < tempSettings.getCount(); ++i)
+ {
+ if (tempSettings[i]->flags & NUF_NOOPTIONS) continue;
+ if (newValue == PROXYTYPE_HTTP && !(tempSettings[i]->flags & (NUF_HTTPCONNS|NUF_HTTPGATEWAY)))
+ tempSettings[i]->settings.proxyType = PROXYTYPE_HTTPS;
+ else if (newValue == PROXYTYPE_HTTPS && tempSettings[i]->flags & NUF_NOHTTPSOPTION)
+ tempSettings[i]->settings.proxyType = PROXYTYPE_HTTP;
+ else tempSettings[i]->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,i;
+ newValue=GetDlgItemInt(hwndDlg,LOWORD(wParam),NULL,FALSE);
+ if (iUser == -1)
+ {
+ for (i = 0; i < tempSettings.getCount(); ++i)
+ if (!(tempSettings[i]->flags & NUF_NOOPTIONS))
+ tempSettings[i]->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:
+ { int iUser;
+ for (iUser = 0; iUser < tempSettings.getCount(); iUser++)
+ NetlibSaveUserSettingsStruct(tempSettings[iUser]->szSettingsModule,
+ &tempSettings[iUser]->settings);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ { int iUser;
+ for (iUser = 0; iUser < tempSettings.getCount(); ++iUser)
+ {
+ mir_free(tempSettings[iUser]->szSettingsModule);
+ NetlibFreeUserSettingsStruct(&tempSettings[iUser]->settings);
+ mir_free(tempSettings[iUser]);
+ }
+ tempSettings.destroy();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static UINT expertOnlyControls[]={IDC_LOGOPTIONS};
+int NetlibOptInitialise(WPARAM wParam,LPARAM)
+{
+ int optionsCount = 0;
+ EnterCriticalSection(&csNetlibUser);
+ for (int i = 0; i < netlibUser.getCount(); ++i)
+ if (!(netlibUser[i]->user.flags & NUF_NOOPTIONS)) ++optionsCount;
+ LeaveCriticalSection(&csNetlibUser);
+ if (optionsCount == 0) return 0;
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 900000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB);
+ odp.pszTitle = LPGEN("Network");
+ odp.pfnDlgProc = DlgProcNetlibOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.expertOnlyControls = expertOnlyControls;
+ odp.nExpertOnlyControls = SIZEOF( expertOnlyControls );
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
diff --git a/src/modules/netlib/netlibpktrecver.cpp b/src/modules/netlib/netlibpktrecver.cpp new file mode 100644 index 0000000000..9722324a4f --- /dev/null +++ b/src/modules/netlib/netlibpktrecver.cpp @@ -0,0 +1,85 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+INT_PTR NetlibPacketRecverCreate(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ struct NetlibPacketRecver *nlpr;
+
+ if(GetNetlibHandleType(nlc)!=NLH_CONNECTION || lParam==0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (INT_PTR)(struct NetlibPacketRecver*)NULL;
+ }
+ nlpr=(struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver));
+ if(nlpr==NULL) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return (INT_PTR)(struct NetlibPacketRecver*)NULL;
+ }
+ nlpr->handleType=NLH_PACKETRECVER;
+ nlpr->nlc=nlc;
+ nlpr->packetRecver.cbSize=sizeof(nlpr->packetRecver);
+ nlpr->packetRecver.bufferSize=lParam;
+ nlpr->packetRecver.buffer=(PBYTE)mir_alloc(nlpr->packetRecver.bufferSize);
+ nlpr->packetRecver.bytesUsed=0;
+ nlpr->packetRecver.bytesAvailable=0;
+ return (INT_PTR)nlpr;
+}
+
+INT_PTR NetlibPacketRecverGetMore(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibPacketRecver *nlpr=(struct NetlibPacketRecver*)wParam;
+ NETLIBPACKETRECVER *nlprParam=(NETLIBPACKETRECVER*)lParam;
+ INT_PTR recvResult;
+
+ if(GetNetlibHandleType(nlpr)!=NLH_PACKETRECVER || nlprParam==NULL || nlprParam->cbSize!=sizeof(NETLIBPACKETRECVER) || nlprParam->bytesUsed>nlpr->packetRecver.bytesAvailable) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+ if (Miranda_Terminated()) { /* 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;
+ NetlibLogf(nlpr->nlc->nlu,"Packet recver: packet overflowed buffer, ditching");
+ }
+ }
+ else {
+ MoveMemory(nlpr->packetRecver.buffer,nlpr->packetRecver.buffer+nlprParam->bytesUsed,nlpr->packetRecver.bytesAvailable-nlprParam->bytesUsed);
+ nlpr->packetRecver.bytesAvailable-=nlprParam->bytesUsed;
+ }
+ if(nlprParam->dwTimeout!=INFINITE) {
+ if(!si.pending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s,nlprParam->dwTimeout) <= 0) {
+ *nlprParam=nlpr->packetRecver;
+ return SOCKET_ERROR;
+ }
+ }
+ recvResult=NLRecv(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/modules/netlib/netlibsecurity.cpp b/src/modules/netlib/netlibsecurity.cpp new file mode 100644 index 0000000000..c20dcf10a1 --- /dev/null +++ b/src/modules/netlib/netlibsecurity.cpp @@ -0,0 +1,570 @@ +/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <rpcdce.h>
+
+static HMODULE g_hSecurity = NULL;
+static PSecurityFunctionTable g_pSSPI = NULL;
+
+typedef struct
+{
+ CtxtHandle hClientContext;
+ CredHandle hClientCredential;
+ TCHAR* szProvider;
+ TCHAR* szPrincipal;
+ unsigned cbMaxToken;
+ bool hasDomain;
+}
+ NtlmHandleType;
+
+typedef struct
+{
+ WORD len;
+ WORD allocedSpace;
+ DWORD offset;
+}
+ NTLM_String;
+
+typedef struct
+{
+ char sign[8];
+ DWORD type; // == 2
+ NTLM_String targetName;
+ DWORD flags;
+ BYTE challenge[8];
+ BYTE context[8];
+ NTLM_String targetInfo;
+}
+ NtlmType2packet;
+
+static unsigned secCnt = 0, ntlmCnt = 0;
+static HANDLE hSecMutex;
+
+static void ReportSecError(SECURITY_STATUS scRet, int line)
+{
+ char szMsgBuf[256];
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, scRet, LANG_USER_DEFAULT, szMsgBuf, SIZEOF(szMsgBuf), NULL);
+
+ char *p = strchr(szMsgBuf, 13); if (p) *p = 0;
+
+ NetlibLogf(NULL, "Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf);
+}
+
+static void LoadSecurityLibrary(void)
+{
+ INIT_SECURITY_INTERFACE pInitSecurityInterface;
+
+ g_hSecurity = LoadLibraryA("secur32.dll");
+ if (g_hSecurity == NULL)
+ g_hSecurity = LoadLibraryA("security.dll");
+
+ if (g_hSecurity == NULL)
+ return;
+
+ pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(g_hSecurity, SECURITY_ENTRYPOINT_ANSI);
+ if (pInitSecurityInterface != NULL)
+ {
+ g_pSSPI = pInitSecurityInterface();
+ }
+
+ if (g_pSSPI == NULL)
+ {
+ FreeLibrary(g_hSecurity);
+ g_hSecurity = NULL;
+ }
+}
+
+static void FreeSecurityLibrary(void)
+{
+ FreeLibrary(g_hSecurity);
+ g_hSecurity = NULL;
+ g_pSSPI = NULL;
+}
+
+HANDLE NetlibInitSecurityProvider(const TCHAR* szProvider, const TCHAR* szPrincipal)
+{
+ HANDLE hSecurity = NULL;
+
+ if (_tcsicmp(szProvider, _T("Basic")) == 0)
+ {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->szProvider = mir_tstrdup(szProvider);
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+
+ return hNtlm;
+ }
+
+ WaitForSingleObject(hSecMutex, INFINITE);
+
+ if (secCnt == 0 )
+ {
+ LoadSecurityLibrary();
+ secCnt += g_hSecurity != NULL;
+ }
+ else secCnt++;
+
+ if (g_pSSPI != NULL)
+ {
+ PSecPkgInfo ntlmSecurityPackageInfo;
+ bool isGSSAPI = _tcsicmp(szProvider, _T("GSSAPI")) == 0;
+ const TCHAR *szProviderC = isGSSAPI ? _T("Kerberos") : szProvider;
+ SECURITY_STATUS sc = g_pSSPI->QuerySecurityPackageInfo((LPTSTR)szProviderC, &ntlmSecurityPackageInfo);
+ if (sc == SEC_E_OK)
+ {
+ NtlmHandleType* hNtlm;
+
+ hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken;
+ g_pSSPI->FreeContextBuffer(ntlmSecurityPackageInfo);
+
+ hNtlm->szProvider = mir_tstrdup(szProvider);
+ hNtlm->szPrincipal = mir_tstrdup(szPrincipal ? szPrincipal : _T(""));
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+ }
+ }
+
+ ReleaseMutex(hSecMutex);
+ return hSecurity;
+}
+
+#ifdef UNICODE
+HANDLE NetlibInitSecurityProvider(const char* szProvider, const char* szPrincipal)
+{
+ return NetlibInitSecurityProvider(StrConvT(szProvider), StrConvT(szPrincipal));
+}
+#endif
+
+void NetlibDestroySecurityProvider(HANDLE hSecurity)
+{
+ if (hSecurity == NULL) return;
+
+ WaitForSingleObject(hSecMutex, INFINITE);
+
+ if (ntlmCnt != 0)
+ {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ if (SecIsValidHandle(&hNtlm->hClientContext)) g_pSSPI->DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential)) g_pSSPI->FreeCredentialsHandle(&hNtlm->hClientCredential);
+ mir_free(hNtlm->szProvider);
+ mir_free(hNtlm->szPrincipal);
+
+ --ntlmCnt;
+
+ mir_free(hNtlm);
+ }
+
+ if (secCnt && --secCnt == 0)
+ FreeSecurityLibrary();
+
+ ReleaseMutex(hSecMutex);
+}
+
+char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz)
+{
+ if (!szChallenge || !szChallenge[0]) return NULL;
+
+ 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 = g_pSSPI->DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ unsigned char LayerMask = inDataBuffer[0];
+ unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]);
+
+ SecPkgContext_Sizes sizes;
+ sc = g_pSSPI->QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ 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 = g_pSSPI->EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ 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;
+ }
+
+ NETLIBBASE64 nlb64;
+ nlb64.cbDecoded = ressz;
+ nlb64.pbDecoded = response;
+ nlb64.cchEncoded = Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded);
+ nlb64.pszEncoded = (char*)alloca(nlb64.cchEncoded);
+ if (!NetlibBase64Encode(0,(LPARAM)&nlb64)) return NULL;
+
+ return mir_strdup(nlb64.pszEncoded);
+}
+
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const TCHAR* login, const TCHAR* psw,
+ bool http, unsigned& complete)
+{
+ SECURITY_STATUS sc;
+ SecBufferDesc outputBufferDescriptor,inputBufferDescriptor;
+ SecBuffer outputSecurityToken,inputSecurityToken;
+ TimeStamp tokenExpiration;
+ ULONG contextAttributes;
+ NETLIBBASE64 nlb64 = { 0 };
+
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+
+ if (hSecurity == NULL || ntlmCnt == 0) return NULL;
+
+ if (_tcsicmp(hNtlm->szProvider, _T("Basic")))
+ {
+ bool isGSSAPI = _tcsicmp(hNtlm->szProvider, _T("GSSAPI")) == 0;
+ TCHAR *szProvider = isGSSAPI ? _T("Kerberos") : hNtlm->szProvider;
+ bool hasChallenge = szChallenge != NULL && szChallenge[0] != '\0';
+ if (hasChallenge)
+ {
+ nlb64.cchEncoded = lstrlenA(szChallenge);
+ nlb64.pszEncoded = (char*)szChallenge;
+ nlb64.cbDecoded = Netlib_GetBase64DecodedBufferSize(nlb64.cchEncoded);
+ nlb64.pbDecoded = (PBYTE)alloca(nlb64.cbDecoded);
+ if (!NetlibBase64Decode(0, (LPARAM)&nlb64)) return NULL;
+
+ if (isGSSAPI && complete)
+ return CompleteGssapi(hSecurity, nlb64.pbDecoded, nlb64.cbDecoded);
+
+ inputBufferDescriptor.cBuffers = 1;
+ inputBufferDescriptor.pBuffers = &inputSecurityToken;
+ inputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ inputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ inputSecurityToken.cbBuffer = nlb64.cbDecoded;
+ inputSecurityToken.pvBuffer = nlb64.pbDecoded;
+
+ // try to decode the domain name from the NTLM challenge
+ if (login != NULL && login[0] != '\0' && !hNtlm->hasDomain)
+ {
+ NtlmType2packet* pkt = ( NtlmType2packet* )nlb64.pbDecoded;
+ if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2)
+ {
+#ifdef UNICODE
+ wchar_t* domainName = (wchar_t*)&nlb64.pbDecoded[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, NULL, 0);
+ wchar_t* buf = (wchar_t*)alloca(bufsz * sizeof(wchar_t));
+ domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1;
+ domainName = buf;
+ }
+ else
+ domainLen /= sizeof(wchar_t);
+#else
+ char* domainName = (char*)&nlb64.pbDecoded[pkt->targetName.offset];
+ int domainLen = pkt->targetName.len;
+
+ // Negotiate Unicode? if yes, convert the unicode name to ANSI
+ if (pkt->flags & 1)
+ {
+ int bufsz = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)domainName, domainLen, NULL, 0, NULL, NULL);
+ char* buf = (char*)alloca(bufsz);
+ domainLen = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)domainName, domainLen, buf, bufsz, NULL, NULL) - 1;
+ domainName = buf;
+ }
+#endif
+
+ if (domainLen)
+ {
+ size_t newLoginLen = _tcslen(login) + domainLen + 1;
+ TCHAR *newLogin = (TCHAR*)alloca(newLoginLen * sizeof(TCHAR));
+
+ _tcsncpy(newLogin, domainName, domainLen);
+ newLogin[domainLen] = '\\';
+ _tcscpy(newLogin + domainLen + 1, login);
+
+ char* szChl = NtlmCreateResponseFromChallenge(hSecurity, NULL, newLogin, psw, http, complete);
+ mir_free(szChl);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (SecIsValidHandle(&hNtlm->hClientContext)) g_pSSPI->DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential)) g_pSSPI->FreeCredentialsHandle(&hNtlm->hClientCredential);
+
+ SEC_WINNT_AUTH_IDENTITY auth;
+
+ if (login != NULL && login[0] != '\0')
+ {
+ memset(&auth, 0, sizeof(auth));
+#ifdef _UNICODE
+ NetlibLogf(NULL, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)");
+#else
+ NetlibLogf(NULL, "Security login requested, user: %s pssw: %s", login, psw ? "(exist)" : "(no psw)");
+#endif
+
+ const TCHAR* loginName = login;
+ const TCHAR* domainName = _tcschr(login, '\\');
+ int domainLen = 0;
+ int loginLen = lstrlen(loginName);
+ if (domainName != NULL)
+ {
+ loginName = domainName + 1;
+ loginLen = lstrlen(loginName);
+ domainLen = domainName - login;
+ domainName = login;
+ }
+ else if ((domainName = _tcschr(login, '@')) != NULL)
+ {
+ loginName = login;
+ loginLen = domainName - login;
+ domainLen = lstrlen(++domainName);
+ }
+
+#ifdef UNICODE
+ auth.User = (PWORD)loginName;
+ auth.UserLength = loginLen;
+ auth.Password = (PWORD)psw;
+ auth.PasswordLength = lstrlen(psw);
+ auth.Domain = (PWORD)domainName;
+ auth.DomainLength = domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else
+ auth.User = (PBYTE)loginName;
+ auth.UserLength = loginLen;
+ auth.Password = (PBYTE)psw;
+ auth.PasswordLength = lstrlen(psw);
+ auth.Domain = (PBYTE)domainName;
+ auth.DomainLength = domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+
+ hNtlm->hasDomain = domainLen != 0;
+ }
+
+ sc = g_pSSPI->AcquireCredentialsHandle(NULL, szProvider,
+ SECPKG_CRED_OUTBOUND, NULL, hNtlm->hasDomain ? &auth : NULL, NULL, NULL,
+ &hNtlm->hClientCredential, &tokenExpiration);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+ }
+
+ outputBufferDescriptor.cBuffers = 1;
+ outputBufferDescriptor.pBuffers = &outputSecurityToken;
+ outputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ outputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ outputSecurityToken.cbBuffer = hNtlm->cbMaxToken;
+ outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer);
+
+ sc = g_pSSPI->InitializeSecurityContext(&hNtlm->hClientCredential,
+ hasChallenge ? &hNtlm->hClientContext : NULL,
+ hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP,
+ hasChallenge ? &inputBufferDescriptor : NULL, 0, &hNtlm->hClientContext,
+ &outputBufferDescriptor, &contextAttributes, &tokenExpiration);
+
+ 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 = g_pSSPI->CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor);
+ }
+
+ if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ nlb64.cbDecoded = outputSecurityToken.cbBuffer;
+ nlb64.pbDecoded = (PBYTE)outputSecurityToken.pvBuffer;
+ }
+ else
+ {
+ if (!login || !psw) return NULL;
+
+ char *szLogin = mir_t2a(login);
+ char *szPassw = mir_t2a(psw);
+
+ size_t authLen = strlen(szLogin) + strlen(szPassw) + 5;
+ char *szAuth = (char*)alloca(authLen);
+
+ nlb64.cbDecoded = mir_snprintf(szAuth, authLen,"%s:%s", szLogin, szPassw);
+ nlb64.pbDecoded=(PBYTE)szAuth;
+ complete = true;
+
+ mir_free(szPassw);
+ mir_free(szLogin);
+ }
+
+ nlb64.cchEncoded = Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded);
+ nlb64.pszEncoded = (char*)alloca(nlb64.cchEncoded);
+ if (!NetlibBase64Encode(0,(LPARAM)&nlb64)) return NULL;
+
+ char* result;
+ if (http)
+ {
+ char* szProvider = mir_t2a(hNtlm->szProvider);
+ nlb64.cchEncoded += (int)strlen(szProvider) + 10;
+ result = (char*)mir_alloc(nlb64.cchEncoded);
+ mir_snprintf(result, nlb64.cchEncoded, "%s %s", szProvider, nlb64.pszEncoded);
+ mir_free(szProvider);
+ }
+ else
+ result = mir_strdup(nlb64.pszEncoded);
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR InitSecurityProviderService(WPARAM, LPARAM lParam)
+{
+ HANDLE hSecurity = NetlibInitSecurityProvider((char*)lParam, NULL);
+ return (INT_PTR)hSecurity;
+}
+
+static INT_PTR InitSecurityProviderService2(WPARAM, LPARAM lParam)
+{
+ NETLIBNTLMINIT2 *req = ( NETLIBNTLMINIT2* )lParam;
+ if (req->cbSize < sizeof(*req)) return 0;
+
+ HANDLE hSecurity;
+
+#ifdef UNICODE
+ if (req->flags & NNR_UNICODE)
+ hSecurity = NetlibInitSecurityProvider(req->szProviderName, req->szPrincipal);
+ else
+#endif
+ hSecurity = NetlibInitSecurityProvider((char*)req->szProviderName, (char*)req->szPrincipal);
+
+ return (INT_PTR)hSecurity;
+}
+
+static INT_PTR DestroySecurityProviderService( WPARAM, LPARAM lParam )
+{
+ NetlibDestroySecurityProvider(( HANDLE )lParam );
+ return 0;
+}
+
+static INT_PTR NtlmCreateResponseService( WPARAM wParam, LPARAM lParam )
+{
+ NETLIBNTLMREQUEST* req = ( NETLIBNTLMREQUEST* )lParam;
+ unsigned complete;
+
+ char* response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ StrConvT(req->userName), StrConvT(req->password), false, complete );
+
+ return (INT_PTR)response;
+}
+
+static INT_PTR NtlmCreateResponseService2( WPARAM wParam, LPARAM lParam )
+{
+ NETLIBNTLMREQUEST2* req = ( NETLIBNTLMREQUEST2* )lParam;
+ if (req->cbSize < sizeof(*req)) return 0;
+
+ char* response;
+
+#ifdef UNICODE
+ if (req->flags & NNR_UNICODE)
+ {
+ response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ req->szUserName, req->szPassword, false, req->complete );
+ }
+ else
+ {
+ TCHAR *szLogin = mir_a2t((char*)req->szUserName);
+ TCHAR *szPassw = mir_a2t((char*)req->szPassword);
+ response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ szLogin, szPassw, false, req->complete );
+ mir_free(szLogin);
+ mir_free(szPassw);
+ }
+#else
+ response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ req->szUserName, req->szPassword, false, req->complete );
+#endif
+
+ return (INT_PTR)response;
+}
+
+void NetlibSecurityInit(void)
+{
+ hSecMutex = CreateMutex(NULL, FALSE, NULL);
+
+ CreateServiceFunction( MS_NETLIB_INITSECURITYPROVIDER, InitSecurityProviderService );
+ CreateServiceFunction( MS_NETLIB_INITSECURITYPROVIDER2, InitSecurityProviderService2 );
+ CreateServiceFunction( MS_NETLIB_DESTROYSECURITYPROVIDER, DestroySecurityProviderService );
+ CreateServiceFunction( MS_NETLIB_NTLMCREATERESPONSE, NtlmCreateResponseService );
+ CreateServiceFunction( MS_NETLIB_NTLMCREATERESPONSE2, NtlmCreateResponseService2 );
+}
+
+void NetlibSecurityDestroy(void)
+{
+ CloseHandle(hSecMutex);
+}
\ No newline at end of file diff --git a/src/modules/netlib/netlibsock.cpp b/src/modules/netlib/netlibsock.cpp new file mode 100644 index 0000000000..875c47b0a1 --- /dev/null +++ b/src/modules/netlib/netlibsock.cpp @@ -0,0 +1,203 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include "netlib.h"
+
+extern HANDLE hConnectionHeaderMutex,hSendEvent,hRecvEvent;
+
+INT_PTR NetlibSend(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam;
+ INT_PTR result;
+
+ if ( nlb == NULL ) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if ( !NetlibEnterNestedCS( nlc, NLNCS_SEND ))
+ return SOCKET_ERROR;
+
+ if ( nlc->usingHttpGateway && !( nlb->flags & MSG_RAW )) {
+ if ( !( nlb->flags & MSG_NOHTTPGATEWAYWRAP ) && nlc->nlu->user.pfnHttpGatewayWrapSend ) {
+ NetlibDumpData( nlc, ( PBYTE )nlb->buf, nlb->len, 1, nlb->flags );
+ result = nlc->nlu->user.pfnHttpGatewayWrapSend(( HANDLE )nlc, ( PBYTE )nlb->buf, nlb->len, nlb->flags | MSG_NOHTTPGATEWAYWRAP, NetlibSend );
+ }
+ else result = NetlibHttpGatewayPost( nlc, nlb->buf, nlb->len, nlb->flags );
+ }
+ else {
+ NetlibDumpData( nlc, ( PBYTE )nlb->buf, nlb->len, 1, nlb->flags );
+ if (nlc->hSsl)
+ result = si.write( nlc->hSsl, nlb->buf, nlb->len );
+ else
+ result = send( nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF );
+ }
+ NetlibLeaveNestedCS( &nlc->ncsSend );
+
+ if ((( THook* )hSendEvent)->subscriberCount ) {
+ NETLIBNOTIFY nln = { nlb, result };
+ CallHookSubscribers( hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user );
+ }
+ return result;
+}
+
+INT_PTR NetlibRecv(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc = (struct NetlibConnection*)wParam;
+ NETLIBBUFFER* nlb = ( NETLIBBUFFER* )lParam;
+ int recvResult;
+
+ if ( nlb == NULL ) {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return SOCKET_ERROR;
+ }
+
+ if ( !NetlibEnterNestedCS( nlc, NLNCS_RECV ))
+ return SOCKET_ERROR;
+
+ if ( nlc->usingHttpGateway && !( nlb->flags & MSG_RAW ))
+ recvResult = NetlibHttpGatewayRecv( nlc, nlb->buf, nlb->len, nlb->flags );
+ else
+ {
+ if (nlc->hSsl)
+ recvResult = si.read( nlc->hSsl, nlb->buf, nlb->len, (nlb->flags & MSG_PEEK) != 0 );
+ else
+ recvResult = recv( nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF );
+ }
+ NetlibLeaveNestedCS( &nlc->ncsRecv );
+ if (recvResult <= 0)
+ return recvResult;
+
+ NetlibDumpData(nlc, (PBYTE)nlb->buf, recvResult, 0, nlb->flags);
+
+ if ((nlb->flags & MSG_PEEK) == 0 && ((THook*)hRecvEvent)->subscriberCount)
+ {
+ NETLIBNOTIFY nln = { nlb, recvResult };
+ CallHookSubscribers(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+ }
+ return recvResult;
+}
+
+static int ConnectionListToSocketList(HANDLE *hConns, fd_set *fd, int& pending)
+{
+ struct NetlibConnection *nlcCheck;
+ int i;
+
+ FD_ZERO(fd);
+ for(i=0;hConns[i] && hConns[i]!=INVALID_HANDLE_VALUE && i<FD_SETSIZE;i++) {
+ nlcCheck=(struct NetlibConnection*)hConns[i];
+ if (nlcCheck->handleType!=NLH_CONNECTION && nlcCheck->handleType!=NLH_BOUNDPORT) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ FD_SET(nlcCheck->s,fd);
+ if ( si.pending( nlcCheck->hSsl ))
+ pending++;
+ }
+ return 1;
+}
+
+INT_PTR NetlibSelect(WPARAM,LPARAM lParam)
+{
+ NETLIBSELECT *nls=(NETLIBSELECT*)lParam;
+ if (nls==NULL || nls->cbSize!=sizeof(NETLIBSELECT)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ TIMEVAL tv;
+ tv.tv_sec=nls->dwTimeout/1000;
+ tv.tv_usec=(nls->dwTimeout%1000)*1000;
+
+ 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;
+
+ return select(0,&readfd,&writefd,&exceptfd,nls->dwTimeout==INFINITE?NULL:&tv);
+}
+
+INT_PTR NetlibSelectEx(WPARAM, LPARAM lParam)
+{
+ NETLIBSELECTEX *nls=(NETLIBSELECTEX*)lParam;
+ if (nls==NULL || nls->cbSize!=sizeof(NETLIBSELECTEX)) {
+ 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?NULL:&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 */
+ struct NetlibConnection *conn=NULL;
+ int j;
+ for (j=0; j<FD_SETSIZE; j++) {
+ conn=(struct NetlibConnection*)nls->hReadConns[j];
+ if (conn==NULL || conn==INVALID_HANDLE_VALUE) break;
+
+ if (si.pending(conn->hSsl))
+ nls->hReadStatus[j] = TRUE;
+ if (conn->usingHttpGateway && conn->nlhpi.szHttpGetUrl == NULL && conn->dataBuffer == NULL)
+ nls->hReadStatus[j] = (conn->pHttpProxyPacketQueue != NULL);
+ else
+ nls->hReadStatus[j] = FD_ISSET(conn->s,&readfd);
+ }
+ for (j=0; j<FD_SETSIZE; j++) {
+ conn=(struct NetlibConnection*)nls->hWriteConns[j];
+ if (conn==NULL || conn==INVALID_HANDLE_VALUE) break;
+ nls->hWriteStatus[j] = FD_ISSET(conn->s,&writefd);
+ }
+ for (j=0; j<FD_SETSIZE; j++) {
+ conn=(struct NetlibConnection*)nls->hExceptConns[j];
+ if (conn==NULL || conn==INVALID_HANDLE_VALUE) break;
+ nls->hExceptStatus[j] = FD_ISSET(conn->s,&exceptfd);
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ return rc;
+}
diff --git a/src/modules/netlib/netlibssl.cpp b/src/modules/netlib/netlibssl.cpp new file mode 100644 index 0000000000..2db769a3da --- /dev/null +++ b/src/modules/netlib/netlibssl.cpp @@ -0,0 +1,981 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/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 "commonheaders.h"
+#include <m_popup.h>
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <schannel.h>
+
+//#include <SCHNLSP.H>
+
+typedef BOOL (* SSL_EMPTY_CACHE_FN_M)(VOID);
+
+static HMODULE g_hSchannel;
+static PSecurityFunctionTableA g_pSSPI;
+static HANDLE g_hSslMutex;
+static SSL_EMPTY_CACHE_FN_M MySslEmptyCache;
+static CredHandle hCreds;
+static bool bSslInitDone;
+
+typedef BOOL (WINAPI *pfnCertGetCertificateChain)(HCERTCHAINENGINE, PCCERT_CONTEXT, LPFILETIME, HCERTSTORE, PCERT_CHAIN_PARA, DWORD, LPVOID, PCCERT_CHAIN_CONTEXT*);
+static pfnCertGetCertificateChain fnCertGetCertificateChain;
+
+typedef VOID (WINAPI *pfnCertFreeCertificateChain)(PCCERT_CHAIN_CONTEXT);
+static pfnCertFreeCertificateChain fnCertFreeCertificateChain;
+
+typedef BOOL (WINAPI *pfnCertFreeCertificateContext)(PCCERT_CONTEXT);
+static pfnCertFreeCertificateContext fnCertFreeCertificateContext;
+
+typedef BOOL (WINAPI *pfnCertVerifyCertificateChainPolicy)(LPCSTR, PCCERT_CHAIN_CONTEXT, PCERT_CHAIN_POLICY_PARA, PCERT_CHAIN_POLICY_STATUS);
+static pfnCertVerifyCertificateChainPolicy fnCertVerifyCertificateChainPolicy;
+
+typedef enum
+{
+ sockOpen,
+ sockClosed,
+ sockError
+} SocketState;
+
+
+struct SslHandle
+{
+ SOCKET s;
+
+ CtxtHandle hContext;
+
+ BYTE *pbRecDataBuf;
+ int cbRecDataBuf;
+ int sbRecDataBuf;
+
+ BYTE *pbIoBuffer;
+ int cbIoBuffer;
+ int sbIoBuffer;
+
+ SocketState state;
+};
+
+static void ReportSslError(SECURITY_STATUS scRet, int line, bool showPopup = false)
+{
+ TCHAR szMsgBuf[256];
+ switch (scRet)
+ {
+ case 0:
+ case ERROR_NOT_READY:
+ return;
+
+ case SEC_E_INVALID_TOKEN:
+ _tcscpy(szMsgBuf, TranslateT("Client cannot decode host message. Possible causes: Host does not support SSL or requires not existing security package"));
+ break;
+
+ case CERT_E_CN_NO_MATCH:
+ case SEC_E_WRONG_PRINCIPAL:
+ _tcscpy(szMsgBuf, TranslateT("Host we are connecting to is not the one certificate was issued for"));
+ break;
+
+ default:
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, scRet, LANG_USER_DEFAULT, szMsgBuf, SIZEOF(szMsgBuf), NULL);
+ }
+
+ TCHAR szMsgBuf2[512];
+ mir_sntprintf(szMsgBuf2, SIZEOF(szMsgBuf2), _T("SSL connection failure (%x %u): %s"), scRet, line, szMsgBuf);
+
+ char* szMsg = Utf8EncodeT(szMsgBuf2);
+ NetlibLogf(NULL, szMsg);
+ mir_free(szMsg);
+
+ SetLastError(scRet);
+ PUShowMessageT(szMsgBuf2, SM_WARNING);
+}
+
+static bool AcquireCredentials(void)
+{
+ SCHANNEL_CRED SchannelCred;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS /*| 0xA00 TLS1.1 & 1.2*/;
+
+ SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
+
+ // Create an SSPI credential.
+ scRet = g_pSSPI->AcquireCredentialsHandleA(
+ NULL, // Name of principal
+ UNISP_NAME_A, // Name of package
+ SECPKG_CRED_OUTBOUND, // Flags indicating use
+ NULL, // Pointer to logon ID
+ &SchannelCred, // Package specific data
+ NULL, // Pointer to GetKey() func
+ NULL, // Value to pass to GetKey()
+ &hCreds, // (out) Cred Handle
+ &tsExpiry); // (out) Lifetime (optional)
+
+ ReportSslError(scRet, __LINE__);
+ return scRet == SEC_E_OK;
+}
+
+static bool SSL_library_init(void)
+{
+ if (bSslInitDone) return true;
+
+ WaitForSingleObject(g_hSslMutex, INFINITE);
+
+ if (!bSslInitDone)
+ {
+ g_hSchannel = LoadLibraryA("schannel.dll");
+ if (g_hSchannel)
+ {
+ INIT_SECURITY_INTERFACE_A pInitSecurityInterface;
+ pInitSecurityInterface = (INIT_SECURITY_INTERFACE_A)GetProcAddress(g_hSchannel, SECURITY_ENTRYPOINT_ANSIA);
+ if (pInitSecurityInterface != NULL)
+ g_pSSPI = pInitSecurityInterface();
+
+ if (g_pSSPI)
+ {
+ HINSTANCE hCrypt = LoadLibraryA("crypt32.dll");
+ if (hCrypt)
+ {
+ fnCertGetCertificateChain = (pfnCertGetCertificateChain)GetProcAddress(hCrypt, "CertGetCertificateChain");
+ fnCertFreeCertificateChain = (pfnCertFreeCertificateChain)GetProcAddress(hCrypt, "CertFreeCertificateChain");
+ fnCertFreeCertificateContext = (pfnCertFreeCertificateContext)GetProcAddress(hCrypt, "CertFreeCertificateContext");
+ fnCertVerifyCertificateChainPolicy = (pfnCertVerifyCertificateChainPolicy)GetProcAddress(hCrypt, "CertVerifyCertificateChainPolicy");
+ }
+
+ MySslEmptyCache = (SSL_EMPTY_CACHE_FN_M)GetProcAddress(g_hSchannel, "SslEmptyCache");
+ AcquireCredentials();
+ bSslInitDone = true;
+ }
+ else
+ {
+ FreeLibrary(g_hSchannel);
+ g_hSchannel = NULL;
+ }
+ }
+ }
+
+ ReleaseMutex(g_hSslMutex);
+ return bSslInitDone;
+}
+
+void NetlibSslFree(SslHandle *ssl)
+{
+ if (ssl == NULL) return;
+
+ g_pSSPI->DeleteSecurityContext(&ssl->hContext);
+
+ mir_free(ssl->pbRecDataBuf);
+ mir_free(ssl->pbIoBuffer);
+ memset(ssl, 0, sizeof(SslHandle));
+ mir_free(ssl);
+}
+
+BOOL NetlibSslPending(SslHandle *ssl)
+{
+ return ssl != NULL && ( ssl->cbRecDataBuf != 0 || ssl->cbIoBuffer != 0 );
+}
+
+static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, DWORD dwCertFlags)
+{
+ if (!fnCertGetCertificateChain)
+ return true;
+
+ static LPSTR rgszUsages[] =
+ {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+
+ CERT_CHAIN_PARA ChainPara = {0};
+ HTTPSPolicyCallbackData polHttps = {0};
+ CERT_CHAIN_POLICY_PARA PolicyPara = {0};
+ CERT_CHAIN_POLICY_STATUS PolicyStatus = {0};
+ PCCERT_CHAIN_CONTEXT pChainContext = NULL;
+ PCCERT_CONTEXT pServerCert = NULL;
+ DWORD scRet;
+
+ PWSTR pwszServerName = mir_a2u(pszServerName);
+
+ scRet = g_pSSPI->QueryContextAttributesA(&ssl->hContext,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pServerCert);
+ if (scRet != SEC_E_OK)
+ goto cleanup;
+
+ if (pServerCert == NULL)
+ {
+ scRet = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ ChainPara.cbSize = sizeof(ChainPara);
+ ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ ChainPara.RequestedUsage.Usage.cUsageIdentifier = SIZEOF(rgszUsages);
+ ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
+
+ if (!fnCertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore,
+ &ChainPara, 0, NULL, &pChainContext))
+ {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
+ polHttps.dwAuthType = AUTHTYPE_SERVER;
+ polHttps.fdwChecks = dwCertFlags;
+ polHttps.pwszServerName = pwszServerName;
+
+ PolicyPara.cbSize = sizeof(PolicyPara);
+ PolicyPara.pvExtraPolicyPara = &polHttps;
+
+ PolicyStatus.cbSize = sizeof(PolicyStatus);
+
+ if (!fnCertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext,
+ &PolicyPara, &PolicyStatus))
+ {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ if (PolicyStatus.dwError)
+ {
+ scRet = PolicyStatus.dwError;
+ goto cleanup;
+ }
+
+ scRet = SEC_E_OK;
+
+cleanup:
+ if (pChainContext)
+ fnCertFreeCertificateChain(pChainContext);
+ if (pServerCert)
+ fnCertFreeCertificateContext(pServerCert);
+ mir_free(pwszServerName);
+
+ ReportSslError(scRet, __LINE__, true);
+ return scRet == SEC_E_OK;
+}
+
+static SECURITY_STATUS ClientHandshakeLoop(SslHandle *ssl, BOOL fDoInitialRead)
+{
+ SecBufferDesc InBuffer;
+ SecBuffer InBuffers[2];
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ BOOL fDoRead;
+
+ dwSSPIFlags =
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ ssl->cbIoBuffer = 0;
+
+ fDoRead = fDoInitialRead;
+
+ scRet = SEC_I_CONTINUE_NEEDED;
+
+ // Loop until the handshake is finished or an error occurs.
+ while (scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ // Read server data
+ if (0 == ssl->cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (fDoRead)
+ {
+ static const TIMEVAL tv = {6, 0};
+ fd_set fd;
+
+ // If buffer not large enough reallocate buffer
+ if (ssl->sbIoBuffer <= ssl->cbIoBuffer)
+ {
+ ssl->sbIoBuffer += 4096;
+ ssl->pbIoBuffer = (PUCHAR)mir_realloc(ssl->pbIoBuffer, ssl->sbIoBuffer);
+ }
+
+ FD_ZERO(&fd);
+ FD_SET(ssl->s, &fd);
+ if (select(1, &fd, NULL, NULL, &tv) != 1)
+ {
+ NetlibLogf(NULL, "SSL Negotiation failure recieving data (timeout) (bytes %u)", ssl->cbIoBuffer);
+ scRet = ERROR_NOT_READY;
+ break;
+ }
+
+ cbData = recv(ssl->s, (char*)ssl->pbIoBuffer + ssl->cbIoBuffer, ssl->sbIoBuffer - ssl->cbIoBuffer, 0);
+ if (cbData == SOCKET_ERROR)
+ {
+ NetlibLogf(NULL, "SSL Negotiation failure recieving data (%d)", WSAGetLastError());
+ scRet = ERROR_NOT_READY;
+ break;
+ }
+ if (cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL Negotiation connection gracefully closed");
+ scRet = ERROR_NOT_READY;
+ break;
+ }
+
+ NetlibDumpData(NULL, ssl->pbIoBuffer + ssl->cbIoBuffer, cbData, 0, MSG_DUMPSSL);
+ ssl->cbIoBuffer += cbData;
+ }
+ else fDoRead = TRUE;
+ }
+
+ // Set up the input buffers. Buffer 0 is used to pass in data
+ // received from the server. Schannel will consume some or all
+ // of this. Leftover data (if any) will be placed in buffer 1 and
+ // given a buffer type of SECBUFFER_EXTRA.
+
+ InBuffers[0].pvBuffer = ssl->pbIoBuffer;
+ InBuffers[0].cbBuffer = ssl->cbIoBuffer;
+ InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ InBuffers[1].pvBuffer = NULL;
+ InBuffers[1].cbBuffer = 0;
+ InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ InBuffer.cBuffers = 2;
+ InBuffer.pBuffers = InBuffers;
+ InBuffer.ulVersion = SECBUFFER_VERSION;
+
+ // Set up the output buffers. These are initialized to NULL
+ // so as to make it less likely we'll attempt to free random
+ // garbage later.
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType= SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->InitializeSecurityContextA(
+ &hCreds,
+ &ssl->hContext,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &InBuffer,
+ 0,
+ NULL,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ // If success (or if the error was one of the special extended ones),
+ // send the contents of the output buffer to the server.
+ if (scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)))
+ {
+ if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+ {
+ NetlibDumpData(NULL, (unsigned char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 1, MSG_DUMPSSL);
+ cbData = send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL Negotiation failure sending data (%d)", WSAGetLastError());
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ // Free output buffer.
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ OutBuffers[0].pvBuffer = NULL;
+ }
+ }
+
+ // we need to read more data from the server and try again.
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue;
+
+ // handshake completed successfully.
+ if (scRet == SEC_E_OK)
+ {
+ // Store remaining data for further use
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memmove(ssl->pbIoBuffer,
+ ssl->pbIoBuffer + (ssl->cbIoBuffer - InBuffers[1].cbBuffer),
+ InBuffers[1].cbBuffer);
+ ssl->cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+ else
+ ssl->cbIoBuffer = 0;
+ break;
+ }
+
+ // Check for fatal error.
+ if (FAILED(scRet)) break;
+
+ // server just requested client authentication.
+ if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ // Server has requested client authentication and
+ // GetNewClientCredentials(ssl);
+
+ // Go around again.
+ fDoRead = FALSE;
+ scRet = SEC_I_CONTINUE_NEEDED;
+ continue;
+ }
+
+
+ // Copy any leftover data from the buffer, and go around again.
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memmove(ssl->pbIoBuffer,
+ ssl->pbIoBuffer + (ssl->cbIoBuffer - InBuffers[1].cbBuffer),
+ InBuffers[1].cbBuffer);
+
+ ssl->cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+ else ssl->cbIoBuffer = 0;
+ }
+
+ // Delete the security context in the case of a fatal error.
+ ReportSslError(scRet, __LINE__);
+
+ if (ssl->cbIoBuffer == 0)
+ {
+ mir_free(ssl->pbIoBuffer);
+ ssl->pbIoBuffer = NULL;
+ ssl->sbIoBuffer = 0;
+ }
+
+ return scRet;
+}
+
+static bool ClientConnect(SslHandle *ssl, const char *host)
+{
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ if (SecIsValidHandle(&ssl->hContext))
+ {
+ g_pSSPI->DeleteSecurityContext(&ssl->hContext);
+ SecInvalidateHandle(&ssl->hContext);
+ }
+
+ if (MySslEmptyCache) MySslEmptyCache();
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ // Initiate a ClientHello message and generate a token.
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->InitializeSecurityContextA(
+ &hCreds,
+ NULL,
+ (SEC_CHAR*)host,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &ssl->hContext,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ if (scRet != SEC_I_CONTINUE_NEEDED)
+ {
+ ReportSslError(scRet, __LINE__);
+ return 0;
+ }
+
+ // Send response to server if there is one.
+ if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+ {
+ NetlibDumpData(NULL, (unsigned char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 1, MSG_DUMPSSL);
+ cbData = send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL failure sending connection data (%d %d)", ssl->s, WSAGetLastError());
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ return 0;
+ }
+
+ // Free output buffer.
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ OutBuffers[0].pvBuffer = NULL;
+ }
+
+ return ClientHandshakeLoop(ssl, TRUE) == SEC_E_OK;
+}
+
+
+SslHandle *NetlibSslConnect(SOCKET s, const char* host, int verify)
+{
+ SslHandle *ssl = (SslHandle*)mir_calloc(sizeof(SslHandle));
+ ssl->s = s;
+
+ SecInvalidateHandle(&ssl->hContext);
+
+ DWORD dwFlags = 0;
+
+ if (!host || inet_addr(host) != INADDR_NONE)
+ dwFlags |= 0x00001000;
+
+ bool res = SSL_library_init();
+
+ if (res) res = ClientConnect(ssl, host);
+ if (res && verify) res = VerifyCertificate(ssl, host, dwFlags);
+
+ if (!res)
+ {
+ NetlibSslFree(ssl);
+ ssl = NULL;
+ }
+ return ssl;
+}
+
+
+void NetlibSslShutdown(SslHandle *ssl)
+{
+ DWORD dwType;
+
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ DWORD scRet;
+
+ if (ssl == NULL || !SecIsValidHandle(&ssl->hContext))
+ return;
+
+ dwType = SCHANNEL_SHUTDOWN;
+
+ OutBuffers[0].pvBuffer = &dwType;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = sizeof(dwType);
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->ApplyControlToken(&ssl->hContext, &OutBuffer);
+ if (FAILED(scRet)) return;
+
+ //
+ // Build an SSL close notify message.
+ //
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->InitializeSecurityContextA(
+ &hCreds,
+ &ssl->hContext,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &ssl->hContext,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ if (FAILED(scRet)) return;
+
+ // Send the close notify message to the server.
+ if (OutBuffers[0].pvBuffer != NULL && OutBuffers[0].cbBuffer != 0)
+ {
+ NetlibDumpData(NULL, (unsigned char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 1, MSG_DUMPSSL);
+ send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ }
+}
+
+static int NetlibSslReadSetResult(SslHandle *ssl, char *buf, int num, int peek)
+{
+ if (ssl->cbRecDataBuf == 0)
+ {
+ return (ssl->state == sockClosed ? 0: SOCKET_ERROR);
+ }
+
+ int bytes = min(num, ssl->cbRecDataBuf);
+ int rbytes = ssl->cbRecDataBuf - bytes;
+
+ memcpy(buf, ssl->pbRecDataBuf, bytes);
+ if (!peek)
+ {
+ memmove(ssl->pbRecDataBuf, ssl->pbRecDataBuf + bytes, rbytes);
+ ssl->cbRecDataBuf = rbytes;
+ }
+
+ return bytes;
+}
+
+int NetlibSslRead(SslHandle *ssl, char *buf, int num, int peek)
+{
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+ DWORD resNum = 0;
+ int i;
+
+ SecBufferDesc Message;
+ SecBuffer Buffers[4];
+ SecBuffer * pDataBuffer;
+ SecBuffer * pExtraBuffer;
+
+ if (ssl == NULL) return SOCKET_ERROR;
+
+ if (num <= 0) return 0;
+
+ if (ssl->state != sockOpen || (ssl->cbRecDataBuf != 0 && (!peek || ssl->cbRecDataBuf >= num)))
+ {
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ scRet = SEC_E_OK;
+
+ for (;;)
+ {
+ if (0 == ssl->cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (ssl->sbIoBuffer <= ssl->cbIoBuffer)
+ {
+ ssl->sbIoBuffer += 2048;
+ ssl->pbIoBuffer = (PUCHAR)mir_realloc(ssl->pbIoBuffer, ssl->sbIoBuffer);
+ }
+
+ if (peek)
+ {
+ static const TIMEVAL tv = {0};
+ fd_set fd;
+ FD_ZERO(&fd);
+ FD_SET(ssl->s, &fd);
+
+ cbData = select(1, &fd, NULL, NULL, &tv);
+ if (cbData == SOCKET_ERROR)
+ {
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ if (cbData == 0 && ssl->cbRecDataBuf)
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ cbData = recv(ssl->s, (char*)ssl->pbIoBuffer + ssl->cbIoBuffer, ssl->sbIoBuffer - ssl->cbIoBuffer, 0);
+ if (cbData == SOCKET_ERROR)
+ {
+ NetlibLogf(NULL, "SSL failure recieving data (%d)", WSAGetLastError());
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ if (cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL connection gracefully closed");
+ if (peek && ssl->cbRecDataBuf)
+ {
+ ssl->state = sockClosed;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ // Server disconnected.
+ if (ssl->cbIoBuffer)
+ {
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ return 0;
+ }
+ else
+ {
+ NetlibDumpData(NULL, ssl->pbIoBuffer + ssl->cbIoBuffer, cbData, 0, MSG_DUMPSSL);
+ ssl->cbIoBuffer += cbData;
+ }
+ }
+
+ // Attempt to decrypt the received data.
+ Buffers[0].pvBuffer = ssl->pbIoBuffer;
+ Buffers[0].cbBuffer = ssl->cbIoBuffer;
+ Buffers[0].BufferType = SECBUFFER_DATA;
+
+ Buffers[1].BufferType = SECBUFFER_EMPTY;
+ Buffers[2].BufferType = SECBUFFER_EMPTY;
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+
+ if (g_pSSPI->DecryptMessage != NULL && g_pSSPI->DecryptMessage != PVOID(0x80000000))
+ scRet = g_pSSPI->DecryptMessage(&ssl->hContext, &Message, 0, NULL);
+ else
+ scRet = ((DECRYPT_MESSAGE_FN)g_pSSPI->Reserved4)(&ssl->hContext, &Message, 0, NULL);
+
+ // The input buffer contains only a fragment of an
+ // encrypted record. Loop around and read some more
+ // data.
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ continue;
+
+ if ( scRet != SEC_E_OK && scRet != SEC_I_RENEGOTIATE && scRet != SEC_I_CONTEXT_EXPIRED)
+ {
+ ReportSslError(scRet, __LINE__);
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ // Locate data and (optional) extra buffers.
+ pDataBuffer = NULL;
+ pExtraBuffer = NULL;
+ for(i = 1; i < 4; i++)
+ {
+ if (pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
+ pDataBuffer = &Buffers[i];
+
+ if (pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
+ pExtraBuffer = &Buffers[i];
+ }
+
+ // Return decrypted data.
+ if (pDataBuffer)
+ {
+ DWORD bytes, rbytes;
+
+ bytes = peek ? 0 : min((DWORD)num, pDataBuffer->cbBuffer);
+ rbytes = pDataBuffer->cbBuffer - bytes;
+
+ NetlibDumpData(NULL, (PBYTE)pDataBuffer->pvBuffer, pDataBuffer->cbBuffer, 0, MSG_DUMPSSL);
+
+ if (rbytes > 0)
+ {
+ int nbytes = ssl->cbRecDataBuf + rbytes;
+ if (ssl->sbRecDataBuf < nbytes)
+ {
+ ssl->sbRecDataBuf = nbytes;
+ ssl->pbRecDataBuf = (PUCHAR)mir_realloc(ssl->pbRecDataBuf, nbytes);
+ }
+ memcpy(ssl->pbRecDataBuf + ssl->cbRecDataBuf, (char*)pDataBuffer->pvBuffer + bytes, rbytes);
+ ssl->cbRecDataBuf = nbytes;
+ }
+
+ if (peek)
+ {
+ resNum = bytes = min(num, ssl->cbRecDataBuf);
+ memcpy(buf, ssl->pbRecDataBuf, bytes);
+ }
+ else
+ {
+ resNum = bytes;
+ memcpy(buf, pDataBuffer->pvBuffer, bytes);
+ }
+ }
+
+ // Move any "extra" data to the input buffer.
+ if (pExtraBuffer)
+ {
+ memmove(ssl->pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
+ ssl->cbIoBuffer = pExtraBuffer->cbBuffer;
+ }
+ else ssl->cbIoBuffer = 0;
+
+ if (pDataBuffer && resNum)
+ return resNum;
+
+ // Server signaled end of session
+ if (scRet == SEC_I_CONTEXT_EXPIRED)
+ {
+ NetlibLogf(NULL, "SSL Server signaled SSL Shutdown");
+ ssl->state = sockClosed;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ if (scRet == SEC_I_RENEGOTIATE)
+ {
+ // The server wants to perform another handshake
+ // sequence.
+
+ scRet = ClientHandshakeLoop(ssl, FALSE);
+ if (scRet != SEC_E_OK)
+ {
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+ }
+ }
+}
+
+int NetlibSslWrite(SslHandle *ssl, const char *buf, int num)
+{
+ SecPkgContext_StreamSizes Sizes;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ SecBufferDesc Message;
+ SecBuffer Buffers[4] = {0};
+
+ PUCHAR pbDataBuffer;
+
+ PUCHAR pbMessage;
+ DWORD cbMessage;
+
+ DWORD sendOff = 0;
+
+ if (ssl == NULL) return SOCKET_ERROR;
+
+ scRet = g_pSSPI->QueryContextAttributesA(&ssl->hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
+ if (scRet != SEC_E_OK) return scRet;
+
+ pbDataBuffer = (PUCHAR)mir_calloc(Sizes.cbMaximumMessage + Sizes.cbHeader + Sizes.cbTrailer);
+
+ pbMessage = pbDataBuffer + Sizes.cbHeader;
+
+ while (sendOff < (DWORD)num)
+ {
+ cbMessage = min(Sizes.cbMaximumMessage, (DWORD)num - sendOff);
+ CopyMemory(pbMessage, buf+sendOff, cbMessage);
+
+ Buffers[0].pvBuffer = pbDataBuffer;
+ Buffers[0].cbBuffer = Sizes.cbHeader;
+ Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+ Buffers[1].pvBuffer = pbMessage;
+ Buffers[1].cbBuffer = cbMessage;
+ Buffers[1].BufferType = SECBUFFER_DATA;
+
+ Buffers[2].pvBuffer = pbMessage + cbMessage;
+ Buffers[2].cbBuffer = Sizes.cbTrailer;
+ Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+
+ if (g_pSSPI->EncryptMessage != NULL)
+ scRet = g_pSSPI->EncryptMessage(&ssl->hContext, 0, &Message, 0);
+ else
+ scRet = ((ENCRYPT_MESSAGE_FN)g_pSSPI->Reserved3)(&ssl->hContext, 0, &Message, 0);
+
+ if (FAILED(scRet)) break;
+
+ // Calculate encrypted packet size
+ cbData = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
+
+ // Send the encrypted data to the server.
+ NetlibDumpData(NULL, pbDataBuffer, cbData, 1, MSG_DUMPSSL);
+ cbData = send(ssl->s, (char*)pbDataBuffer, cbData, 0);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL failure sending data (%d)", WSAGetLastError());
+ scRet = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+
+ sendOff += cbMessage;
+ }
+
+ mir_free(pbDataBuffer);
+ return scRet == SEC_E_OK ? num : SOCKET_ERROR;
+}
+
+static INT_PTR GetSslApi(WPARAM, LPARAM lParam)
+{
+ SSL_API* si = (SSL_API*)lParam;
+ if (si == NULL) return FALSE;
+
+ if (si->cbSize != sizeof(SSL_API))
+ return FALSE;
+
+ si->connect = (HSSL (__cdecl *)(SOCKET,const char *,int))NetlibSslConnect;
+ si->pending = (BOOL (__cdecl *)(HSSL))NetlibSslPending;
+ si->read = (int (__cdecl *)(HSSL,char *,int,int))NetlibSslRead;
+ si->write = (int (__cdecl *)(HSSL,const char *,int))NetlibSslWrite;
+ si->shutdown = (void (__cdecl *)(HSSL))NetlibSslShutdown;
+ si->sfree = (void (__cdecl *)(HSSL))NetlibSslFree;
+
+ return TRUE;
+}
+
+int LoadSslModule(void)
+{
+ CreateServiceFunction(MS_SYSTEM_GET_SI, GetSslApi);
+ g_hSslMutex = CreateMutex(NULL, FALSE, NULL);
+ SecInvalidateHandle(&hCreds);
+
+ return 0;
+}
+
+void UnloadSslModule(void)
+{
+ if (g_pSSPI && SecIsValidHandle(&hCreds))
+ g_pSSPI->FreeCredentialsHandle(&hCreds);
+ CloseHandle(g_hSslMutex);
+ if (g_hSchannel) FreeLibrary(g_hSchannel);
+}
diff --git a/src/modules/netlib/netlibupnp.cpp b/src/modules/netlib/netlibupnp.cpp new file mode 100644 index 0000000000..935801d330 --- /dev/null +++ b/src/modules/netlib/netlibupnp.cpp @@ -0,0 +1,885 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/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 "commonheaders.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[] =
+ "<serviceType>%s</serviceType>";
+
+static const char soap_action[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<s:Envelope\r\n"
+ " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:%s xmlns:u=\"%s\">\r\n"
+ "%s"
+ " </u:%s>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char soap_query[] =
+ "<s:Envelope\r\n"
+ " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\r\n"
+ " <u:varName>%s</u:varName>\r\n"
+ " </u:QueryStateVariable>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char add_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n"
+ " <NewInternalPort>%i</NewInternalPort>\r\n"
+ " <NewInternalClient>%s</NewInternalClient>\r\n"
+ " <NewEnabled>1</NewEnabled>\r\n"
+ " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
+ " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
+
+static const char delete_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n";
+
+static const char get_port_mapping[] =
+ " <NewPortMappingIndex>%i</NewPortMappingIndex>\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 != NULL) {
+ cp1 = strstr(szData, presearch);
+ if (cp1 == NULL) return false;
+ }
+ else
+ cp1 = szData;
+
+ cp = strstr(cp1, start);
+ if (cp == NULL) return false;
+ cp += strlen(start);
+ while (*cp == ' ') ++cp;
+
+ cp1 = strstr(cp, finish);
+ if (cp1 == NULL) 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 == NULL) phost = szUrl;
+ else phost += 3;
+
+ ppath = strchr(phost,'/');
+ if (ppath == NULL) ppath = phost + strlen(phost);
+
+ pport = strchr(phost,':');
+ if (pport == NULL) pport = ppath;
+
+ if (szHost != NULL)
+ {
+ sz = pport - phost + 1;
+ if (sz>256) sz = 256;
+ strncpy(szHost, phost, sz);
+ szHost[sz-1] = 0;
+ }
+
+ if (sPort != NULL)
+ {
+ if (pport < ppath)
+ {
+ long prt = atol(pport+1);
+ *sPort = prt != 0 ? (unsigned short)prt : 80;
+ }
+ else
+ *sPort = 80;
+ }
+
+ if (szPath != NULL)
+ {
+ strncpy(szPath, ppath, 256);
+ szPath[255] = 0;
+ }
+}
+
+
+static void LongLog(char* szData)
+{
+ CallService(MS_NETLIB_LOG, 0, (LPARAM)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];
+ bool opened;
+
+ if (sock == INVALID_SOCKET)
+ return;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ switch (select(1, &rfd, NULL, NULL, &tv))
+ {
+ case SOCKET_ERROR:
+ opened = false;
+ break;
+
+ 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;
+
+ const char* szPostHdr = soap_post_hdr;
+ char* szData = ( char* )mir_alloc(4096);
+ char* szReq = NULL;
+
+ parseURL(szUrl, szHost, &sPort, szPath);
+
+ if (sPort != sConnPort || _stricmp(szHost, szConnHost))
+ closeRouterConnection();
+ else
+ validateSocket();
+
+ for (;;)
+ {
+ 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, sizeof(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, sizeof(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];
+ }
+
+ NetlibLogf(NULL, "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();
+ NetlibLogf(NULL, "UPnP connect failed %d", err);
+ break;
+ }
+ // Wait for socket to connect
+ else if (select(1, &rfd, &wfd, &efd, &tv) != 1)
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "UPnP connect timeout");
+ break;
+ }
+ else if (!FD_ISSET(sock, &wfd))
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "UPnP connect failed");
+ break;
+ }
+ }
+ strcpy(szConnHost, szHost); sConnPort = sPort;
+ }
+
+ if (send(sock, szData, sz, 0) != SOCKET_ERROR)
+ {
+ char *hdrend = NULL;
+ int acksz = 0, pktsz = 0;
+
+ if (szActionName == NULL)
+ {
+ 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 != NULL)
+ locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0];
+ }
+ }
+
+ LongLog(szData);
+ sz = 0;
+ for(;;)
+ {
+ int bytesRecv;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ // Wait for the next packet
+ if (select(1, &rfd, NULL, NULL, &tv) != 1)
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "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 == NULL)
+ {
+ // Find HTTP header end
+ hdrend = strstr(szResult, "\r\n\r\n");
+ if (hdrend == NULL)
+ {
+ hdrend = strstr(szResult, "\n\n");
+ if (hdrend) hdrend += 2;
+ }
+
+ else
+ hdrend += 4;
+
+ if (hdrend != NULL)
+ {
+ // Get packet size if provided
+ if (txtParseParam(szResult, NULL, "Content-Length:", "\n", szRes, sizeof(szRes)) ||
+ txtParseParam(szResult, NULL, "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, NULL, "Transfer-Encoding:", "\n", szRes, sizeof(szRes)))
+ {
+ if (_stricmp(lrtrimp(szRes), "Chunked") == 0)
+ acksz = hdrend - szResult;
+ }
+ if (txtParseParam(szResult, NULL, "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 != NULL)
+ {
+ char *peol2 = strchr(++peol1, '\n');
+ if (peol2 != NULL)
+ {
+ // Get chunk size
+ int chunkBytes = strtol(peol1, NULL, 16);
+ acksz += chunkBytes;
+ peol2++;
+
+ memmove(data, peol2, 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
+ NetlibLogf(NULL, "UPnP send failed %d", WSAGetLastError());
+ }
+ }
+ txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes));
+ res = atol(szRes);
+ if (szActionName != NULL && 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, "<NewExternalIPAddress", ">", "<", 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, NULL, DeviceGetReq) == 200;
+ if (gatewayFound)
+ {
+ char szTemp[256], *rpth;
+ size_t ctlLen;
+
+ txtParseParam(szData, NULL, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp));
+ strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl));
+ szCtlUrl[sizeof(szCtlUrl)-1] = 0;
+
+ mir_snprintf(szTemp, sizeof(szTemp), search_device, szDev);
+ txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl);
+
+ // URL combining per RFC 2396
+ if ( szUrl[0] != 0 )
+ {
+ if (strstr(szUrl, "://") != NULL) // absolute URI
+ rpth = szCtlUrl;
+ else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ if (rpth == NULL) rpth = szCtlUrl;
+ }
+ else if (szUrl[0] == '/') // relative URI abs_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ rpth = rpth ? rpth + 2 : szCtlUrl;
+
+ rpth = strchr(rpth, '/');
+ if (rpth == NULL) rpth = szCtlUrl + strlen(szCtlUrl);
+ }
+ else
+ { // relative URI rel_path
+ size_t ctlCLen = 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 = NULL;
+
+ static const unsigned any = INADDR_ANY;
+ static const TIMEVAL tv = { 1, 600000 };
+
+ char szUrl[256] = "";
+ char hostname[256];
+ PHOSTENT he;
+ fd_set readfd;
+
+ SOCKET sock = 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(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned));
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1");
+ sendto(sock, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1");
+ sendto(sock, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+ }
+
+ if (Miranda_Terminated()) break;
+
+ FD_ZERO(&readfd);
+ FD_SET(sock, &readfd);
+
+ while (select(1, &readfd, NULL, NULL, &tv) >= 1)
+ {
+ buflen = recv(sock, buf, 1500, 0);
+ if (buflen != SOCKET_ERROR)
+ {
+ buf[buflen] = 0;
+ LongLog(buf);
+
+ if (txtParseParam(buf, NULL, "LOCATION:", "\n", szUrl, sizeof(szUrl)) ||
+ txtParseParam(buf, NULL, "Location:", "\n", szUrl, sizeof(szUrl)))
+ {
+ char age[30];
+ char szHostNew[256], szHostExist[256];
+
+ lrtrim(szUrl);
+
+ parseURL(szUrl, szHostNew, NULL, NULL);
+ parseURL(szCtlUrl, szHostExist, NULL, NULL);
+ if (strcmp(szHostNew, szHostExist) == 0)
+ {
+ gatewayFound = true;
+ break;
+ }
+
+ txtParseParam(buf, NULL, "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(sock, &readfd);
+ }
+ }
+
+ mir_free(buf);
+ mir_free(ips);
+ setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned));
+ closesocket(sock);
+}
+
+
+static bool findUPnPGateway(void)
+{
+ if ((time(NULL) - lastDiscTime) >= expireTime)
+ {
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ time_t curTime = time(NULL);
+
+ if ((curTime - lastDiscTime) >= expireTime)
+ {
+ gatewayFound = false;
+
+ discoverUPnP();
+ lastDiscTime = curTime;
+
+ NetlibLogf(NULL, "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, NULL, "<errorCode>", "</errorCode>", 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*)
+{
+ if (DBGetContactSettingByte(NULL,"Netlib","NLEnableUPnP",1)==0)
+ // upnp is disabled globally, no need for a cleanup
+ return;
+
+ {
+ int i, incoming = 0;
+ EnterCriticalSection(&csNetlibUser);
+ for (i = 0; i < netlibUser.getCount(); ++i)
+ {
+ if (netlibUser[i]->user.flags & NUF_INCOMING)
+ {
+ incoming = 1;
+ break;
+ }
+ }
+ LeaveCriticalSection(&csNetlibUser);
+ if (!incoming) return;
+ }
+
+ if (findUPnPGateway())
+ {
+ char* szData = (char*)alloca(4096);
+ char buf[50], lip[50];
+ unsigned i, j = 0, k, num = 100;
+
+ WORD ports[30];
+
+ strcpy(lip, inet_ntoa(locIP.sin_addr));
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 &&
+ txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf)))
+ num = atol(buf);
+
+ for (i=0; i<num && !Miranda_Terminated(); ++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, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || strcmp(buf, "Miranda") != 0)
+ continue;
+
+ if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || strcmp(buf, lip) != 0)
+ continue;
+
+ if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf)))
+ {
+ WORD mport = (WORD)atol(buf);
+
+ if (j >= SIZEOF(ports))
+ break;
+
+ for (k=0; k<numports; ++k)
+ if (portList[k] == mport)
+ break;
+
+ if (k >= numports)
+ ports[j++] = mport;
+ }
+ }
+
+ ReleaseMutex(portListMutex);
+
+ for (i=0; i<j && !Miranda_Terminated(); ++i)
+ NetlibUPnPDeletePortMapping(ports[i], "TCP");
+ }
+}
+
+void NetlibUPnPInit(void)
+{
+ numports = 0;
+ numportsAlloc = 10;
+ portList = (WORD*)mir_alloc(sizeof(WORD)*numportsAlloc);
+
+ portListMutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+void NetlibUPnPDestroy(void)
+{
+ mir_free(portList);
+ CloseHandle(portListMutex);
+}
|