#include "windows.h" #include "types.h" #include "winsock.h" #include "wsasync.h" #ifdef UNDER_CE #include #endif DWORD CopyHostEnt(PHOSTENT lpWinsockHostEnt, PHOSTENT lpHostEnt, LPDWORD lpdwHostEntBufferSize); #define ProxyDbgAssert(x) ASSERT(x) // If Count is not already aligned, then // round Count up to an even multiple of "Pow2". "Pow2" must be a power of 2. // // DWORD // ROUND_UP_COUNT( // IN DWORD Count, // IN DWORD Pow2 // ); #define ROUND_UP_COUNT(Count,Pow2) \ ( ((Count)+(Pow2)-1) & (~((Pow2)-1)) ) #define ALIGN_DWORD 4 #ifdef DEBUG DBGPARAM dpCurSettings = { TEXT("WSASYNC"), { TEXT("Init"), TEXT("HostName"),TEXT("Undefined"),TEXT("Undefined"), TEXT("Recv"),TEXT("Send"),TEXT("Undefined"),TEXT("Undefined"), TEXT("Undefined"),TEXT("Undefined"),TEXT("Interface"),TEXT("Misc"), TEXT("Alloc"), TEXT("Function"), TEXT("Warning"), TEXT("Error") }, 0x0000c000 }; #define ZONE_INIT DEBUGZONE(0) // 0x0001 #define ZONE_HNAME DEBUGZONE(1) // 0x0002 //#define ZONE_??? DEBUGZONE(2) // 0x0004 //#define ZONE_??? DEBUGZONE(3) // 0x0008 #define ZONE_RECV DEBUGZONE(4) // 0x0010 #define ZONE_SEND DEBUGZONE(5) // 0x0020 #define ZONE_SELECT DEBUGZONE(6) // 0x0040 #define ZONE_CLOSE DEBUGZONE(7) // 0x0080 //#define ZONE_??? DEBUGZONE(8) // 0x0100 #define ZONE_MSG DEBUGZONE(9) // 0x0200 #define ZONE_INTERFACE DEBUGZONE(10) // 0x0400 #define ZONE_MISC DEBUGZONE(11) // 0x0800 #define ZONE_ALLOC DEBUGZONE(12) // 0x1000 #define ZONE_FUNCTION DEBUGZONE(13) // 0x2000 #define ZONE_WARN DEBUGZONE(14) // 0x4000 #define ZONE_ERROR DEBUGZONE(15) // 0x8000 #endif typedef struct GetNames { struct GetNames *pNext; HANDLE hThread; HWND hWnd; uint wMsg; char *pBuf; DWORD dwBufLen; char *pName; DWORD Status; } GetNames; GetNames *v_pNameList; CRITICAL_SECTION v_NameListCS; BOOL v_fNameListInit = FALSE; #define MAXALIASES 15 // We only support 5 aliases #define AVG_HOST_LEN 40 // To calculate buff space #define MAXADDRS 15 // Max of 5 IP addrs typedef struct FULL_HOSTENT { HOSTENT hostent; PSTR host_aliases[MAXALIASES+1]; char hostbuf[(MAXALIASES+1)*AVG_HOST_LEN]; PSTR h_addr_ptrs[MAXADDRS + 1]; uchar hostaddr[MAXADDRS*4]; } FULL_HOSTENT; GetNames **_FindName(HANDLE hThread) { GetNames **ppName; ppName = &v_pNameList; while (*ppName && hThread != (*ppName)->hThread) ppName = &(*ppName)->pNext; return ppName; } // _FindName() // HANDLE CreateThread(NULL, 0, Addr, pParam, NULL, ThreadId); // BOOL PostMessage(hWnd, wMsg, wParam, lParam); void CallGetHostByName(DWORD Name) { GetNames **ppName, *pName = (GetNames *)Name; HOSTENT *pHostent; #if 0 FULL_HOSTENT *pFull; int cLen, i; char *p; #endif DWORD lParam; EnterCriticalSection(&v_NameListCS); // put it in list of pending items pName->pNext = v_pNameList; v_pNameList = pName; LeaveCriticalSection(&v_NameListCS); pHostent = gethostbyname(pName->pName); DEBUGMSG(ZONE_HNAME, (TEXT("gethostbyname returned %x\r\n"), pHostent)); // take this task out of the pending items list EnterCriticalSection(&v_NameListCS); ppName = _FindName(pName->hThread); ASSERT(*ppName); ASSERT(*ppName == pName); *ppName = pName->pNext; pName->pNext = NULL; LeaveCriticalSection(&v_NameListCS); if (pName->Status != 0xffffffff) { if (pHostent) { #if 0 lParam = 0; pFull = (FULL_HOSTENT *)pName->pBuf; memcpy(pFull, pHostent, sizeof(FULL_HOSTENT)); // now change the pointers to point to the correct places pFull->hostent.h_name = pFull->hostbuf; cLen = strlen(pFull->hostbuf) + 1; p = pFull->hostbuf + cLen; // do the aliases pFull->hostent.h_aliases = pFull->host_aliases; i = 0; while (pHostent->h_aliases[i]) { p += strlen(pHostent->h_aliases[i]) + 1; pFull->hostent.h_aliases[i++] = p; } // h_addrtype & h_length should have been copied with memcpy // do the h_addr_list pFull->hostent.h_addr_list = pFull->h_addr_ptrs; while(pHostent->h_addr_list[i]) { pFull->h_adddr_ptrs[i] = } p = pFull->hostaddr; i = 0; while (pHostent->h_addr_list[i]) { pFull->h_addr_ptrs[i] = p; (int *)p = *(int *)pHostent->h_addr_list[i++]; p += 4; } #endif lParam = CopyHostEnt(pHostent, (PHOSTENT)pName->pBuf, &pName->dwBufLen); lParam <<= 16; lParam += pName->dwBufLen; } else { // if (pHostent) // we must have some error condition lParam = GetLastError(); lParam <<= 16; lParam += sizeof(FULL_HOSTENT); } // BUGBUG: we don't retry on failure PostMessage(pName->hWnd, pName->wMsg, (WPARAM)pName->hThread, lParam); } // if (pName->Status) LocalFree(pName->pName); LocalFree(pName); } // CallGetHostByName() HANDLE WSAAsyncGetHostByName(HWND hWnd, unsigned wMsg, const char FAR *name, char FAR * buf, int buflen) { HANDLE hThread; DWORD ThreadId; GetNames *pName; DWORD Status = 0; int cc; if (buflen < sizeof(HOSTENT)) { Status = WSAENOBUFS; } else if (pName = LocalAlloc(LPTR, sizeof(*pName))) { pName->hWnd = hWnd; pName->wMsg = wMsg; pName->pBuf = buf; pName->dwBufLen = (DWORD)buflen; cc = strlen(name) + 1; if (pName->pName = LocalAlloc(LPTR, cc)) { memcpy(pName->pName, name, cc); pName->Status = 0; if (!v_fNameListInit) { InitializeCriticalSection(&v_NameListCS); v_fNameListInit = TRUE; } EnterCriticalSection(&v_NameListCS); pName->hThread = hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CallGetHostByName, pName, 0, &ThreadId); LeaveCriticalSection(&v_NameListCS); CloseHandle(hThread); } else { LocalFree(pName); Status = WSAENOBUFS; } } else { Status = WSAENOBUFS; } if (Status) { SetLastError(Status); return (HANDLE)0; } else return (HANDLE)hThread; } // WSAASyncGetHostByName() int WSACancelAsyncRequest (HANDLE hAsyncTaskHandle) { GetNames **ppName; int Status; EnterCriticalSection(&v_NameListCS); ppName = _FindName(hAsyncTaskHandle); if (*ppName) { (*ppName)->Status = 0xffffffff; LeaveCriticalSection(&v_NameListCS); Status = 0; } else { LeaveCriticalSection(&v_NameListCS); Status = SOCKET_ERROR; SetLastError(WSAEINVAL); } return Status; } // WSACancelAsyncRequest() typedef int (*WSRecvFn)(SOCKET, char *, int, int); typedef int (*WSSendFn)(SOCKET, const char *, int, int); typedef int (*WSCloseFn)(SOCKET); WSRecvFn v_pRecv; WSSendFn v_pSend; WSCloseFn v_pClose; SOCKADDR_IN SockAddr; #ifndef IP_LOOPBACK #define IP_LOOPBACK 0x100007f #endif static fd_set ReadSet, WriteSet, ExcSet; static int v_fDone; static SOCKET v_Sock= INVALID_SOCKET; static SOCKET v_WakeSock; static SOCKET v_SendSock; static int v_SelEvents; static int v_Disabled; static HWND v_hWnd; static int v_wMsg; static int v_fAlready; // implicitly set to FALSE CRITICAL_SECTION v_EventCS; void SelectWorker(DWORD Sock); int WSAAsyncSelect (SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent) { static int fInit = FALSE; HINSTANCE hinst; HANDLE hThread; DWORD ThreadId; int True = 1; int len; if (! fInit) { fInit = TRUE; InitializeCriticalSection(&v_EventCS); if (hinst = LoadLibrary(TEXT("winsock.dll"))) { v_pRecv = (WSRecvFn)GetProcAddressW(hinst, TEXT("recv")); v_pSend = (WSSendFn)GetProcAddressW(hinst, TEXT("send")); v_pClose = (WSCloseFn)GetProcAddressW(hinst, TEXT("closesocket")); if (! (v_pRecv && v_pSend && v_pClose)) { DEBUGMSG(ZONE_WARN | ZONE_ERROR, (TEXT("Couldn't get ProcAddr of winsock recv/send\r\n"))); SetLastError(WSAENOBUFS); return SOCKET_ERROR; } } else { DEBUGMSG(ZONE_WARN | ZONE_ERROR, (TEXT("Couldn't LoadLibrary winsock.dll\r\n"))); SetLastError(WSAENOBUFS); return SOCKET_ERROR; } // now WakeSock and SendSock initialized at init time only v_WakeSock = socket(AF_INET, SOCK_DGRAM, 0); if (INVALID_SOCKET == v_WakeSock) { DEBUGMSG(ZONE_WARN|ZONE_ERROR, (TEXT("Can't create dgram socket\r\n"))); SetLastError(WSAENOBUFS); return SOCKET_ERROR; } memset ((char *)&SockAddr, 0, sizeof(SockAddr)); SockAddr.sin_family = AF_INET; SockAddr.sin_port = 0; SockAddr.sin_addr.S_un.S_addr = IP_LOOPBACK; if (SOCKET_ERROR == bind(v_WakeSock, (SOCKADDR *)&SockAddr, sizeof(SockAddr))) { DEBUGMSG(ZONE_WARN|ZONE_ERROR, (TEXT("Can't bind WakeSock\r\n"))); return SOCKET_ERROR; } v_SendSock = socket(AF_INET, SOCK_DGRAM, 0); if (INVALID_SOCKET == v_SendSock) { DEBUGMSG(ZONE_WARN|ZONE_ERROR, (TEXT("Can't create send socket\r\n"))); v_pClose(v_WakeSock); SetLastError(WSAENOBUFS); return SOCKET_ERROR; } len = sizeof(SockAddr); getsockname (v_WakeSock, (SOCKADDR *)&SockAddr, &len); connect(v_SendSock, (SOCKADDR *)&SockAddr, sizeof(SockAddr)); } // if (! fInit) EnterCriticalSection(&v_EventCS); if (! lEvent) { v_fDone = TRUE; LeaveCriticalSection(&v_EventCS); return 0; } if (v_fAlready) { LeaveCriticalSection(&v_EventCS); return 0; } else { v_fAlready = TRUE; v_fDone = FALSE; v_hWnd = hWnd; v_wMsg = wMsg; v_SelEvents = lEvent; ASSERT(INVALID_SOCKET == v_Sock); v_Sock = s; v_Disabled = 0; LeaveCriticalSection(&v_EventCS); // set socket to non-blocking if (SOCKET_ERROR == ioctlsocket(s, FIONBIO, &True)) { DEBUGMSG(ZONE_WARN, (TEXT("ioctlsocket FIONBIO failed w/ %d\r\n"), GetLastError())); } hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SelectWorker, (void *)s, 0, &ThreadId); CloseHandle(hThread); } return 0; } void SelectWorker(DWORD Sock) { SOCKET s = (SOCKET) Sock; char c; DWORD lParam = 0; int Status; #ifdef OS_WINCE TIMEVAL tv = {0, 25000}; #endif // OS_WINCE while (2) { FD_ZERO(&ReadSet); FD_ZERO(&WriteSet); FD_ZERO(&ExcSet); if (INVALID_SOCKET != v_WakeSock) { FD_SET(v_WakeSock, &ReadSet); } EnterCriticalSection(&v_EventCS); if (v_fDone) { // do this here b/c of CS v_fAlready = FALSE; // LeaveCriticalSection(&v_EventCS); break; } if (v_SelEvents & ~v_Disabled & (FD_WRITE | FD_CONNECT)) { FD_SET(s, &WriteSet); DEBUGMSG(ZONE_MSG, (TEXT("W\r\n"))); } if (v_SelEvents & ~v_Disabled & FD_READ) { FD_SET(s, &ReadSet); DEBUGMSG(ZONE_MSG, (TEXT("R\r\n"))); } //FD_SET(s, &ExcSet); LeaveCriticalSection(&v_EventCS); DEBUGMSG(ZONE_SELECT, (TEXT("calling select\r\n"))); #ifdef OS_WINCE /* When select(...) is called with a NULL timeval parameter, it does not return after closesocket(...) is called on that socket. To work around that behavior, use a time- out on the call to select(...) (and take advantage of the while(2) wrapping this code-block. */ // This was only made for HPCs, WBT was always fine using the old way, // so we're going to keep it the same. if (g_CEConfig != CE_CONFIG_WBT) { Status = select(0, &ReadSet, &WriteSet, NULL, &tv); } else { Status = select(0, &ReadSet, &WriteSet, NULL, NULL); } #else // OS_WINCE Status = select(0, &ReadSet, &WriteSet, NULL, NULL); #endif // OS_WINCE if (SOCKET_ERROR == Status) { //v_fDone = TRUE; Status = GetLastError(); DEBUGMSG(ZONE_WARN|ZONE_SELECT, (TEXT("Select returned error: %d \r\n"), Status)); } else { if (FD_ISSET(v_WakeSock, &ReadSet)) { v_pRecv(v_WakeSock, &c, 1, 0); } EnterCriticalSection(&v_EventCS); if (FD_ISSET(s, &WriteSet)) { // we need to report connect events only once if (v_SelEvents & ~v_Disabled & FD_CONNECT) { v_Disabled |= FD_CONNECT; lParam = FD_CONNECT; DEBUGMSG(ZONE_MSG, (TEXT("p-C\r\n"))); PostMessage(v_hWnd, v_wMsg, (WPARAM)s, lParam); } if (v_SelEvents & ~v_Disabled & FD_WRITE) { v_Disabled |= FD_WRITE; lParam = FD_WRITE; DEBUGMSG(ZONE_MSG, (TEXT("p-W\r\n"))); PostMessage(v_hWnd, v_wMsg, (WPARAM)s, lParam); } } if (FD_ISSET(s, &ReadSet)) { v_Disabled |= FD_READ; lParam = FD_READ; DEBUGMSG(ZONE_MSG, (TEXT("p-R\r\n"))); PostMessage(v_hWnd, v_wMsg, (WPARAM)s, lParam); } if (FD_ISSET(s, &ExcSet)) { DEBUGMSG(ZONE_WARN, (TEXT("Selects ExcSet is set!!\r\n"))); } LeaveCriticalSection(&v_EventCS); } // else SOCKET_ERROR == Status } // note we have the CS here!!! v_Sock = INVALID_SOCKET; v_hWnd = NULL; LeaveCriticalSection(&v_EventCS); return; } // SelectWorker() int recv(SOCKET s, char *buf, int len, int flags) { char c = (char)7; int Status; DEBUGMSG(ZONE_RECV, (TEXT("+recv\r\n"))); Status = v_pRecv(s, buf, len, flags); EnterCriticalSection(&v_EventCS); if (v_Sock != INVALID_SOCKET && v_Sock == s) { // BUGBUG: should we check for errors as well? if (! Status) { // recv'd 0 bytes DEBUGMSG(ZONE_MSG, (TEXT("p-Close\r\n"))); PostMessage(v_hWnd, v_wMsg, (WPARAM)s, FD_CLOSE); // after this we don't want to re-enable FD_READ's } else if (v_Disabled & FD_READ) { v_Disabled &= ~FD_READ; v_pSend(v_SendSock, &c, 1, 0); } } LeaveCriticalSection(&v_EventCS); DEBUGMSG(ZONE_RECV, (TEXT("-recv\r\n"))); return Status; } // recv int send(SOCKET s, const char *buf, int len, int flags) { char c = (char)8; int Status; DEBUGMSG(ZONE_SEND, (TEXT("+send\r\n"))); Status = v_pSend(s, buf, len, flags); EnterCriticalSection(&v_EventCS); if (v_Sock != INVALID_SOCKET && v_Sock == s) { // BUGBUG: should we check for errors?? if (v_Disabled & FD_WRITE) { v_Disabled &= ~FD_WRITE; v_pSend(v_SendSock, &c, 1, 0); } } LeaveCriticalSection(&v_EventCS); DEBUGMSG(ZONE_SEND, (TEXT("-send\r\n"))); return Status; } // send() int closesocket(SOCKET s) { int Status; DEBUGMSG(ZONE_CLOSE, (TEXT("+close\r\n"))); Status = v_pClose(s); EnterCriticalSection(&v_EventCS); if (v_Sock != INVALID_SOCKET && v_Sock == s) { v_fDone = TRUE; } LeaveCriticalSection(&v_EventCS); DEBUGMSG(ZONE_CLOSE, (TEXT("-close\r\n"))); return Status; } // closesocket() BOOL __stdcall dllentry ( HANDLE hinstDLL, DWORD Op, LPVOID lpvReserved ) { switch (Op) { case DLL_PROCESS_ATTACH : DEBUGREGISTER(hinstDLL); DEBUGMSG (ZONE_INIT, (TEXT(" CXPORT:dllentry ProcessAttach\r\n"))); break; case DLL_PROCESS_DETACH : break; default : break; } return TRUE; } DWORD CopyHostEnt( PHOSTENT lpWinsockHostEnt, PHOSTENT lpHostEnt, LPDWORD lpdwHostEntBufferSize ) /*++ Arguments: Return Value: Windows Error Code. --*/ { DWORD dwSize; LPSTR *lpAliasNames; LPSTR *lpDestAliasNames; LPSTR *lpAddrList; LPSTR *lpDestAddrList; LPBYTE lpNextVariable; LPBYTE lpEndBuffer; DWORD dwNumAliasNames = 0; DWORD dwNumAddresses = 0; DWORD dwDnsNameLen; // // compute the size required. // dwSize = sizeof(HOSTENT); dwDnsNameLen = ROUND_UP_COUNT( (strlen(lpWinsockHostEnt->h_name) + sizeof(CHAR)), ALIGN_DWORD ); dwSize += dwDnsNameLen; lpAliasNames = lpWinsockHostEnt->h_aliases; while( *lpAliasNames != NULL ) { dwSize += ROUND_UP_COUNT( (strlen(*lpAliasNames) + sizeof(CHAR)), ALIGN_DWORD ); dwSize += sizeof(LPSTR); dwNumAliasNames++; lpAliasNames++; } dwSize += sizeof(LPSTR); lpAddrList = lpWinsockHostEnt->h_addr_list; while( *lpAddrList != NULL ) { dwSize += sizeof(DWORD); dwSize += sizeof(LPSTR); dwNumAddresses++; lpAddrList++; } dwSize += sizeof(LPSTR); if( dwSize > *lpdwHostEntBufferSize ) { *lpdwHostEntBufferSize = dwSize; return( WSAENOBUFS ); } // // copy data. // lpNextVariable = (LPBYTE)lpHostEnt + sizeof(HOSTENT); lpEndBuffer = (LPBYTE)lpHostEnt + dwSize; // // copy fixed part. // lpHostEnt->h_addrtype = lpWinsockHostEnt->h_addrtype; lpHostEnt->h_length = lpWinsockHostEnt->h_length; // // copy variable parts. // lpHostEnt->h_name = (LPSTR)lpNextVariable; strcpy( lpHostEnt->h_name, lpWinsockHostEnt->h_name ); lpNextVariable += dwDnsNameLen; ProxyDbgAssert( lpNextVariable < lpEndBuffer); lpHostEnt->h_aliases = (LPSTR *)lpNextVariable; lpNextVariable += (dwNumAliasNames + 1) * sizeof(LPSTR); ProxyDbgAssert( lpNextVariable < lpEndBuffer); lpAliasNames = lpWinsockHostEnt->h_aliases; lpDestAliasNames = lpHostEnt->h_aliases; while( *lpAliasNames != NULL ) { *lpDestAliasNames = (LPSTR)lpNextVariable; strcpy( *lpDestAliasNames, *lpAliasNames ); lpNextVariable += ROUND_UP_COUNT( (strlen(*lpAliasNames) + sizeof(CHAR)), ALIGN_DWORD ); ProxyDbgAssert( lpNextVariable < lpEndBuffer); lpDestAliasNames++; lpAliasNames++; } *lpDestAliasNames = NULL; lpHostEnt->h_addr_list = (LPSTR *)lpNextVariable; lpNextVariable += (dwNumAddresses + 1) * sizeof(LPSTR); ProxyDbgAssert( lpNextVariable < lpEndBuffer); lpAddrList = lpWinsockHostEnt->h_addr_list; lpDestAddrList = lpHostEnt->h_addr_list; while( *lpAddrList != NULL ) { *lpDestAddrList = (LPSTR)lpNextVariable; *(LPDWORD)(*lpDestAddrList) = *(LPDWORD)(*lpAddrList); lpNextVariable += sizeof(DWORD); ProxyDbgAssert( lpNextVariable <= lpEndBuffer); lpDestAddrList++; lpAddrList++; } *lpDestAddrList = NULL; return( ERROR_SUCCESS ); } // ---------------------------------------------------------------- // // The Windows Sockets WSAAsyncGetHostByName function gets host // information corresponding to a host name asynchronously. // // HANDLE WSAAsyncGetHostByName ( HWND hWnd, unsigned int wMsg, const // char FAR * name, char FAR * buf, int buflen ); // // Parameters // // hWnd // // [in] The handle of the window that will receive a message when the // asynchronous request completes. // // wMsg // // [in] The message to be received when the asynchronous request // completes. // // name // // [in] A pointer to the null-terminated name of the host. // // buf // // [out] A pointer to the data area to receive the HOSTENT data. It must // be larger than the size of a HOSTENT structure because the supplied // data area is used by Windows Sockets to contain not only a HOSTENT // structure but any and all of the data referenced by members of the // HOSTENT structure. A buffer of MAXGETHOSTSTRUCT bytes is recommended. // // buflen // // [in] The size of data area the buf parameter. // // Remarks // // This function is an asynchronous version of gethostbyname, and is // used to retrieve host name and address information corresponding to a // host name. Windows Sockets initiates the operation and returns to the // caller immediately, passing back an opaque "asynchronous task handle" // that whichthe application can use to identify the operation. When the // operation is completed, the results (if any) are copied into the // buffer provided by the caller and a message is sent to the // application's window. // // When the asynchronous operation is complete the application's window // hWnd receives message wMsg. The wParam parameter contains the // asynchronous task handle as returned by the original function call. // The high 16 bits of lParam contain any error code. The error code can // be any error as defined in WINSOCK2.H. An error code of zero // indicates successful completion of the asynchronous operation. On // successful completion, the buffer supplied to the original function // call contains a HOSTENT structure. To access the elements of this // structure, the original buffer address should be cast to a HOSTENT // structure pointer and accessed as appropriate. // // If the error code is WSAENOBUFS, the size of the buffer specified by // buflen in the original call was too small to contain all the // resulting information. In this case, the low 16 bits of lParam // contain the size of buffer required to supply all the requisite // information. If the application decides that the partial data is // inadequate, it can reissue the WSAAsyncGetHostByAddr function call // with a buffer large enough to receive all the desired information // (that is, no smaller than the low 16 bits of lParam). // // The buffer supplied to this function is used by Windows Sockets to // construct a HOSTENT structure together with the contents of data // areas referenced by members of the same HOSTENT structure. To avoid // the WSAENOBUFS error, the application should provide a buffer of at // least MAXGETHOSTSTRUCT bytes (as defined in WINSOCK2.H). // // The error code and buffer length should be extracted from the lParam // using the macros WSAGETASYNCERROR and WSAGETASYNCBUFLEN, defined in // WINSOCK2.H as: // // #define WSAGETASYNCERROR(lParam) HIWORD(lParam) #define // WSAGETASYNCBUFLEN(lParam) LOWORD(lParam) The use of these macros will // maximize the portability of the source code for the application. // // WSAAsyncGetHostByName is guaranteed to resolve the string returned by // a successful call to gethostname. // // Return Values // // The return value specifies whether or not the asynchronous operation // was successfully initiated. Note that it does not imply success or // failure of the operation itself. // // If the operation was successfully initiated, WSAAsyncGetHostByName // returns a nonzero value of type HANDLE that is the asynchronous task // handle (not to be confused with a Windows HTASK) for the request. // This value can be used in two ways. It can be used to cancel the // operation using WSACancelAsyncRequest. It can also be used to match // up asynchronous operations and completion messages by examining the // wParam message parameter. // // If the asynchronous operation could not be initiated, // WSAAsyncGetHostByName returns a zero value, and a specific error // number can be retrieved by calling WSAGetLastError. // // Error Codes // // The following error codes can be set when an application window // receives a message. As described above, they can be extracted from // the lParam in the reply message using the WSAGETASYNCERROR macro. // // WSAENETDOWN The network subsystem has failed. // // WSAENOBUFS Insufficient buffer space is available. // // WSAEFAULT name or buf is not in a valid part of the process address // space. // // WSAHOST_NOT_FOUND Authoritative Answer Host not found. // // WSATRY_AGAIN Non-Authoritative Host not found, or SERVERFAIL. // // WSANO_RECOVERY Nonrecoverable errors, FORMERR, REFUSED, NOTIMP. // // WSANO_DATA Valid name, no data record of requested type. // // The following errors can occur at the time of the function call, and // indicate that the asynchronous operation could not be initiated. // // WSANOTINITIALISED A successful WSAStartup must occur before using // this function. // // WSAENETDOWN The network subsystem has failed. // // WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or // the service provider is still processing a callback function. // // WSAEWOULDBLOCK The asynchronous operation cannot be scheduled at this // time due to resource or other constraints within the Windows Sockets // implementation. // // ---------------------------------------------------------------- // // ---------------------------------------------------------------- // // The Windows Sockets WSACancelAsyncRequest function cancels an // incomplete asynchronous operation. // // int WSACancelAsyncRequest ( // HANDLE hAsyncTaskHandle // ); // // Parameters // hAsyncTaskHandle // [in] Specifies the asynchronous operation to be canceled. // // Remarks // The WSACancelAsyncRequest function is used to cancel an asynchronous // operation that was initiated by one of the WSAAsyncGetXByY functions // such as WSAAsyncGetHostByName. The operation to be canceled is // identified by the hAsyncTaskHandle parameter, which should be set to // the asynchronous task handle as returned by the initiating // WSAAsyncGetXByY function. // // Return Values // The value returned by WSACancelAsyncRequest is zero if the operation // was successfully canceled. Otherwise, the value SOCKET_ERROR is // returned, and a specific error number may be retrieved by calling // WSAGetLastError. // // Comments // An attempt to cancel an existing asynchronous WSAAsyncGetXByY // operation can fail with an error code of WSAEALREADY for two reasons. // First, the original operation has already completed and the // application has dealt with the resultant message. Second, the // original operation has already completed but the resultant message is // still waiting in the application window queue. // // Note // It is unclear whether the application can usefully distinguish // between WSAEINVAL and WSAEALREADY, since in both cases the error // indicates that there is no asynchronous operation in progress with // the indicated handle. [Trivial exception: zero is always an invalid // asynchronous task handle.] The Windows Sockets specification does not // prescribe how a conformant Windows Sockets provider should // distinguish between the two cases. For maximum portability, a Windows // Sockets application should treat the two errors as equivalent. // // // Error Codes // WSANOTINITIALISED A successful WSAStartup must occur before using // this function. // // WSAENETDOWN The network subsystem has failed. // // WSAEINVAL Indicates that the specified asynchronous task handle was // invalid // // WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or // the service provider is still processing a callback function. // // WSAEALREADY The asynchronous routine being canceled has already // completed. // // ---------------------------------------------------------------- // ---------------------------------------------------------------- // // // The Windows Sockets WSAAsyncSelect function requests Windows message-based notification of network events for a socket. // // int WSAAsyncSelect ( // SOCKET s, // HWND hWnd, // unsigned int wMsg, // long lEvent // ); // // Parameters // s // [in] A descriptor identifying the socket for which event notification // is required. // // hWnd // [in] A handle identifying the window that should receive a message // when a network event occurs. // // wMsg // [in] The message to be received when a network event occurs. // // lEvent // [in] A bitmask that specifies a combination of network events in // which the application is interested. // // Remarks // This function is used to request that the Windows Sockets DLL should // send a message to the window hWnd whenever it detects any of the // network events specified by the lEvent parameter. The message that // should be sent is specified by the wMsg parameter. The socket for // which notification is required is identified by s. // // This function automatically sets socket s to nonblocking mode, // regardless of the value of lEvent. See ioctlsocket about how to set // the nonoverlapped socket back to blocking mode. // // The lEvent parameter is constructed by or'ing any of the values // specified in the following list. // // Value Meaning // FD_READ Want to receive notification of readiness for reading // // FD_WRITE Want to receive notification of readiness for writing // // FD_OOB Want to receive notification of the arrival of // out-of-band data // // FD_ACCEPT Want to receive notification of incoming connections // // FD_CONNECT Want to receive notification of completed connection // // FD_CLOSE Want to receive notification of socket closure // // FD_QOS Want to receive notification of socket Quality of Service // (QOS) changes // // FD_GROUP_QOS Want to receive notification of socket group Quality of // Service (QOS) changes // // Issuing a WSAAsyncSelect for a socket cancels any previous // WSAAsyncSelect or WSAEventSelect for the same socket. For example, to // receive notification for both reading and writing, the application // must call WSAAsyncSelect with both FD_READ and FD_WRITE, as follows: // // rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE); // // It is not possible to specify different messages for different // events. The following code will not work; the second call will cancel // the effects of the first, and only FD_WRITE events will be reported // with message wMsg2: // // rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ); // rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE); // // To cancel all notification (that is, to indicate that Windows Sockets // should send no further messages related to network events on the // socket) lEvent should be set to zero. // // rc = WSAAsyncSelect(s, hWnd, 0, 0); // // Although in this instance WSAAsyncSelect immediately disables event // message posting for the socket, it is possible that messages can be // waiting in the application's message queue. The application must // therefore be prepared to receive network event messages even after // cancellation. Closing a socket with closesocket also cancels // WSAAsyncSelect message sending, but the same caveat about messages in // the queue prior to the closesocket still applies. // // Since an accept'ed socket has the same properties as the listening // socket used to accept it, any WSAAsyncSelect events set for the // listening socket apply to the accepted socket. For example, if a // listening socket has WSAAsyncSelect events FD_ACCEPT, FD_READ, and // FD_WRITE, then any socket accepted on that listening socket will also // have FD_ACCEPT, FD_READ, and FD_WRITE events with the same wMsg value // used for messages. If a different wMsg or events are desired, the // application should call WSAAsyncSelect, passing the accepted socket // and the desired new information. // // When one of the nominated network events occurs on the specified // socket s, the application's window hWnd receives message wMsg. The // wParam parameter identifies the socket on which a network event has // occurred. The low word of lParam specifies the network event that has // occurred. The high word of lParam contains any error code. The error // code be any error as defined in WINSOCK2.H. Note Upon receipt of an // event notification message the WSAGetLastError function cannot be // used to check the error value, because the error value returned can // differ from the value in the high word of lParam. // // The error and event codes can be extracted from the lParam using the // macros WSAGETSELECTERROR and WSAGETSELECTEVENT, defined in WINSOCK2.H // as: // // #define WSAGETSELECTERROR(lParam) HIWORD(lParam) // #define WSAGETSELECTEVENT(lParam) LOWORD(lParam) // // The use of these macros will maximize the portability of the source // code for the application. // // The possible network event codes that can be returned are as follows: // // Value Meaning // FD_READ Socket s ready for reading // // FD_WRITE Socket s ready for writing // // FD_OOB Out-of-band data ready for reading on socket s // // FD_ACCEPT Socket s ready for accepting a new incoming connection // // FD_CONNECT Connection initiated on socket s completed // // FD_CLOSE Connection identified by socket s has been closed // // FD_QOS Quality of Service associated with socket s has changed // // FD_GROUP_QOS Quality of Service associated with the socket group to // which s belongs has changed // // Return Values // // The return value is zero if the application's declaration of interest // in the network event set was successful. Otherwise, the value // SOCKET_ERROR is returned, and a specific error number can be // retrieved by calling WSAGetLastError. // // Comments // // Although WSAAsyncSelect can be called with interest in multiple // events, the application window will receive a single message for each // network event. // // As in the case of the select function, WSAAsyncSelect will frequently // be used to determine when a data transfer operation (send or recv) // can be issued with the expectation of immediate success. // Nevertheless, a robust application must be prepared for the // possibility that it can receive a message and issue a Windows Sockets // 2 call that returns WSAEWOULDBLOCK immediately. For example, the // following sequence of events is possible: // // 1. data arrives on socket s; Windows Sockets 2 posts WSAAsyncSelect // message // // 2. application processes some other message // // 3. while processing, application issues an ioctlsocket(s, // FIONREAD...) and notices that there is data ready to be read // // 4. application issues a recv(s,...) to read the data // // 5. application loops to process next message, eventually reaching the // WSAAsyncSelect message indicating that data is ready to read // // 6. application issues recv(s,...), which fails with the error // WSAEWOULDBLOCK. // // Other sequences are possible. // // The Windows Sockets DLL will not continually flood an application // with messages for a particular network event. Having successfully // posted notification of a particular event to an application window, // no further message(s) for that network event will be posted to the // application window until the application makes the function call that // implicitly re-enables notification of that network event. // // Event Re-enabling function // // FD_READ recv, recvfrom, WSARecv, or WSARecvFrom // // FD_WRITE send, sendto, WSASend, or WSASendTo // // FD_OOB recv, recvfrom, WSARecv, or WSARecvFrom // // FD_ACCEPT accept or WSAAccept unless the error code is WSATRY_AGAIN // indicating that the condition function returned CF_DEFER // // FD_CONNECT NONE // // FD_CLOSE NONE // // FD_QOS WSAIoctl with command SIO_GET_QOS // // FD_GROUP_QOS WSAIoctl with command SIO_GET_GROUP_QOS // // Any call to the re-enabling routine, even one that fails, results in // re-enabling of message posting for the relevant event. // // For FD_READ, FD_OOB, and FD_ACCEPT events, message posting is // "level-triggered." This means that if the re-enabling routine is // called and the relevant condition is still met after the call, a // WSAAsyncSelect message is posted to the application. This allows an // application to be event-driven and not be concerned with the amount // of data that arrives at any one time. Consider the following sequence: // // 1. Network transport stack receives 100 bytes of data on socket s and // causes Windows Sockets 2 to post an FD_READ message. // // 2. The application issues recv( s, buffptr, 50, 0) to read 50 bytes. // // 3. Another FD_READ message is posted since there is still data to be // read. // // With these semantics, an application need not read all available data // in response to an FD_READ messageľa single recv in response to each // FD_READ message is appropriate. If an application issues multiple // recv calls in response to a single FD_READ, it can receive multiple // FD_READ messages. Such an application may need to disable FD_READ // messages before starting the recv calls by calling WSAAsyncSelect // with the FD_READ event not set. // // The FD_QOS and FD_GROUP_QOS events are considered edge triggered. A // message will be posted exactly once when a QOS change occurs. Further // messages will not be forthcoming until either the provider detects a // further change in QOS or the application renegotiates the QOS for the // socket. // // If any event has already happened when the application calls // WSAAsyncSelect or when the re-enabling function is called, then a // message is posted as appropriate. For example, consider the following // sequence: // // 1. an application calls listen, // // 2. a connect request is received but not yet accepted, // // 3. the application calls WSAAsyncSelect specifying that it wants to // receive FD_ACCEPT messages for the socket. Due to the persistence of // events, Windows Sockets 2 posts an FD_ACCEPT message immediately. // // The FD_WRITE event is handled slightly differently. An FD_WRITE // message is posted when a socket is first connected with // connect/WSAConnect (after FD_CONNECT, if also registered) or accepted // with accept/WSAAccept, and then after a send operation fails with // WSAEWOULDBLOCK and buffer space becomes available. Therefore, an // application can assume that sends are possible starting from the // first FD_WRITE message and lasting until a send returns // WSAEWOULDBLOCK. After such a failure the application will be notified // that sends are again possible with an FD_WRITE message. // // The FD_OOB event is used only when a socket is configured to receive // out-of-band data separately. (See section Out-Of-Band data for a // discussion of this topic.) If the socket is configured to receive // out-of-band data in-line, the out-of-band (expedited) data is treated // as normal data and the application should register an interest in, // and will receive, FD_READ events, not FD_OOB events. An application // may set or inspect the way in which out-of-band data is to be handled // by using setsockopt or getsockopt for the SO_OOBINLINE option. // // The error code in an FD_CLOSE message indicates whether the socket // close was graceful or abortive. If the error code is zero, then the // close was graceful; if the error code is WSAECONNRESET, then the // socket's virtual circuit was reset. This only applies to // connection-oriented sockets such as SOCK_STREAM. // // The FD_CLOSE message is posted when a close indication is received // for the virtual circuit corresponding to the socket. In TCP terms, // this means that the FD_CLOSE is posted when the connection goes into // the TIME WAIT or CLOSE WAIT states. This results from the remote end // performing a shutdown on the send side or a closesocket. FD_CLOSE // should only be posted after all data is read from a socket, but an // application should check for remaining data upon receipt of FD_CLOSE // to avoid any possibility of losing data. // // Please note your application will receive ONLY an FD_CLOSE message to // indicate closure of a virtual circuit, and only when all the received // data has been read if this is a graceful close. It will not receive // an FD_READ message to indicate this condition. // // The FD_QOS or FD_GROUP_QOS message is posted when any field in the // flow specification associated with socket s or the socket group that // s belongs to has changed, respectively. Applications should use // WSAIoctl with command SIO_GET_QOS or SIO_GET_GROUP_QOS to get the // current QOS for socket s or for the socket group s belongs to, // respectively. // // Here is a summary of events and conditions for each asynchronous // notification message: // // FD_READ: // // 1. when WSAAsyncSelect called, if there is data currently available // to receive, // // 2. when data arrives, if FD_READ not already posted, // // 3. after recv or recvfrom called (with or without MSG_PEEK), if data // is still available to receive. Note when setsockopt SO_OOBINLINE is // enabled "data" includes both normal data and out-of-band (OOB) data // in the instances noted above. FD_WRITE: // // 1. when WSAAsyncSelect called, if a send or sendto is possible // // 2. after connect or accept called, when connection established // // 3. after send or sendto fail with WSAEWOULDBLOCK, when send or sendto // are likely to succeed, // // 4. after bind on a datagram socket. // // FD_OOB: Only valid when setsockopt SO_OOBINLINE is disabled (default). // // 1. when WSAAsyncSelect called, if there is OOB data currently // available to receive with the MSG_OOB flag, // // 2. when OOB data arrives, if FD_OOB not already posted, // // 3. after recv or recvfrom called with or without MSG_OOB flag, if OOB // data is still available to receive. // // FD_ACCEPT: // // 1. when WSAAsyncSelect called, if there is currently a connection // request available to accept, // // 2. when a connection request arrives, if FD_ACCEPT not already // posted, // // 3. after accept called, if there is another connection request // available to accept. // // FD_CONNECT: // // 1. when WSAAsyncSelect called, if there is currently a connection // established, // // 2. after connect called, when connection is established (even when // connect succeeds immediately, as is typical with a datagram socket) // // FD_CLOSE: Only valid on connection-oriented sockets (for example, // SOCK_STREAM) 1. when WSAAsyncSelect called, if socket connection has // been closed, // // 2. after remote system initiated graceful close, when no data // currently available to receive (note: if data has been received and // is waiting to be read when the remote system initiates a graceful // close, the FD_CLOSE is not delivered until all pending data has been // read), // // 3. after local system initiates graceful close with shutdown and // remote system has responded with "End of Data" notification (for // example, TCP FIN), when no data currently available to receive, // // 4. when remote system terminates connection (for example, sent TCP // RST), and lParam will contain WSAECONNRESET error value. // // // Note FD_CLOSE is not posted after closesocket is called. FD_QOS: // // 1. when WSAAsyncSelect called, if the QOS associated with the socket // has been changed, // // 2. after WSAIoctl with SIO_GET_QOS called, when the QOS is changed. // // FD_GROUP_QOS: // // 1. when WSAAsyncSelect called, if the group QOS associated with the // socket has been changed, // // 2. after WSAIoctl with SIO_GET_GROUP_QOS called, when the group QOS // is changed. Error Codes // // WSANOTINITIALISED A successful WSAStartup must occur before using // this function. // // WSAENETDOWN The network subsystem has failed. // // WSAEINVAL Indicates that one of the specified parameters was invalid // such as the window handle not referring to an existing window, or the // specified socket is in an invalid state. // // WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or // the service provider is still processing a callback function. // // WSAENOTSOCK The descriptor is not a socket. Additional error codes // may be set when an application window receives a message. This error // code is extracted from the lParam in the reply message using the // WSAGETSELECTERROR macro. Possible error codes for each network event // are: // // Event: FD_CONNECT // // Error Code Meaning // // WSAEADDRINUSE The specified address is already in use. // // WSAEADDRNOTAVAIL The specified address is not available from the // local machine. // // WSAEAFNOSUPPORT Addresses in the specified family cannot be used with // this socket. // // WSAECONNREFUSED The attempt to connect was forcefully rejected. // // WSAENETUNREACH The network cannot be reached from this host at this // time. // // WSAEFAULT The namelen parameter is incorrect. // // WSAEINVAL The socket is already bound to an address. // // WSAEISCONN The socket is already connected. // // WSAEMFILE No more file descriptors are available. // // WSAENOBUFS No buffer space is available. The socket cannot be // connected. // // WSAENOTCONN The socket is not connected. // // WSAETIMEDOUT Attempt to connect timed out without establishing a // connection. // // // // Event: FD_CLOSE // // Error Code Meaning // // WSAENETDOWN The network subsystem has failed. // // WSAECONNRESET The connection was reset by the remote side. // // WSAECONNABORTED The connection was terminated due to a time-out or // other failure. // // // Event: FD_READ // // Event: FD_WRITE // // Event: FD_OOB // // Event: FD_ACCEPT // // Event: FD_QOS // // Event: FD_GROUP_QOS // // Error Code Meaning // // WSAENETDOWN The network subsystem has failed. // // ----------------------------------------------------------------