|
|
#include "windows.h"
#include "types.h"
#include "winsock.h"
#include "wsasync.h"
#ifdef UNDER_CE
#include <ceconfig.h>
#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.
//
// ----------------------------------------------------------------
|