windows-nt/Source/XPSP1/NT/termsrv/wince/wsasync/wsasync.c

1378 lines
43 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#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<67>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.
//
// ----------------------------------------------------------------