From 8549e48fbb8520027d4d8ba74b6f12f3042faebc Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sun, 27 May 2012 21:30:30 +0000 Subject: ipv6 sync git-svn-id: http://svn.miranda-ng.org/main/trunk@205 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- src/modules/netlib/netlib.cpp | 56 +++++- src/modules/netlib/netlib.h | 14 +- src/modules/netlib/netlibbind.cpp | 129 ++++++++++---- src/modules/netlib/netlibhttp.cpp | 4 +- src/modules/netlib/netlibopenconn.cpp | 204 ++++++++++++---------- src/modules/netlib/netlibsock.cpp | 280 +++++++++++++++++++++++------- src/modules/updatenotify/updatenotify.cpp | 14 +- 7 files changed, 490 insertions(+), 211 deletions(-) (limited to 'src/modules') diff --git a/src/modules/netlib/netlib.cpp b/src/modules/netlib/netlib.cpp index 000639474f..7cd39b1d7a 100644 --- a/src/modules/netlib/netlib.cpp +++ b/src/modules/netlib/netlib.cpp @@ -242,9 +242,11 @@ void NetlibDoClose(NetlibConnection *nlc, bool noShutdown) INT_PTR NetlibCloseHandle(WPARAM wParam, LPARAM) { - switch(GetNetlibHandleType(wParam)) { + switch(GetNetlibHandleType(wParam)) + { case NLH_USER: - { struct NetlibUser *nlu=(struct NetlibUser*)wParam; + { + struct NetlibUser *nlu=(struct NetlibUser*)wParam; int i; EnterCriticalSection(&csNetlibUser); @@ -323,21 +325,24 @@ INT_PTR NetlibCloseHandle(WPARAM wParam, LPARAM) static INT_PTR NetlibGetSocket(WPARAM wParam, LPARAM) { SOCKET s; - if(wParam==0) { - s=INVALID_SOCKET; + if (wParam == 0) + { + s = INVALID_SOCKET; SetLastError(ERROR_INVALID_PARAMETER); } - else { + else + { WaitForSingleObject(hConnectionHeaderMutex,INFINITE); - switch(GetNetlibHandleType(wParam)) { + switch (GetNetlibHandleType(wParam)) + { case NLH_CONNECTION: - s=((struct NetlibConnection*)wParam)->s; + s = ((struct NetlibConnection*)wParam)->s; break; case NLH_BOUNDPORT: - s=((struct NetlibBoundPort*)wParam)->s; + s = ((struct NetlibBoundPort*)wParam)->s; break; default: - s=INVALID_SOCKET; + s = INVALID_SOCKET; SetLastError(ERROR_INVALID_PARAMETER); break; } @@ -346,6 +351,35 @@ static INT_PTR NetlibGetSocket(WPARAM wParam, LPARAM) return s; } +INT_PTR NetlibStringToAddressSrv(WPARAM wParam, LPARAM lParam) +{ + return (INT_PTR)!NetlibStringToAddress((char*)wParam, (SOCKADDR_INET_M*)lParam); +} + +INT_PTR NetlibAddressToStringSrv(WPARAM wParam, LPARAM lParam) +{ + if (wParam) + { + SOCKADDR_INET_M iaddr = {0}; + iaddr.Ipv4.sin_family = AF_INET; + iaddr.Ipv4.sin_addr.s_addr = htonl((unsigned)lParam); + return (INT_PTR)NetlibAddressToString(&iaddr); + } + else + return (INT_PTR)NetlibAddressToString((SOCKADDR_INET_M*)lParam); +} + +INT_PTR NetlibGetConnectionInfoSrv(WPARAM wParam, LPARAM lParam) +{ + NetlibGetConnectionInfo((NetlibConnection*)wParam, (NETLIBCONNINFO*)lParam); + return 0; +} + +INT_PTR NetlibGetMyIp(WPARAM wParam, LPARAM) +{ + return (INT_PTR)GetMyIp((unsigned)wParam); +} + INT_PTR NetlibShutdown(WPARAM wParam, LPARAM) { if (wParam) @@ -623,6 +657,10 @@ int LoadNetlibModule(void) CreateServiceFunction(MS_NETLIB_GETMOREPACKETS,NetlibPacketRecverGetMore); CreateServiceFunction(MS_NETLIB_SETPOLLINGTIMEOUT,NetlibHttpSetPollingTimeout); CreateServiceFunction(MS_NETLIB_STARTSSL,NetlibStartSsl); + CreateServiceFunction(MS_NETLIB_STARINGTOADDRESS,NetlibStringToAddressSrv); + CreateServiceFunction(MS_NETLIB_ADDRESSTOSTRING,NetlibAddressToStringSrv); + CreateServiceFunction(MS_NETLIB_GETCONNECTIONINFO,NetlibGetConnectionInfoSrv); + CreateServiceFunction(MS_NETLIB_GETMYIP,NetlibGetMyIp); hRecvEvent = CreateHookableEvent(ME_NETLIB_FASTRECV); hSendEvent = CreateHookableEvent(ME_NETLIB_FASTSEND); diff --git a/src/modules/netlib/netlib.h b/src/modules/netlib/netlib.h index 575e77f18c..8939e7fafc 100644 --- a/src/modules/netlib/netlib.h +++ b/src/modules/netlib/netlib.h @@ -52,6 +52,12 @@ struct NetlibHTTPProxyPacketQueue int dataBufferLen; }; +typedef union _SOCKADDR_INET_M { + SOCKADDR_IN Ipv4; + SOCKADDR_IN6 Ipv6; + USHORT si_family; +} SOCKADDR_INET_M, *PSOCKADDR_INET_M; + struct NetlibConnection { int handleType; @@ -83,6 +89,7 @@ struct NetlibConnection struct NetlibBoundPort { int handleType; SOCKET s; + SOCKET s6; WORD wPort; WORD wExPort; struct NetlibUser *nlu; @@ -123,7 +130,7 @@ 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); +bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn); //netlibhttp.c INT_PTR NetlibHttpSendRequest(WPARAM wParam,LPARAM lParam); @@ -179,6 +186,11 @@ INT_PTR NetlibSelect(WPARAM wParam,LPARAM lParam); INT_PTR NetlibSelectEx(WPARAM wParam,LPARAM lParam); INT_PTR NetlibShutdown(WPARAM wParam,LPARAM lParam); +bool NetlibStringToAddress(const char* str, SOCKADDR_INET_M* addr); +char* NetlibAddressToString(SOCKADDR_INET_M* addr); +void NetlibGetConnectionInfo(NetlibConnection* nlc, NETLIBCONNINFO *connInfo); +NETLIBIPLIST* GetMyIp(unsigned flags); + //netlibupnp.c bool NetlibUPnPAddPortMapping(WORD intport, char *proto, WORD *extport, DWORD *extip, bool search); diff --git a/src/modules/netlib/netlibbind.cpp b/src/modules/netlib/netlibbind.cpp index 6c0c4c19e9..5fb9c252cc 100644 --- a/src/modules/netlib/netlibbind.cpp +++ b/src/modules/netlib/netlibbind.cpp @@ -2,7 +2,7 @@ Miranda IM: the free IM client for Microsoft* Windows* -Copyright 2000-2009 Miranda ICQ/IM project, +Copyright 2000-2012 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. @@ -23,17 +23,19 @@ 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) +bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) { SOCKADDR_IN sin = {0}; sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); + + SOCKADDR_IN6 sin6 = {0}; + sin6.sin6_family = AF_INET6; EnterCriticalSection(&csNetlibUser); - if (--*portn < 0 && s != INVALID_SOCKET) + if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { - BindSocketToPort(szPorts, INVALID_SOCKET, portn); + BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); if (*portn == 0) { LeaveCriticalSection(&csNetlibUser); @@ -88,7 +90,12 @@ bool BindSocketToPort(const char *szPorts, SOCKET s, int* portn) } sin.sin_port = htons((WORD)port); - if (bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0) + bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; + + sin6.sin6_port = htons((WORD)port); + bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; + + if (bV4Mapped && bV6Mapped) { LeaveCriticalSection(&csNetlibUser); *portn = portnum + 1; @@ -111,10 +118,10 @@ bool BindSocketToPort(const char *szPorts, SOCKET s, int* portn) } } - int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp) { closesocket(nlbp->s); + closesocket(nlbp->s6); WaitForSingleObject(nlbp->hThread,INFINITE); CloseHandle(nlbp->hThread); NetlibLogf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); @@ -125,18 +132,39 @@ int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp) static unsigned __stdcall NetlibBindAcceptThread(void* param) { SOCKET s; - SOCKADDR_IN sin; + SOCKADDR_INET_M 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(;;) + for (;;) { + fd_set r; + FD_ZERO(&r); + FD_SET(nlbp->s, &r); + FD_SET(nlbp->s6, &r); + + if (select(0, &r, NULL, NULL, NULL) == SOCKET_ERROR) + break; + 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); + memset(&sin, 0, sizeof(sin)); + + if (FD_ISSET(nlbp->s, &r)) + { + s = accept(nlbp->s, (struct sockaddr*)&sin, &sinLen); + if (s == INVALID_SOCKET) break; + } + else if (FD_ISSET(nlbp->s6, &r)) + { + s = accept(nlbp->s6, (struct sockaddr*)&sin, &sinLen); + if (s == INVALID_SOCKET) break; + } + + char *szHostA = NetlibAddressToString(&sin); + NetlibLogf(nlbp->nlu, "New incoming connection on port %u from %s (%p )", nlbp->wPort, szHostA, s); + mir_free(szHostA); nlc = (NetlibConnection*)mir_calloc(sizeof(NetlibConnection)); nlc->handleType = NLH_CONNECTION; nlc->nlu = nlbp->nlu; @@ -144,21 +172,22 @@ static unsigned __stdcall NetlibBindAcceptThread(void* param) 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); + + if (nlbp->pfnNewConnectionV2) nlbp->pfnNewConnectionV2(nlc, ntohl(sin.Ipv4.sin_addr.S_un.S_addr), nlbp->pExtra); } NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); return 0; } -INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam) +INT_PTR NetlibBindPort(WPARAM wParam, LPARAM lParam) { NETLIBBIND *nlb = (NETLIBBIND*)lParam; struct NetlibUser *nlu = (struct NetlibUser*)wParam; struct NetlibBoundPort *nlbp; - SOCKADDR_IN sin; + SOCKADDR_IN sin = {0}; + SOCKADDR_IN6 sin6 = {0}; int foundPort = 0; UINT dwThreadId; @@ -178,23 +207,24 @@ INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam) nlbp->handleType = NLH_BOUNDPORT; nlbp->nlu = nlu; nlbp->pfnNewConnectionV2 = nlb->pfnNewConnectionV2; - nlbp->s = socket(AF_INET, SOCK_STREAM, 0); + + nlbp->s = socket(PF_INET, SOCK_STREAM, 0); + nlbp->s6 = socket(PF_INET6, SOCK_STREAM, 0); nlbp->pExtra = (nlb->cbSize != NETLIBBIND_SIZEOF_V1) ? nlb->pExtra : NULL; - if (nlbp->s == INVALID_SOCKET) + if (nlbp->s == INVALID_SOCKET && nlbp->s6 == 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; + sin6.sin6_family = AF_INET6; /* if the netlib user wanted a free port given in the range, then they better have given wPort==0, let's hope so */ if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { - if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, &nlu->outportnum)) + if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { NetlibLogf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); SetLastError(WSAEADDRINUSE); @@ -210,52 +240,78 @@ INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam) { 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); + sin6.sin6_port = htons(nlb->wPort); } if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) + { + SOCKADDR_IN sin = {0}; + int len = sizeof(sin); + if (!getsockname(nlbp->s, (PSOCKADDR)&sin, &len)) + sin6.sin6_port = sin.sin_port; + foundPort = 1; + } + + if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) foundPort = 1; } if (!foundPort) { NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"bind",WSAGetLastError()); closesocket(nlbp->s); + closesocket(nlbp->s6); mir_free(nlbp); return 0; } - if (listen(nlbp->s, 5)) + if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"listen",WSAGetLastError()); closesocket(nlbp->s); + closesocket(nlbp->s6); mir_free(nlbp); return 0; } - { int len; - DWORD extIP; + if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) + { + NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"listen",WSAGetLastError()); + closesocket(nlbp->s); + closesocket(nlbp->s6); + mir_free(nlbp); + return 0; + } - ZeroMemory(&sin,sizeof(sin)); - len = sizeof(sin); - if (getsockname(nlbp->s,(SOCKADDR *)&sin,&len)) + { + SOCKADDR_INET_M sin = {0}; + int len = sizeof(sin); + if (!getsockname(nlbp->s, (PSOCKADDR)&sin, &len)) + { + nlb->wPort = ntohs(sin.Ipv4.sin_port); + nlb->dwInternalIP = ntohl(sin.Ipv4.sin_addr.S_un.S_addr); + } + else if (!getsockname(nlbp->s6, (PSOCKADDR)&sin, &len)) + nlb->wPort = ntohs(sin.Ipv6.sin6_port); + else { NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"getsockname",WSAGetLastError()); closesocket(nlbp->s); + closesocket(nlbp->s6); 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; - + char hostname[64] = ""; gethostname(hostname, SIZEOF(hostname)); - he = gethostbyname(hostname); - if (he && he->h_addr_list[0]) - nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr_list[0]); - } + + PHOSTENT he = gethostbyname(hostname); + if (he && he->h_addr) + nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); + } + + DWORD extIP; if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, nlb->cbSize > NETLIBBIND_SIZEOF_V2)) { @@ -282,6 +338,7 @@ INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam) } } } + 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 index 970a28cb10..2893344ade 100644 --- a/src/modules/netlib/netlibhttp.cpp +++ b/src/modules/netlib/netlibhttp.cpp @@ -525,9 +525,9 @@ INT_PTR NetlibHttpSendRequest(WPARAM wParam, LPARAM lParam) if (ip && szHost) { mir_free(szHost); - szHost = (char*)mir_alloc(30); + szHost = (char*)mir_alloc(64); if (cln) *cln = ':'; - mir_snprintf(szHost, 30, "%s%s", inet_ntoa(*(PIN_ADDR)&ip), cln ? cln : ""); + mir_snprintf(szHost, 64, "%s%s", inet_ntoa(*(PIN_ADDR)&ip), cln ? cln : ""); } /* if (ip && pszUrl[0] != '/') diff --git a/src/modules/netlib/netlibopenconn.cpp b/src/modules/netlib/netlibopenconn.cpp index b9d4753b9a..f24906b20c 100644 --- a/src/modules/netlib/netlibopenconn.cpp +++ b/src/modules/netlib/netlibopenconn.cpp @@ -219,15 +219,19 @@ static int NetlibInitSocks5Connection(struct NetlibConnection *nlc,struct Netlib int nHostLen; DWORD hostIP; - if(nlc->dnsThroughProxy) { - if((hostIP=inet_addr(nloc->szHost))==INADDR_NONE) - nHostLen=lstrlenA(nloc->szHost)+1; - else nHostLen=4; + if (nlc->dnsThroughProxy) + { + hostIP = inet_addr(nloc->szHost); + if(hostIP == INADDR_NONE) + nHostLen = lstrlenA(nloc->szHost)+1; + else nHostLen = 4; } - else { - if((hostIP=DnsLookup(nlu,nloc->szHost))==0) + else + { + hostIP = DnsLookup(nlu,nloc->szHost); + if (hostIP == 0) return 0; - nHostLen=4; + nHostLen = 4; } pInit=(PBYTE)mir_alloc(6+nHostLen); pInit[0]=5; //SOCKS5 @@ -394,8 +398,9 @@ static bool my_connectIPv4(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc) if (Miranda_Terminated()) return false; } - SOCKADDR_IN sin = {0}; - sin.sin_family = AF_INET; + PHOSTENT he; + SOCKADDR_IN sin = {0}; + sin.sin_family = AF_INET; if (nlc->proxyType) { @@ -405,107 +410,124 @@ static bool my_connectIPv4(NetlibConnection *nlc, NETLIBOPENCONNECTION * 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); + he = gethostbyname(nlc->szProxyServer); } else { - if (!nloc || !nloc->szHost) return false; + if (!nloc || !nloc->szHost || nloc->szHost[0] == '[' || strchr(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); + he = gethostbyname(nloc->szHost); } -retry: - nlc->s = socket(AF_INET,nloc->flags & NLOCF_UDP ? SOCK_DGRAM : SOCK_STREAM, 0); - if (nlc->s == INVALID_SOCKET) return false; + for (char** har = he->h_addr_list; *har && !Miranda_Terminated(); ++har) + { + sin.sin_addr.s_addr = *(u_long*)*har; - // return the socket to non blocking - if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) return false; + char* szIp = NetlibAddressToString((SOCKADDR_INET_M*)&sin); + NetlibLogf(nlc->nlu,"(%p) Connecting to ip %s ....", nlc, szIp); + mir_free(szIp); - 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"); - } +retry: + nlc->s = socket(AF_INET, nloc->flags & NLOCF_UDP ? SOCK_DGRAM : SOCK_STREAM, 0); + if (nlc->s == INVALID_SOCKET) return false; - // try a connect - if (connect(nlc->s, (LPSOCKADDR)&sin, sizeof(sin)) == 0) - { - goto unblock; - } + // return the socket to non blocking + if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) return false; - // didn't work, was it cos of nonblocking? - if (WSAGetLastError() != WSAEWOULDBLOCK) - { - rc = SOCKET_ERROR; - goto unblock; - } + if (nlc->nlu->settings.specifyOutgoingPorts && nlc->nlu->settings.szOutgoingPorts && nlc->nlu->settings.szOutgoingPorts[0]) + { + if (!BindSocketToPort(nlc->nlu->settings.szOutgoingPorts, nlc->s, INVALID_SOCKET, &nlc->nlu->inportnum)) + NetlibLogf(nlc->nlu,"Netlib connect: Not enough ports for outgoing connections specified"); + } - 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) + // try a connect + if (connect(nlc->s, (LPSOCKADDR)&sin, sizeof(sin)) == 0) + { + rc = 0; break; + } + + // didn't work, was it cos of nonblocking? + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + rc = SOCKET_ERROR; + + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + continue; + } + + 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)) + 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()) { - // connection was successful - rc = 0; - } - if (FD_ISSET(nlc->s, &r)) + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + else if (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2 && + nloc->waitcallback != NULL && nloc->waitcallback(&dwTimeout) == 0) { - // connection was closed rc = SOCKET_ERROR; - lasterr = WSAECONNRESET; + lasterr = ERROR_TIMEOUT; + break; } - if (FD_ISSET(nlc->s, &e)) + if (--dwTimeout == 0) { - // 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; - } + lasterr = ERROR_TIMEOUT; + break; } - 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; } -unblock: notblocking = 0; - ioctlsocket(nlc->s, FIONBIO, ¬blocking); - if (lasterr) SetLastError(lasterr); + if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); + if (rc && lasterr) SetLastError(lasterr); return rc == 0; } @@ -548,7 +570,7 @@ static bool my_connectIPv6(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc) hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; } - + if (nlc->proxyType) { if (!nlc->szProxyServer) return false; @@ -579,8 +601,11 @@ static bool my_connectIPv6(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc) } } - for (ai = air; ai && !Miranda_Terminated(); ai = ai->ai_next) + for (ai = air; ai && !Miranda_Terminated(); ai = ai->ai_next) { + char* szIp = NetlibAddressToString((SOCKADDR_INET_M*)ai->ai_addr); + NetlibLogf(nlc->nlu,"(%p) Connecting to ip %s ....", nlc, szIp); + mir_free(szIp); retry: nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (nlc->s == INVALID_SOCKET) return false; @@ -590,7 +615,9 @@ retry: 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)) + SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; + SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; + if (!BindSocketToPort(nlc->nlu->settings.szOutgoingPorts, s, s6, &nlc->nlu->inportnum)) NetlibLogf(nlc->nlu,"Netlib connect: Not enough ports for outgoing connections specified"); } @@ -605,7 +632,10 @@ retry: if (WSAGetLastError() != WSAEWOULDBLOCK) { rc = SOCKET_ERROR; - break; + + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + continue; } for (;;) // timeout loop diff --git a/src/modules/netlib/netlibsock.cpp b/src/modules/netlib/netlibsock.cpp index 875c47b0a1..8ec6a473f5 100644 --- a/src/modules/netlib/netlibsock.cpp +++ b/src/modules/netlib/netlibsock.cpp @@ -2,7 +2,7 @@ Miranda IM: the free IM client for Microsoft* Windows* -Copyright 2000-2009 Miranda ICQ/IM project, +Copyright 2000-2012 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. @@ -23,69 +23,73 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "commonheaders.h" #include "netlib.h" -extern HANDLE hConnectionHeaderMutex,hSendEvent,hRecvEvent; +extern HANDLE hConnectionHeaderMutex, hSendEvent, hRecvEvent; -INT_PTR NetlibSend(WPARAM wParam,LPARAM lParam) +INT_PTR NetlibSend(WPARAM wParam, LPARAM lParam) { - struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; - NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam; + NetlibConnection *nlc = (NetlibConnection*)wParam; + NETLIBBUFFER *nlb = (NETLIBBUFFER*)lParam; INT_PTR result; - if ( nlb == NULL ) { + if (nlb == NULL) + { SetLastError(ERROR_INVALID_PARAMETER); return SOCKET_ERROR; } - if ( !NetlibEnterNestedCS( nlc, NLNCS_SEND )) + 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 ); + 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 result = NetlibHttpGatewayPost(nlc, nlb->buf, nlb->len, nlb->flags); } else { - NetlibDumpData( nlc, ( PBYTE )nlb->buf, nlb->len, 1, nlb->flags ); + NetlibDumpData(nlc, (PBYTE)nlb->buf, nlb->len, 1, nlb->flags); if (nlc->hSsl) - result = si.write( nlc->hSsl, nlb->buf, nlb->len ); + result = si.write(nlc->hSsl, nlb->buf, nlb->len); else - result = send( nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF ); + result = send(nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF); } - NetlibLeaveNestedCS( &nlc->ncsSend ); + NetlibLeaveNestedCS(&nlc->ncsSend); - if ((( THook* )hSendEvent)->subscriberCount ) { + if (((THook*)hSendEvent)->subscriberCount) + { NETLIBNOTIFY nln = { nlb, result }; - CallHookSubscribers( hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user ); + CallHookSubscribers(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); } return result; } -INT_PTR NetlibRecv(WPARAM wParam,LPARAM lParam) +INT_PTR NetlibRecv(WPARAM wParam, LPARAM lParam) { - struct NetlibConnection *nlc = (struct NetlibConnection*)wParam; - NETLIBBUFFER* nlb = ( NETLIBBUFFER* )lParam; + NetlibConnection *nlc = (NetlibConnection*)wParam; + NETLIBBUFFER* nlb = (NETLIBBUFFER*)lParam; int recvResult; - if ( nlb == NULL ) { - SetLastError( ERROR_INVALID_PARAMETER ); + if (nlb == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); return SOCKET_ERROR; } - if ( !NetlibEnterNestedCS( nlc, NLNCS_RECV )) + if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) return SOCKET_ERROR; - if ( nlc->usingHttpGateway && !( nlb->flags & MSG_RAW )) - recvResult = NetlibHttpGatewayRecv( nlc, nlb->buf, nlb->len, nlb->flags ); + 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 ); + 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 ); + recvResult = recv(nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF); } - NetlibLeaveNestedCS( &nlc->ncsRecv ); + NetlibLeaveNestedCS(&nlc->ncsRecv); if (recvResult <= 0) return recvResult; @@ -101,18 +105,20 @@ INT_PTR NetlibRecv(WPARAM wParam,LPARAM lParam) static int ConnectionListToSocketList(HANDLE *hConns, fd_set *fd, int& pending) { - struct NetlibConnection *nlcCheck; + NetlibConnection *nlcCheck; int i; FD_ZERO(fd); - for(i=0;hConns[i] && hConns[i]!=INVALID_HANDLE_VALUE && ihandleType!=NLH_CONNECTION && nlcCheck->handleType!=NLH_BOUNDPORT) { + for (i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) + { + nlcCheck = (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 )) + if (si.pending(nlcCheck->hSsl)) pending++; } return 1; @@ -120,8 +126,9 @@ static int ConnectionListToSocketList(HANDLE *hConns, fd_set *fd, int& pending) INT_PTR NetlibSelect(WPARAM,LPARAM lParam) { - NETLIBSELECT *nls=(NETLIBSELECT*)lParam; - if (nls==NULL || nls->cbSize!=sizeof(NETLIBSELECT)) { + NETLIBSELECT *nls = (NETLIBSELECT*)lParam; + if (nls == NULL || nls->cbSize != sizeof(NETLIBSELECT)) + { SetLastError(ERROR_INVALID_PARAMETER); return SOCKET_ERROR; } @@ -132,54 +139,58 @@ INT_PTR NetlibSelect(WPARAM,LPARAM lParam) 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; + 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); + 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)) { + 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); + 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; + 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); + int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? NULL : &tv); - WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + 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; + NetlibConnection *conn = NULL; int j; - for (j=0; jhReadConns[j]; - if (conn==NULL || conn==INVALID_HANDLE_VALUE) break; + for (j = 0; j < FD_SETSIZE; j++) + { + conn = (NetlibConnection*)nls->hReadConns[j]; + if (conn == NULL || conn == INVALID_HANDLE_VALUE) break; if (si.pending(conn->hSsl)) nls->hReadStatus[j] = TRUE; @@ -188,16 +199,153 @@ INT_PTR NetlibSelectEx(WPARAM, LPARAM lParam) else nls->hReadStatus[j] = FD_ISSET(conn->s,&readfd); } - for (j=0; jhWriteConns[j]; - if (conn==NULL || conn==INVALID_HANDLE_VALUE) break; + for (j = 0; j < FD_SETSIZE; j++) + { + conn = (NetlibConnection*)nls->hWriteConns[j]; + if (conn == NULL || conn == INVALID_HANDLE_VALUE) break; nls->hWriteStatus[j] = FD_ISSET(conn->s,&writefd); } - for (j=0; jhExceptConns[j]; - if (conn==NULL || conn==INVALID_HANDLE_VALUE) break; + for (j = 0; j < FD_SETSIZE; j++) + { + conn = (NetlibConnection*)nls->hExceptConns[j]; + if (conn == NULL || conn == INVALID_HANDLE_VALUE) break; nls->hExceptStatus[j] = FD_ISSET(conn->s,&exceptfd); } ReleaseMutex(hConnectionHeaderMutex); return rc; } + +bool NetlibStringToAddress(const char* str, SOCKADDR_INET_M* addr) +{ + if (!str) return false; + + if (MyWSAStringToAddress) + { + int len = sizeof(SOCKADDR_INET_M); + return !MyWSAStringToAddress((char*)str, AF_INET6, NULL, (PSOCKADDR)addr, &len); + } + else + { + unsigned iaddr = inet_addr(str); + if (!iaddr) return false; + + addr->Ipv4.sin_addr.s_addr = iaddr; + addr->Ipv4.sin_family = AF_INET; + return true; + } +} + +char* NetlibAddressToString(SOCKADDR_INET_M* addr) +{ + char saddr[128]; + + if (MyWSAAddressToString) + { + DWORD len = sizeof(saddr); + if (!MyWSAAddressToString((PSOCKADDR)addr, sizeof(*addr), NULL, saddr, &len)) + return mir_strdup(saddr); + } + else if (addr->si_family == AF_INET) + { + char *szIp = inet_ntoa(addr->Ipv4.sin_addr); + if (addr->Ipv4.sin_port != 0) + { + mir_snprintf(saddr, sizeof(saddr), "%s:%d", szIp, htons(addr->Ipv4.sin_port)); + return mir_strdup(saddr); + } + else + return mir_strdup(szIp); + } + return NULL; +} + +void NetlibGetConnectionInfo(NetlibConnection* nlc, NETLIBCONNINFO *connInfo) +{ + if (!nlc || !connInfo || connInfo->cbSize < sizeof(NETLIBCONNINFO)) return; + + SOCKADDR_INET_M sin = {0}; + int len = sizeof(sin); + + if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) + { + connInfo->wPort = ntohs(sin.Ipv4.sin_port); + connInfo->dwIpv4 = sin.si_family == AF_INET ? htonl(sin.Ipv4.sin_addr.s_addr) : 0; + + char *szTmp = NetlibAddressToString(&sin); + strncpy(connInfo->szIpPort, szTmp, sizeof(connInfo->szIpPort)); + connInfo->szIpPort[sizeof(connInfo->szIpPort) - 1] = 0; + mir_free(szTmp); + } +} + +inline bool IsAddrGlobal(const IN6_ADDR *a) +{ + unsigned char High = a->s6_bytes[0] & 0xf0; + return High != 0 && High != 0xf0; +} + +static NETLIBIPLIST* GetMyIpv6(unsigned flags) +{ + addrinfo *air = NULL, *ai, hints = {0}; + const char *szMyHost = ""; + + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + + if (MyGetaddrinfo(szMyHost, NULL, &hints, &air)) + return NULL; + + unsigned n = 0; + for (ai = air; ai; ai = ai->ai_next) + { + SOCKADDR_INET_M* iaddr = (SOCKADDR_INET_M*)ai->ai_addr; + if (ai->ai_family == AF_INET || + (ai->ai_family == AF_INET6 && + (!(flags & 1) || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) + ++n; + } + + NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); + addr->cbNum = n; + + unsigned i = 0; + for (ai = air; ai; ai = ai->ai_next) + { + SOCKADDR_INET_M* iaddr = (SOCKADDR_INET_M*)ai->ai_addr; + if (ai->ai_family == AF_INET || + (ai->ai_family == AF_INET6 && + (!(flags & 1) || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) + { + + char* szIp = NetlibAddressToString(iaddr); + if (szIp) strcpy(addr->szIp[i++], szIp); + mir_free(szIp); + } + } + MyFreeaddrinfo(air); + return addr; +} + +static NETLIBIPLIST* GetMyIpv4(void) +{ + char hostname[256] = ""; + + gethostname(hostname, sizeof(hostname)); + PHOSTENT he = gethostbyname(hostname); + + unsigned n; + for (n = 0; he->h_addr_list[n]; ++n) ; + + NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); + addr->cbNum = n; + + for (unsigned i = 0; i < n; ++i) + strcpy(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i])); + + return addr; +} + +NETLIBIPLIST* GetMyIp(unsigned flags) +{ + return (MyGetaddrinfo && MyFreeaddrinfo) ? GetMyIpv6(flags) : GetMyIpv4(); +} diff --git a/src/modules/updatenotify/updatenotify.cpp b/src/modules/updatenotify/updatenotify.cpp index ef955ba3c5..5b2162a041 100644 --- a/src/modules/updatenotify/updatenotify.cpp +++ b/src/modules/updatenotify/updatenotify.cpp @@ -214,17 +214,11 @@ static DWORD UpdateNotifyMakeVersion(char *str) { return PLUGIN_MAKE_VERSION(a1, a2, a3, a4); } -static int UpdateNotifyIsNewer(DWORD dwCurrent, DWORD dwTest) { - if (dwTest>dwCurrent) - return 1; - return 0; -} +inline bool UpdateNotifyIsNewer(DWORD dwCurrent, DWORD dwTest) +{ return dwTest > dwCurrent; } -static int UpdateNotifyReleaseDataValid(UpdateNotifyReleaseData *d) { - if (d&&d->szVersionPublic&&d->szVersion&&d->szDownload&&d->szNotes) - return 1; - return 0; -} +inline bool UpdateNotifyReleaseDataValid(UpdateNotifyReleaseData *d) +{ return d && d->szVersionPublic && d->szVersion && d->szDownload && d->szNotes; } static void UpdateNotifyFreeReleaseData(UpdateNotifyReleaseData *d) { if (!d) -- cgit v1.2.3