Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / dll / win32 / ws2_32 / src / async.c
diff --git a/dll/win32/ws2_32/src/async.c b/dll/win32/ws2_32/src/async.c
new file mode 100644 (file)
index 0000000..52eb42e
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS WinSock 2 API
+ * FILE:        dll/win32/ws2_32_new/src/async.c
+ * PURPOSE:     Async Block Object and Async Thread Management
+ * PROGRAMMER:  Alex Ionescu (alex@relsoft.net)
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <ws2_32.h>
+
+/* DATA **********************************************************************/
+
+BOOLEAN WsAsyncThreadInitialized;
+LONG WsAsyncTaskHandle;
+PLIST_ENTRY WsAsyncQueue;
+CRITICAL_SECTION WsAsyncCritSect;
+HANDLE WsAsyncEvent;
+HANDLE WsAsyncCurrentTaskHandle;
+HANDLE WsAsyncCancelledTaskHandle;
+HINSTANCE WsAsyncDllHandle;
+
+#define WsAsyncLock()       EnterCriticalSection(&WsAsyncCritSect)
+#define WsAsyncUnlock()     LeaveCriticalSection(&WsAsyncCritSect)
+
+/* FUNCTIONS *****************************************************************/
+
+VOID
+WSAAPI
+WsAsyncGlobalInitialize(VOID)
+{
+    /* Initialize the async lock */
+    InitializeCriticalSection(&WsAsyncCritSect);
+}
+
+VOID
+WSAAPI
+WsAsyncGlobalTerminate(VOID)
+{
+    /* Destroy the async lock */
+    DeleteCriticalSection(&WsAsyncCritSect);
+}
+
+
+SIZE_T
+WSAAPI
+BytesInHostent(PHOSTENT Hostent)
+{
+    SIZE_T Bytes;
+    INT i;
+
+    /* Start with the static stuff */
+    Bytes = sizeof(HOSTENT) + strlen(Hostent->h_name) + sizeof(CHAR);
+
+    /* Add 2 pointers for the list-terminators */
+    Bytes += 2 * sizeof(ULONG_PTR);
+
+    /* Now loop for the aliases */
+    for (i = 0; Hostent->h_aliases[i]; i++)
+    {
+        /* Add the alias size, plus the space its pointer takes */
+        Bytes += strlen(Hostent->h_aliases[i]) + sizeof(CHAR) + sizeof(ULONG_PTR);
+    }
+
+    /* Now loop for the hostnames  */
+    for (i = 0; Hostent->h_addr_list[i]; i++)
+    {
+        /* Add the alias size, plus the space its pointer takes */
+        Bytes += Hostent->h_length + sizeof(ULONG_PTR);
+    }
+
+    /* Align to 8 bytes */
+    return (Bytes + 7) & ~7;
+}
+
+SIZE_T
+WSAAPI
+BytesInServent(PSERVENT Servent)
+{
+    SIZE_T Bytes;
+    INT i;
+
+    /* Start with the static stuff */
+    Bytes = sizeof(SERVENT) +
+            strlen(Servent->s_name) + sizeof(CHAR) +
+            strlen(Servent->s_proto) + sizeof(CHAR);
+
+    /* Add 1 pointers for the list terminator */
+    Bytes += sizeof(ULONG_PTR);
+
+    /* Now loop for the aliases */
+    for (i = 0; Servent->s_aliases[i]; i++)
+    {
+        /* Add the alias size, plus the space its pointer takes */
+        Bytes += strlen(Servent->s_aliases[i]) + sizeof(CHAR) + sizeof(ULONG_PTR);
+    }
+
+    /* return */
+    return Bytes;
+}
+
+SIZE_T
+WSAAPI
+BytesInProtoent(PPROTOENT Protoent)
+{
+    SIZE_T Bytes;
+    INT i;
+
+    /* Start with the static stuff */
+    Bytes = sizeof(SERVENT) + strlen(Protoent->p_name) + sizeof(CHAR);
+
+    /* Add 1 pointers for the list terminator */
+    Bytes += sizeof(ULONG_PTR);
+
+    /* Now loop for the aliases */
+    for (i = 0; Protoent->p_aliases[i]; i++)
+    {
+        /* Add the alias size, plus the space its pointer takes */
+        Bytes += strlen(Protoent->p_aliases[i]) + sizeof(CHAR) + sizeof(ULONG_PTR);
+    }
+
+    /* return */
+    return Bytes;
+}
+
+SIZE_T
+WSAAPI
+CopyHostentToBuffer(IN PCHAR Buffer,
+                    IN INT BufferLength,
+                    IN PHOSTENT Hostent)
+{
+    SIZE_T BufferSize, CurrentSize, NameSize;
+    PCHAR p = Buffer;
+    DWORD Aliases = 0, Names = 0;
+    DWORD i;
+    PHOSTENT ReturnedHostent = (PHOSTENT)Buffer;
+
+    /* Determine the buffer size required */
+    BufferSize = BytesInHostent(Hostent);
+
+    /* Check which size to use */
+    if ((DWORD)BufferLength > BufferSize)
+    {
+        /* Zero the buffer */
+        RtlZeroMemory(Buffer, BufferSize);
+    }
+    else
+    {
+        /* Zero the buffer */
+        RtlZeroMemory(Buffer, BufferLength);
+    }
+
+    /* Start with the raw Hostent */
+    CurrentSize = sizeof(HOSTENT);
+
+    /* Return the size needed now */
+    if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+    /* Copy the Hostent and initialize it */
+    CopyMemory(p, Hostent, sizeof(HOSTENT));
+    p = Buffer + CurrentSize;
+    ReturnedHostent->h_name = NULL;
+    ReturnedHostent->h_aliases = NULL;
+    ReturnedHostent->h_addr_list = NULL;
+
+    /* Find out how many aliases there are */
+    while (Hostent->h_aliases[Aliases])
+    {
+        /* Increase the alias count */
+        Aliases++;
+    }
+
+    /* Add the aliases to the size, and validate it */
+    CurrentSize += (Aliases + 1) * sizeof(ULONG_PTR);
+    if (CurrentSize > (DWORD)BufferLength)
+    {
+        /* Clear the aliases and return */
+        Hostent->h_aliases = NULL;
+        return BufferSize;
+    }
+
+    /* Write the aliases, update the pointer */
+    ReturnedHostent->h_aliases = (PCHAR*)p;
+    p = Buffer + CurrentSize;
+
+    /* Find out how many names there are */
+    while (Hostent->h_addr_list[Names])
+    {
+        /* Increase the alias count */
+        Names++;
+    }
+
+    /* Add the names to the size, and validate it */
+    CurrentSize += (Names + 1) * sizeof(ULONG_PTR);
+    if (CurrentSize > (DWORD)BufferLength)
+    {
+        /* Clear the aliases and return */
+        Hostent->h_addr_list = NULL;
+        return BufferSize;
+    }
+
+    /* Write the names, update the pointer */
+    ReturnedHostent->h_addr_list = (PCHAR*)p;
+    p = Buffer + CurrentSize;
+
+    /* Now add the names */
+    for (i = 0; i < Names; i++)
+    {
+        /* Update size and validate */
+        CurrentSize += Hostent->h_length;
+        if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+        /* Write pointer and copy */
+        ReturnedHostent->h_addr_list[i] = p;
+        CopyMemory(p, Hostent->h_addr_list[i], Hostent->h_length);
+
+        /* Update pointer */
+        p = Buffer + CurrentSize;
+    }
+
+    /* Finalize the list */
+    ReturnedHostent->h_addr_list[i] = NULL;
+
+    /* Add the service name to the size, and validate it */
+    NameSize = strlen(Hostent->h_name) + sizeof(CHAR);
+    CurrentSize += NameSize;
+    if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+    /* Write the service name and update the pointer */
+    ReturnedHostent->h_name = p;
+    CopyMemory(p, Hostent->h_name, NameSize);
+    p = Buffer + CurrentSize;
+
+    /* Now add the aliases */
+    for (i = 0; i < Aliases; i++)
+    {
+        /* Update size and validate */
+        NameSize = strlen(Hostent->h_aliases[i]) + sizeof(CHAR);
+        CurrentSize += NameSize;
+        if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+        /* Write pointer and copy */
+        ReturnedHostent->h_aliases[i] = p;
+        CopyMemory(p, Hostent->h_aliases[i], NameSize);
+
+        /* Update pointer */
+        p = Buffer + CurrentSize;
+    }
+
+    /* Finalize the list and return */
+    ReturnedHostent->h_aliases[i] = NULL;
+    return BufferSize;
+}
+
+SIZE_T
+WSAAPI
+CopyServentToBuffer(IN PCHAR Buffer,
+                    IN INT BufferLength,
+                    IN PSERVENT Servent)
+{
+    SIZE_T BufferSize, CurrentSize, NameSize;
+    PCHAR p = Buffer;
+    DWORD Aliases = 0;
+    DWORD i;
+    PSERVENT ReturnedServent = (PSERVENT)Buffer;
+
+    /* Determine the buffer size required */
+    BufferSize = BytesInServent(Servent);
+
+    /* Check which size to use */
+    if ((DWORD)BufferLength > BufferSize)
+    {
+        /* Zero the buffer */
+        ZeroMemory(Buffer, BufferSize);
+    }
+    else
+    {
+        /* Zero the buffer */
+        ZeroMemory(Buffer, BufferLength);
+    }
+
+    /* Start with the raw servent */
+    CurrentSize = sizeof(SERVENT);
+
+    /* Return the size needed now */
+    if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+    /* Copy the servent and initialize it */
+    CopyMemory(p, Servent, sizeof(SERVENT));
+    p = Buffer + CurrentSize;
+    ReturnedServent->s_name = NULL;
+    ReturnedServent->s_aliases = NULL;
+    ReturnedServent->s_proto = NULL;
+
+    /* Find out how many aliases there are */
+    while (Servent->s_aliases[Aliases])
+    {
+        /* Increase the alias count */
+        Aliases++;
+    }
+
+    /* Add the aliases to the size, and validate it */
+    CurrentSize += (Aliases + 1) * sizeof(ULONG_PTR);
+    if (CurrentSize > (DWORD)BufferLength)
+    {
+        /* Clear the aliases and return */
+        Servent->s_aliases = NULL;
+        return BufferSize;
+    }
+
+    /* Write the aliases, update the pointer */
+    ReturnedServent->s_aliases = (PCHAR*)p;
+    p = Buffer + CurrentSize;
+
+    /* Add the service name to the size, and validate it */
+    NameSize = strlen(Servent->s_name) + sizeof(CHAR);
+    CurrentSize += NameSize;
+    if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+    /* Write the service name and update the pointer */
+    ReturnedServent->s_name = p;
+    CopyMemory(p, Servent->s_name, NameSize);
+    p = Buffer + CurrentSize;
+
+    /* Now add the aliases */
+    for (i = 0; i < Aliases; i++)
+    {
+        /* Update size and validate */
+        NameSize = strlen(Servent->s_aliases[i]) + sizeof(CHAR);
+        CurrentSize += NameSize;
+        if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+        /* Write pointer and copy */
+        ReturnedServent->s_aliases[i] = p;
+        CopyMemory(p, Servent->s_aliases[i], NameSize);
+
+        /* Update pointer */
+        p = Buffer + CurrentSize;
+    }
+
+    /* Finalize the list and return */
+    ReturnedServent->s_aliases[i] = NULL;
+    return BufferSize;
+}
+
+SIZE_T
+WSAAPI
+CopyProtoentToBuffer(IN PCHAR Buffer,
+                     IN INT BufferLength,
+                     IN PPROTOENT Protoent)
+{
+    SIZE_T BufferSize, CurrentSize, NameSize;
+    PCHAR p = Buffer;
+    DWORD Aliases = 0;
+    DWORD i;
+    PPROTOENT ReturnedProtoent = (PPROTOENT)Buffer;
+
+    /* Determine the buffer size required */
+    BufferSize = BytesInProtoent(Protoent);
+
+    /* Check which size to use */
+    if ((DWORD)BufferLength > BufferSize)
+    {
+        /* Zero the buffer */
+        ZeroMemory(Buffer, BufferSize);
+    }
+    else
+    {
+        /* Zero the buffer */
+        ZeroMemory(Buffer, BufferLength);
+    }
+
+    /* Start with the raw servent */
+    CurrentSize = sizeof(PROTOENT);
+
+    /* Return the size needed now */
+    if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+    /* Copy the servent and initialize it */
+    CopyMemory(p, Protoent, sizeof(PROTOENT));
+    p = Buffer + CurrentSize;
+    ReturnedProtoent->p_name = NULL;
+    ReturnedProtoent->p_aliases = NULL;
+
+    /* Find out how many aliases there are */
+    while (Protoent->p_aliases[Aliases])
+    {
+        /* Increase the alias count */
+        Aliases++;
+    }
+
+    /* Add the aliases to the size, and validate it */
+    CurrentSize += (Aliases + 1) * sizeof(ULONG_PTR);
+    if (CurrentSize > (DWORD)BufferLength)
+    {
+        /* Clear the aliases and return */
+        Protoent->p_aliases = NULL;
+        return BufferSize;
+    }
+
+    /* Write the aliases, update the pointer */
+    ReturnedProtoent->p_aliases = (PCHAR*)p;
+    p = Buffer + CurrentSize;
+
+    /* Add the service name to the size, and validate it */
+    NameSize = strlen(Protoent->p_name) + sizeof(CHAR);
+    CurrentSize += NameSize;
+    if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+    /* Write the service name and update the pointer */
+    ReturnedProtoent->p_name = p;
+    CopyMemory(p, Protoent->p_name, NameSize);
+    p = Buffer + CurrentSize;
+
+    /* Now add the aliases */
+    for (i = 0; i < Aliases; i++)
+    {
+        /* Update size and validate */
+        NameSize = strlen(Protoent->p_aliases[i]) + sizeof(CHAR);
+        CurrentSize += NameSize;
+        if (CurrentSize > (DWORD)BufferLength) return BufferSize;
+
+        /* Write pointer and copy */
+        ReturnedProtoent->p_aliases[i] = p;
+        CopyMemory(p, Protoent->p_aliases[i], NameSize);
+
+        /* Update pointer */
+        p = Buffer + CurrentSize;
+    }
+
+    /* Finalize the list and return */
+    ReturnedProtoent->p_aliases[i] = NULL;
+    return BufferSize;
+}
+
+PWSASYNCBLOCK
+WSAAPI
+WsAsyncAllocateBlock(IN SIZE_T ExtraLength)
+{
+    PWSASYNCBLOCK AsyncBlock;
+
+    /* Add the size of the block */
+    ExtraLength += sizeof(WSASYNCBLOCK);
+
+    /* Allocate it */
+    AsyncBlock = HeapAlloc(WsSockHeap, 0, ExtraLength);
+
+    /* Get a handle to it */
+    AsyncBlock->TaskHandle = UlongToPtr(InterlockedIncrement(&WsAsyncTaskHandle));
+
+    /* Return it */
+    return AsyncBlock;
+}
+
+BOOL
+WINAPI
+WsAsyncThreadBlockingHook(VOID)
+{
+    /* Check if this task is being cancelled */
+    if (WsAsyncCurrentTaskHandle == WsAsyncCancelledTaskHandle)
+    {
+        /* Cancel the blocking call so we can get back */
+        WSACancelBlockingCall();
+    }
+
+    /* Return to system */
+    return FALSE;
+}
+
+VOID
+WSAAPI
+WsAsyncFreeBlock(IN PWSASYNCBLOCK AsyncBlock)
+{
+    /* Free it */
+    HeapFree(WsSockHeap, 0, AsyncBlock);
+}
+
+VOID
+WSAAPI
+WsAsyncGetServ(IN HANDLE TaskHandle,
+               IN DWORD Operation,
+               IN HWND hWnd,
+               IN UINT wMsg,
+               IN CHAR FAR *ByWhat,
+               IN CHAR FAR *Protocol,
+               IN CHAR FAR *Buffer,
+               IN INT BufferLength)
+{
+    PSERVENT Servent;
+    SIZE_T BufferSize = 0;
+    LPARAM lParam;
+    INT ErrorCode = 0;
+
+    /* Check the operation */
+    if (Operation == WsAsyncGetServByName)
+    {
+        /* Call the API */
+        Servent = getservbyname(ByWhat, Protocol);
+    }
+    else
+    {
+        /* Call the API */
+        Servent = getservbyport(PtrToUlong(ByWhat), Protocol);
+    }
+
+    /* Make sure we got one */
+    if (!Servent) ErrorCode = GetLastError();
+
+    /* Acquire the lock */
+    WsAsyncLock();
+
+    /* Check if this task got cancelled */
+    if (TaskHandle == WsAsyncCancelledTaskHandle)
+    {
+        /* Return */
+        WsAsyncUnlock();
+        return;
+    }
+
+    /* If we got a Servent back, copy it */
+    if (Servent)
+    {
+        /* Copy it into the buffer */
+        BufferSize = CopyServentToBuffer(Buffer, BufferLength, Servent);
+
+        /* Check if we had enough space */
+        if (BufferSize > (DWORD)BufferLength)
+        {
+            /* Not enough */
+            ErrorCode = WSAENOBUFS;
+        }
+        else
+        {
+            /* Perfect */
+            ErrorCode = NO_ERROR;
+        }
+    }
+
+    /* Not processing anymore */
+    WsAsyncCurrentTaskHandle = NULL;
+
+    /* Release the lock */
+    WsAsyncUnlock();
+
+    /* Make the messed-up lParam reply */
+    lParam = WSAMAKEASYNCREPLY(BufferSize, ErrorCode);
+
+    /* Sent it through the Upcall API */
+    WPUPostMessage(hWnd, wMsg, (WPARAM)TaskHandle, lParam);
+}
+
+VOID
+WSAAPI
+WsAsyncGetProto(IN HANDLE TaskHandle,
+                IN DWORD Operation,
+                IN HWND hWnd,
+                IN UINT wMsg,
+                IN CHAR FAR *ByWhat,
+                IN CHAR FAR *Buffer,
+                IN INT BufferLength)
+{
+    PPROTOENT Protoent;
+    SIZE_T BufferSize = 0;
+    LPARAM lParam;
+    INT ErrorCode = 0;
+
+    /* Check the operation */
+    if (Operation == WsAsyncGetProtoByName)
+    {
+        /* Call the API */
+        Protoent = getprotobyname(ByWhat);
+    }
+    else
+    {
+        /* Call the API */
+        Protoent = getprotobynumber(PtrToUlong(ByWhat));
+    }
+
+    /* Make sure we got one */
+    if (!Protoent) ErrorCode = GetLastError();
+
+    /* Acquire the lock */
+    WsAsyncLock();
+
+    /* Check if this task got cancelled */
+    if (TaskHandle == WsAsyncCancelledTaskHandle)
+    {
+        /* Return */
+        WsAsyncUnlock();
+        return;
+    }
+
+    /* If we got a Servent back, copy it */
+    if (Protoent)
+    {
+        /* Copy it into the buffer */
+        BufferSize = CopyProtoentToBuffer(Buffer, BufferLength, Protoent);
+
+        /* Check if we had enough space */
+        if (BufferSize > (DWORD)BufferLength)
+        {
+            /* Not enough */
+            ErrorCode = WSAENOBUFS;
+        }
+        else
+        {
+            /* Perfect */
+            ErrorCode = NO_ERROR;
+        }
+    }
+
+    /* Not processing anymore */
+    WsAsyncCurrentTaskHandle = NULL;
+
+    /* Release the lock */
+    WsAsyncUnlock();
+
+    /* Make the messed-up lParam reply */
+    lParam = WSAMAKEASYNCREPLY(BufferSize, ErrorCode);
+
+    /* Sent it through the Upcall API */
+    WPUPostMessage(hWnd, wMsg, (WPARAM)TaskHandle, lParam);
+}
+
+VOID
+WSAAPI
+WsAsyncGetHost(IN HANDLE TaskHandle,
+               IN DWORD Operation,
+               IN HWND hWnd,
+               IN UINT wMsg,
+               IN CHAR FAR *ByWhat,
+               IN INT Length,
+               IN INT Type,
+               IN CHAR FAR *Buffer,
+               IN INT BufferLength)
+{
+    PHOSTENT Hostent;
+    SIZE_T BufferSize = 0;
+    LPARAM lParam;
+    INT ErrorCode = 0;
+
+    /* Check the operation */
+    if (Operation == WsAsyncGetHostByAddr)
+    {
+        /* Call the API */
+        Hostent = gethostbyaddr(ByWhat, Length, Type);
+    }
+    else
+    {
+        /* Call the API */
+        Hostent = gethostbyname(ByWhat);
+    }
+
+    /* Make sure we got one */
+    if (!Hostent) ErrorCode = GetLastError();
+
+    /* Acquire the lock */
+    WsAsyncLock();
+
+    /* Check if this task got cancelled */
+    if (TaskHandle == WsAsyncCancelledTaskHandle)
+    {
+        /* Return */
+        WsAsyncUnlock();
+        return;
+    }
+
+    /* If we got a Servent back, copy it */
+    if (Hostent)
+    {
+        /* Copy it into the buffer */
+        BufferSize = CopyHostentToBuffer(Buffer, BufferLength, Hostent);
+
+        /* Check if we had enough space */
+        if (BufferSize > (DWORD)BufferLength)
+        {
+            /* Not enough */
+            ErrorCode = WSAENOBUFS;
+        }
+        else
+        {
+            /* Perfect */
+            ErrorCode = NO_ERROR;
+        }
+    }
+
+    /* Not processing anymore */
+    WsAsyncCurrentTaskHandle = NULL;
+
+    /* Release the lock */
+    WsAsyncUnlock();
+
+    /* Make the messed-up lParam reply */
+    lParam = WSAMAKEASYNCREPLY(BufferSize, ErrorCode);
+
+    /* Sent it through the Upcall API */
+    WPUPostMessage(hWnd, wMsg, (WPARAM)TaskHandle, lParam);
+}
+
+DWORD
+WINAPI
+WsAsyncThread(IN PVOID ThreadContext)
+{
+    PWSASYNCCONTEXT Context = ThreadContext;
+    PWSASYNCBLOCK AsyncBlock;
+    PLIST_ENTRY Entry;
+    HANDLE AsyncEvent = Context->AsyncEvent;
+    PLIST_ENTRY ListHead = &Context->AsyncQueue;
+
+    /* Set the blocking hook */
+    WSASetBlockingHook((FARPROC)WsAsyncThreadBlockingHook);
+
+    /* Loop */
+    while (TRUE)
+    {
+        /* Wait for the event */
+        WaitForSingleObject(AsyncEvent, INFINITE);
+
+        /* Get the lock */
+        WsAsyncLock();
+
+        /* Process the queue */
+        while (!IsListEmpty(ListHead))
+        {
+            /* Remove this entry and get the async block */
+            Entry = RemoveHeadList(ListHead);
+            AsyncBlock = CONTAINING_RECORD(Entry, WSASYNCBLOCK, AsyncQueue);
+
+            /* Save the current task handle */
+            WsAsyncCurrentTaskHandle = AsyncBlock->TaskHandle;
+
+            /* Release the lock */
+            WsAsyncUnlock();
+
+            /* Check which operation to do */
+            switch (AsyncBlock->Operation)
+            {
+                /* Get Host by Y */
+                case WsAsyncGetHostByAddr: case WsAsyncGetHostByName:
+
+                    /* Call the handler */
+                    WsAsyncGetHost(AsyncBlock->TaskHandle,
+                                   AsyncBlock->Operation,
+                                   AsyncBlock->GetHost.hWnd,
+                                   AsyncBlock->GetHost.wMsg,
+                                   AsyncBlock->GetHost.ByWhat,
+                                   AsyncBlock->GetHost.Length,
+                                   AsyncBlock->GetHost.Type,
+                                   AsyncBlock->GetHost.Buffer,
+                                   AsyncBlock->GetHost.BufferLength);
+                    break;
+
+                /* Get Proto by Y */
+                case WsAsyncGetProtoByNumber: case WsAsyncGetProtoByName:
+
+                    /* Call the handler */
+                    WsAsyncGetProto(AsyncBlock->TaskHandle,
+                                    AsyncBlock->Operation,
+                                    AsyncBlock->GetProto.hWnd,
+                                    AsyncBlock->GetProto.wMsg,
+                                    AsyncBlock->GetHost.ByWhat,
+                                    AsyncBlock->GetProto.Buffer,
+                                    AsyncBlock->GetProto.BufferLength);
+                    break;
+
+                /* Get Serv by Y */
+                case WsAsyncGetServByPort: case WsAsyncGetServByName:
+
+                    /* Call the handler */
+                    WsAsyncGetServ(AsyncBlock->TaskHandle,
+                                   AsyncBlock->Operation,
+                                   AsyncBlock->GetServ.hWnd,
+                                   AsyncBlock->GetServ.wMsg,
+                                   AsyncBlock->GetServ.ByWhat,
+                                   AsyncBlock->GetServ.Protocol,
+                                   AsyncBlock->GetServ.Buffer,
+                                   AsyncBlock->GetServ.BufferLength);
+                    break;
+
+                /* Termination */
+                case WsAsyncTerminate:
+
+                    /* Clean up the extra reference */
+                    WSACleanup();
+
+                    /* Free the context block */
+                    WsAsyncFreeBlock(AsyncBlock);
+
+                    /* Acquire the lock */
+                    WsAsyncLock();
+
+                    /* Loop the queue and flush it */
+                    while (!IsListEmpty(ListHead))
+                    {
+                        Entry = RemoveHeadList(ListHead);
+                        AsyncBlock = CONTAINING_RECORD(Entry,
+                                                       WSASYNCBLOCK,
+                                                       AsyncQueue);
+                        WsAsyncFreeBlock(AsyncBlock);
+                    }
+
+                    /* Release lock */
+                    WsAsyncUnlock();
+
+                    /* Close the event, free the Context */
+                    CloseHandle(AsyncEvent);
+                    HeapFree(WsSockHeap, 0, Context);
+
+                    /* Remove the extra DLL reference and kill us */
+                    FreeLibraryAndExitThread(WsAsyncDllHandle, 0);
+
+                default:
+                    break;
+            }
+
+            /* Done processing */
+            WsAsyncCurrentTaskHandle = NULL;
+
+            /* Free this block, get lock and reloop */
+            WsAsyncFreeBlock(AsyncBlock);
+            WsAsyncLock();
+        }
+
+        /* Release the lock */
+        WsAsyncUnlock();
+    }
+}
+
+BOOL
+WSAAPI
+WsAsyncCheckAndInitThread(VOID)
+{
+    HANDLE ThreadHandle;
+    DWORD Tid;
+    PWSASYNCCONTEXT Context = NULL;
+    WSADATA WsaData;
+
+    /* Make sure we're not initialized */
+    if (WsAsyncThreadInitialized) return TRUE;
+
+    /* Acquire the lock */
+    WsAsyncLock();
+
+    /* Make sure we're not initialized */
+    if (!WsAsyncThreadInitialized)
+    {
+        /* Initialize Thread Context */
+        Context = HeapAlloc(WsSockHeap, 0, sizeof(*Context));
+        if (!Context)
+            goto Exit;
+
+        /* Initialize the Queue and event */
+        WsAsyncQueue = &Context->AsyncQueue;
+        InitializeListHead(WsAsyncQueue);
+        Context->AsyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+        WsAsyncEvent = Context->AsyncEvent;
+
+        /* Prevent us from ever being killed while running */
+        if (WSAStartup(MAKEWORD(2,2), &WsaData) != ERROR_SUCCESS)
+            goto Fail;
+
+        /* Create the thread */
+        ThreadHandle = CreateThread(NULL,
+                                    0,
+                                    WsAsyncThread,
+                                    Context,
+                                    0,
+                                    &Tid);
+        if (ThreadHandle == NULL)
+        {
+            /* Cleanup and fail */
+            WSACleanup();
+            goto Fail;
+        }
+
+        /* Close the handle and set init */
+        CloseHandle(ThreadHandle);
+        WsAsyncThreadInitialized = TRUE;
+    }
+
+Exit:
+    /* Release the lock */
+    WsAsyncUnlock();
+    return WsAsyncThreadInitialized;
+
+Fail:
+    /* Close the event, free the Context */
+    if (Context->AsyncEvent)
+        CloseHandle(Context->AsyncEvent);
+    HeapFree(WsSockHeap, 0, Context);
+
+    /* Bail out */
+    goto Exit;
+}
+
+VOID
+WSAAPI
+WsAsyncTerminateThread(VOID)
+{
+    PWSASYNCBLOCK AsyncBlock;
+
+    /* Make sure we're initialized */
+    if (!WsAsyncThreadInitialized) return;
+
+    /* Allocate a block */
+    AsyncBlock = WsAsyncAllocateBlock(0);
+
+    /* Initialize it for termination */
+    AsyncBlock->Operation = WsAsyncTerminate;
+
+    /* Queue the request and return */
+    WsAsyncQueueRequest(AsyncBlock);
+    WsAsyncThreadInitialized = FALSE;
+}
+
+VOID
+WSAAPI
+WsAsyncQueueRequest(IN PWSASYNCBLOCK AsyncBlock)
+{
+    /* Get the lock */
+    WsAsyncLock();
+
+    /* Insert it into the queue */
+    InsertTailList(WsAsyncQueue, &AsyncBlock->AsyncQueue);
+
+    /* Wake up the thread */
+    SetEvent(WsAsyncEvent);
+
+    /* Release lock and return */
+    WsAsyncUnlock();
+}
+
+INT
+WSAAPI
+WsAsyncCancelRequest(IN HANDLE TaskHandle)
+{
+    PLIST_ENTRY Entry;
+    PWSASYNCBLOCK AsyncBlock;
+
+    /* Make sure we're initialized */
+    if (!WsAsyncThreadInitialized) return WSAEINVAL;
+
+    /* Acquire the lock */
+    WsAsyncLock();
+
+    /* Check if we're cancelling the current task */
+    if (TaskHandle == WsAsyncCurrentTaskHandle)
+    {
+        /* Mark us as cancelled, the async thread will see this later */
+        WsAsyncCancelledTaskHandle = TaskHandle;
+
+        /* Release lock and return */
+        WsAsyncUnlock();
+        return NO_ERROR;
+    }
+
+    /* Loop the queue */
+    Entry = WsAsyncQueue->Flink;
+    while (Entry != WsAsyncQueue)
+    {
+        /* Get the Async Block */
+        AsyncBlock = CONTAINING_RECORD(Entry, WSASYNCBLOCK, AsyncQueue);
+
+        /* Check if this is the one */
+        if (TaskHandle == AsyncBlock->TaskHandle)
+        {
+            /* It is, remove it */
+            RemoveEntryList(Entry);
+
+            /* Release the lock, free the block, and return */
+            WsAsyncUnlock();
+            WsAsyncFreeBlock(AsyncBlock);
+            return NO_ERROR;
+        }
+
+        /* Move to the next entry */
+        Entry = Entry->Flink;
+    }
+
+    /* Nothing found, fail */
+    WsAsyncUnlock();
+    return WSAEINVAL;
+}