xref: /reactos/dll/win32/ws2_32/src/async.c (revision 3476cdae)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS WinSock 2 API
4  * FILE:        dll/win32/ws2_32/src/async.c
5  * PURPOSE:     Async Block Object and Async Thread Management
6  * PROGRAMMER:  Alex Ionescu (alex@relsoft.net)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ws2_32.h>
12 
13 /* DATA **********************************************************************/
14 
15 BOOLEAN WsAsyncThreadInitialized;
16 LONG WsAsyncTaskHandle;
17 PLIST_ENTRY WsAsyncQueue;
18 CRITICAL_SECTION WsAsyncCritSect;
19 HANDLE WsAsyncEvent;
20 HANDLE WsAsyncCurrentTaskHandle;
21 HANDLE WsAsyncCancelledTaskHandle;
22 HINSTANCE WsAsyncDllHandle;
23 
24 #define WsAsyncLock()       EnterCriticalSection(&WsAsyncCritSect)
25 #define WsAsyncUnlock()     LeaveCriticalSection(&WsAsyncCritSect)
26 
27 /* FUNCTIONS *****************************************************************/
28 
29 VOID
30 WSAAPI
31 WsAsyncGlobalInitialize(VOID)
32 {
33     /* Initialize the async lock */
34     InitializeCriticalSection(&WsAsyncCritSect);
35 }
36 
37 VOID
38 WSAAPI
39 WsAsyncGlobalTerminate(VOID)
40 {
41     /* Destroy the async lock */
42     DeleteCriticalSection(&WsAsyncCritSect);
43 }
44 
45 
46 SIZE_T
47 WSAAPI
48 BytesInHostent(PHOSTENT Hostent)
49 {
50     SIZE_T Bytes;
51     INT i;
52 
53     /* Start with the static stuff */
54     Bytes = sizeof(HOSTENT) + strlen(Hostent->h_name) + sizeof(CHAR);
55 
56     /* Add 2 pointers for the list-terminators */
57     Bytes += 2 * sizeof(ULONG_PTR);
58 
59     /* Now loop for the aliases */
60     for (i = 0; Hostent->h_aliases[i]; i++)
61     {
62         /* Add the alias size, plus the space its pointer takes */
63         Bytes += strlen(Hostent->h_aliases[i]) + sizeof(CHAR) + sizeof(ULONG_PTR);
64     }
65 
66     /* Now loop for the hostnames  */
67     for (i = 0; Hostent->h_addr_list[i]; i++)
68     {
69         /* Add the alias size, plus the space its pointer takes */
70         Bytes += Hostent->h_length + sizeof(ULONG_PTR);
71     }
72 
73     /* Align to 8 bytes */
74     return (Bytes + 7) & ~7;
75 }
76 
77 SIZE_T
78 WSAAPI
79 BytesInServent(PSERVENT Servent)
80 {
81     SIZE_T Bytes;
82     INT i;
83 
84     /* Start with the static stuff */
85     Bytes = sizeof(SERVENT) +
86             strlen(Servent->s_name) + sizeof(CHAR) +
87             strlen(Servent->s_proto) + sizeof(CHAR);
88 
89     /* Add 1 pointers for the list terminator */
90     Bytes += sizeof(ULONG_PTR);
91 
92     /* Now loop for the aliases */
93     for (i = 0; Servent->s_aliases[i]; i++)
94     {
95         /* Add the alias size, plus the space its pointer takes */
96         Bytes += strlen(Servent->s_aliases[i]) + sizeof(CHAR) + sizeof(ULONG_PTR);
97     }
98 
99     /* return */
100     return Bytes;
101 }
102 
103 SIZE_T
104 WSAAPI
105 BytesInProtoent(PPROTOENT Protoent)
106 {
107     SIZE_T Bytes;
108     INT i;
109 
110     /* Start with the static stuff */
111     Bytes = sizeof(SERVENT) + strlen(Protoent->p_name) + sizeof(CHAR);
112 
113     /* Add 1 pointers for the list terminator */
114     Bytes += sizeof(ULONG_PTR);
115 
116     /* Now loop for the aliases */
117     for (i = 0; Protoent->p_aliases[i]; i++)
118     {
119         /* Add the alias size, plus the space its pointer takes */
120         Bytes += strlen(Protoent->p_aliases[i]) + sizeof(CHAR) + sizeof(ULONG_PTR);
121     }
122 
123     /* return */
124     return Bytes;
125 }
126 
127 SIZE_T
128 WSAAPI
129 CopyHostentToBuffer(IN PCHAR Buffer,
130                     IN INT BufferLength,
131                     IN PHOSTENT Hostent)
132 {
133     SIZE_T BufferSize, CurrentSize, NameSize;
134     PCHAR p = Buffer;
135     DWORD Aliases = 0, Names = 0;
136     DWORD i;
137     PHOSTENT ReturnedHostent = (PHOSTENT)Buffer;
138 
139     /* Determine the buffer size required */
140     BufferSize = BytesInHostent(Hostent);
141 
142     /* Check which size to use */
143     if ((DWORD)BufferLength > BufferSize)
144     {
145         /* Zero the buffer */
146         RtlZeroMemory(Buffer, BufferSize);
147     }
148     else
149     {
150         /* Zero the buffer */
151         RtlZeroMemory(Buffer, BufferLength);
152     }
153 
154     /* Start with the raw Hostent */
155     CurrentSize = sizeof(HOSTENT);
156 
157     /* Return the size needed now */
158     if (CurrentSize > (DWORD)BufferLength) return BufferSize;
159 
160     /* Copy the Hostent and initialize it */
161     CopyMemory(p, Hostent, sizeof(HOSTENT));
162     p = Buffer + CurrentSize;
163     ReturnedHostent->h_name = NULL;
164     ReturnedHostent->h_aliases = NULL;
165     ReturnedHostent->h_addr_list = NULL;
166 
167     /* Find out how many aliases there are */
168     while (Hostent->h_aliases[Aliases])
169     {
170         /* Increase the alias count */
171         Aliases++;
172     }
173 
174     /* Add the aliases to the size, and validate it */
175     CurrentSize += (Aliases + 1) * sizeof(ULONG_PTR);
176     if (CurrentSize > (DWORD)BufferLength)
177     {
178         /* Clear the aliases and return */
179         Hostent->h_aliases = NULL;
180         return BufferSize;
181     }
182 
183     /* Write the aliases, update the pointer */
184     ReturnedHostent->h_aliases = (PCHAR*)p;
185     p = Buffer + CurrentSize;
186 
187     /* Find out how many names there are */
188     while (Hostent->h_addr_list[Names])
189     {
190         /* Increase the alias count */
191         Names++;
192     }
193 
194     /* Add the names to the size, and validate it */
195     CurrentSize += (Names + 1) * sizeof(ULONG_PTR);
196     if (CurrentSize > (DWORD)BufferLength)
197     {
198         /* Clear the aliases and return */
199         Hostent->h_addr_list = NULL;
200         return BufferSize;
201     }
202 
203     /* Write the names, update the pointer */
204     ReturnedHostent->h_addr_list = (PCHAR*)p;
205     p = Buffer + CurrentSize;
206 
207     /* Now add the names */
208     for (i = 0; i < Names; i++)
209     {
210         /* Update size and validate */
211         CurrentSize += Hostent->h_length;
212         if (CurrentSize > (DWORD)BufferLength) return BufferSize;
213 
214         /* Write pointer and copy */
215         ReturnedHostent->h_addr_list[i] = p;
216         CopyMemory(p, Hostent->h_addr_list[i], Hostent->h_length);
217 
218         /* Update pointer */
219         p = Buffer + CurrentSize;
220     }
221 
222     /* Finalize the list */
223     ReturnedHostent->h_addr_list[i] = NULL;
224 
225     /* Add the service name to the size, and validate it */
226     NameSize = strlen(Hostent->h_name) + sizeof(CHAR);
227     CurrentSize += NameSize;
228     if (CurrentSize > (DWORD)BufferLength) return BufferSize;
229 
230     /* Write the service name and update the pointer */
231     ReturnedHostent->h_name = p;
232     CopyMemory(p, Hostent->h_name, NameSize);
233     p = Buffer + CurrentSize;
234 
235     /* Now add the aliases */
236     for (i = 0; i < Aliases; i++)
237     {
238         /* Update size and validate */
239         NameSize = strlen(Hostent->h_aliases[i]) + sizeof(CHAR);
240         CurrentSize += NameSize;
241         if (CurrentSize > (DWORD)BufferLength) return BufferSize;
242 
243         /* Write pointer and copy */
244         ReturnedHostent->h_aliases[i] = p;
245         CopyMemory(p, Hostent->h_aliases[i], NameSize);
246 
247         /* Update pointer */
248         p = Buffer + CurrentSize;
249     }
250 
251     /* Finalize the list and return */
252     ReturnedHostent->h_aliases[i] = NULL;
253     return BufferSize;
254 }
255 
256 SIZE_T
257 WSAAPI
258 CopyServentToBuffer(IN PCHAR Buffer,
259                     IN INT BufferLength,
260                     IN PSERVENT Servent)
261 {
262     SIZE_T BufferSize, CurrentSize, NameSize;
263     PCHAR p = Buffer;
264     DWORD Aliases = 0;
265     DWORD i;
266     PSERVENT ReturnedServent = (PSERVENT)Buffer;
267 
268     /* Determine the buffer size required */
269     BufferSize = BytesInServent(Servent);
270 
271     /* Check which size to use */
272     if ((DWORD)BufferLength > BufferSize)
273     {
274         /* Zero the buffer */
275         ZeroMemory(Buffer, BufferSize);
276     }
277     else
278     {
279         /* Zero the buffer */
280         ZeroMemory(Buffer, BufferLength);
281     }
282 
283     /* Start with the raw servent */
284     CurrentSize = sizeof(SERVENT);
285 
286     /* Return the size needed now */
287     if (CurrentSize > (DWORD)BufferLength) return BufferSize;
288 
289     /* Copy the servent and initialize it */
290     CopyMemory(p, Servent, sizeof(SERVENT));
291     p = Buffer + CurrentSize;
292     ReturnedServent->s_name = NULL;
293     ReturnedServent->s_aliases = NULL;
294     ReturnedServent->s_proto = NULL;
295 
296     /* Find out how many aliases there are */
297     while (Servent->s_aliases[Aliases])
298     {
299         /* Increase the alias count */
300         Aliases++;
301     }
302 
303     /* Add the aliases to the size, and validate it */
304     CurrentSize += (Aliases + 1) * sizeof(ULONG_PTR);
305     if (CurrentSize > (DWORD)BufferLength)
306     {
307         /* Clear the aliases and return */
308         Servent->s_aliases = NULL;
309         return BufferSize;
310     }
311 
312     /* Write the aliases, update the pointer */
313     ReturnedServent->s_aliases = (PCHAR*)p;
314     p = Buffer + CurrentSize;
315 
316     /* Add the service name to the size, and validate it */
317     NameSize = strlen(Servent->s_name) + sizeof(CHAR);
318     CurrentSize += NameSize;
319     if (CurrentSize > (DWORD)BufferLength) return BufferSize;
320 
321     /* Write the service name and update the pointer */
322     ReturnedServent->s_name = p;
323     CopyMemory(p, Servent->s_name, NameSize);
324     p = Buffer + CurrentSize;
325 
326     /* Now add the aliases */
327     for (i = 0; i < Aliases; i++)
328     {
329         /* Update size and validate */
330         NameSize = strlen(Servent->s_aliases[i]) + sizeof(CHAR);
331         CurrentSize += NameSize;
332         if (CurrentSize > (DWORD)BufferLength) return BufferSize;
333 
334         /* Write pointer and copy */
335         ReturnedServent->s_aliases[i] = p;
336         CopyMemory(p, Servent->s_aliases[i], NameSize);
337 
338         /* Update pointer */
339         p = Buffer + CurrentSize;
340     }
341 
342     /* Finalize the list and return */
343     ReturnedServent->s_aliases[i] = NULL;
344     return BufferSize;
345 }
346 
347 SIZE_T
348 WSAAPI
349 CopyProtoentToBuffer(IN PCHAR Buffer,
350                      IN INT BufferLength,
351                      IN PPROTOENT Protoent)
352 {
353     SIZE_T BufferSize, CurrentSize, NameSize;
354     PCHAR p = Buffer;
355     DWORD Aliases = 0;
356     DWORD i;
357     PPROTOENT ReturnedProtoent = (PPROTOENT)Buffer;
358 
359     /* Determine the buffer size required */
360     BufferSize = BytesInProtoent(Protoent);
361 
362     /* Check which size to use */
363     if ((DWORD)BufferLength > BufferSize)
364     {
365         /* Zero the buffer */
366         ZeroMemory(Buffer, BufferSize);
367     }
368     else
369     {
370         /* Zero the buffer */
371         ZeroMemory(Buffer, BufferLength);
372     }
373 
374     /* Start with the raw servent */
375     CurrentSize = sizeof(PROTOENT);
376 
377     /* Return the size needed now */
378     if (CurrentSize > (DWORD)BufferLength) return BufferSize;
379 
380     /* Copy the servent and initialize it */
381     CopyMemory(p, Protoent, sizeof(PROTOENT));
382     p = Buffer + CurrentSize;
383     ReturnedProtoent->p_name = NULL;
384     ReturnedProtoent->p_aliases = NULL;
385 
386     /* Find out how many aliases there are */
387     while (Protoent->p_aliases[Aliases])
388     {
389         /* Increase the alias count */
390         Aliases++;
391     }
392 
393     /* Add the aliases to the size, and validate it */
394     CurrentSize += (Aliases + 1) * sizeof(ULONG_PTR);
395     if (CurrentSize > (DWORD)BufferLength)
396     {
397         /* Clear the aliases and return */
398         Protoent->p_aliases = NULL;
399         return BufferSize;
400     }
401 
402     /* Write the aliases, update the pointer */
403     ReturnedProtoent->p_aliases = (PCHAR*)p;
404     p = Buffer + CurrentSize;
405 
406     /* Add the service name to the size, and validate it */
407     NameSize = strlen(Protoent->p_name) + sizeof(CHAR);
408     CurrentSize += NameSize;
409     if (CurrentSize > (DWORD)BufferLength) return BufferSize;
410 
411     /* Write the service name and update the pointer */
412     ReturnedProtoent->p_name = p;
413     CopyMemory(p, Protoent->p_name, NameSize);
414     p = Buffer + CurrentSize;
415 
416     /* Now add the aliases */
417     for (i = 0; i < Aliases; i++)
418     {
419         /* Update size and validate */
420         NameSize = strlen(Protoent->p_aliases[i]) + sizeof(CHAR);
421         CurrentSize += NameSize;
422         if (CurrentSize > (DWORD)BufferLength) return BufferSize;
423 
424         /* Write pointer and copy */
425         ReturnedProtoent->p_aliases[i] = p;
426         CopyMemory(p, Protoent->p_aliases[i], NameSize);
427 
428         /* Update pointer */
429         p = Buffer + CurrentSize;
430     }
431 
432     /* Finalize the list and return */
433     ReturnedProtoent->p_aliases[i] = NULL;
434     return BufferSize;
435 }
436 
437 PWSASYNCBLOCK
438 WSAAPI
439 WsAsyncAllocateBlock(IN SIZE_T ExtraLength)
440 {
441     PWSASYNCBLOCK AsyncBlock;
442 
443     /* Add the size of the block */
444     ExtraLength += sizeof(WSASYNCBLOCK);
445 
446     /* Allocate it */
447     AsyncBlock = HeapAlloc(WsSockHeap, 0, ExtraLength);
448 
449     /* Get a handle to it */
450     AsyncBlock->TaskHandle = UlongToPtr(InterlockedIncrement(&WsAsyncTaskHandle));
451 
452     /* Return it */
453     return AsyncBlock;
454 }
455 
456 BOOL
457 WINAPI
458 WsAsyncThreadBlockingHook(VOID)
459 {
460     /* Check if this task is being cancelled */
461     if (WsAsyncCurrentTaskHandle == WsAsyncCancelledTaskHandle)
462     {
463         /* Cancel the blocking call so we can get back */
464         WSACancelBlockingCall();
465     }
466 
467     /* Return to system */
468     return FALSE;
469 }
470 
471 VOID
472 WSAAPI
473 WsAsyncFreeBlock(IN PWSASYNCBLOCK AsyncBlock)
474 {
475     /* Free it */
476     HeapFree(WsSockHeap, 0, AsyncBlock);
477 }
478 
479 VOID
480 WSAAPI
481 WsAsyncGetServ(IN HANDLE TaskHandle,
482                IN DWORD Operation,
483                IN HWND hWnd,
484                IN UINT wMsg,
485                IN CHAR FAR *ByWhat,
486                IN CHAR FAR *Protocol,
487                IN CHAR FAR *Buffer,
488                IN INT BufferLength)
489 {
490     PSERVENT Servent;
491     SIZE_T BufferSize = 0;
492     LPARAM lParam;
493     INT ErrorCode = 0;
494 
495     /* Check the operation */
496     if (Operation == WsAsyncGetServByName)
497     {
498         /* Call the API */
499         Servent = getservbyname(ByWhat, Protocol);
500     }
501     else
502     {
503         /* Call the API */
504         Servent = getservbyport(PtrToUlong(ByWhat), Protocol);
505     }
506 
507     /* Make sure we got one */
508     if (!Servent) ErrorCode = GetLastError();
509 
510     /* Acquire the lock */
511     WsAsyncLock();
512 
513     /* Check if this task got cancelled */
514     if (TaskHandle == WsAsyncCancelledTaskHandle)
515     {
516         /* Return */
517         WsAsyncUnlock();
518         return;
519     }
520 
521     /* If we got a Servent back, copy it */
522     if (Servent)
523     {
524         /* Copy it into the buffer */
525         BufferSize = CopyServentToBuffer(Buffer, BufferLength, Servent);
526 
527         /* Check if we had enough space */
528         if (BufferSize > (DWORD)BufferLength)
529         {
530             /* Not enough */
531             ErrorCode = WSAENOBUFS;
532         }
533         else
534         {
535             /* Perfect */
536             ErrorCode = NO_ERROR;
537         }
538     }
539 
540     /* Not processing anymore */
541     WsAsyncCurrentTaskHandle = NULL;
542 
543     /* Release the lock */
544     WsAsyncUnlock();
545 
546     /* Make the messed-up lParam reply */
547     lParam = WSAMAKEASYNCREPLY(BufferSize, ErrorCode);
548 
549     /* Sent it through the Upcall API */
550     WPUPostMessage(hWnd, wMsg, (WPARAM)TaskHandle, lParam);
551 }
552 
553 VOID
554 WSAAPI
555 WsAsyncGetProto(IN HANDLE TaskHandle,
556                 IN DWORD Operation,
557                 IN HWND hWnd,
558                 IN UINT wMsg,
559                 IN CHAR FAR *ByWhat,
560                 IN CHAR FAR *Buffer,
561                 IN INT BufferLength)
562 {
563     PPROTOENT Protoent;
564     SIZE_T BufferSize = 0;
565     LPARAM lParam;
566     INT ErrorCode = 0;
567 
568     /* Check the operation */
569     if (Operation == WsAsyncGetProtoByName)
570     {
571         /* Call the API */
572         Protoent = getprotobyname(ByWhat);
573     }
574     else
575     {
576         /* Call the API */
577         Protoent = getprotobynumber(PtrToUlong(ByWhat));
578     }
579 
580     /* Make sure we got one */
581     if (!Protoent) ErrorCode = GetLastError();
582 
583     /* Acquire the lock */
584     WsAsyncLock();
585 
586     /* Check if this task got cancelled */
587     if (TaskHandle == WsAsyncCancelledTaskHandle)
588     {
589         /* Return */
590         WsAsyncUnlock();
591         return;
592     }
593 
594     /* If we got a Servent back, copy it */
595     if (Protoent)
596     {
597         /* Copy it into the buffer */
598         BufferSize = CopyProtoentToBuffer(Buffer, BufferLength, Protoent);
599 
600         /* Check if we had enough space */
601         if (BufferSize > (DWORD)BufferLength)
602         {
603             /* Not enough */
604             ErrorCode = WSAENOBUFS;
605         }
606         else
607         {
608             /* Perfect */
609             ErrorCode = NO_ERROR;
610         }
611     }
612 
613     /* Not processing anymore */
614     WsAsyncCurrentTaskHandle = NULL;
615 
616     /* Release the lock */
617     WsAsyncUnlock();
618 
619     /* Make the messed-up lParam reply */
620     lParam = WSAMAKEASYNCREPLY(BufferSize, ErrorCode);
621 
622     /* Sent it through the Upcall API */
623     WPUPostMessage(hWnd, wMsg, (WPARAM)TaskHandle, lParam);
624 }
625 
626 VOID
627 WSAAPI
628 WsAsyncGetHost(IN HANDLE TaskHandle,
629                IN DWORD Operation,
630                IN HWND hWnd,
631                IN UINT wMsg,
632                IN CHAR FAR *ByWhat,
633                IN INT Length,
634                IN INT Type,
635                IN CHAR FAR *Buffer,
636                IN INT BufferLength)
637 {
638     PHOSTENT Hostent;
639     SIZE_T BufferSize = 0;
640     LPARAM lParam;
641     INT ErrorCode = 0;
642 
643     /* Check the operation */
644     if (Operation == WsAsyncGetHostByAddr)
645     {
646         /* Call the API */
647         Hostent = gethostbyaddr(ByWhat, Length, Type);
648     }
649     else
650     {
651         /* Call the API */
652         Hostent = gethostbyname(ByWhat);
653     }
654 
655     /* Make sure we got one */
656     if (!Hostent) ErrorCode = GetLastError();
657 
658     /* Acquire the lock */
659     WsAsyncLock();
660 
661     /* Check if this task got cancelled */
662     if (TaskHandle == WsAsyncCancelledTaskHandle)
663     {
664         /* Return */
665         WsAsyncUnlock();
666         return;
667     }
668 
669     /* If we got a Servent back, copy it */
670     if (Hostent)
671     {
672         /* Copy it into the buffer */
673         BufferSize = CopyHostentToBuffer(Buffer, BufferLength, Hostent);
674 
675         /* Check if we had enough space */
676         if (BufferSize > (DWORD)BufferLength)
677         {
678             /* Not enough */
679             ErrorCode = WSAENOBUFS;
680         }
681         else
682         {
683             /* Perfect */
684             ErrorCode = NO_ERROR;
685         }
686     }
687 
688     /* Not processing anymore */
689     WsAsyncCurrentTaskHandle = NULL;
690 
691     /* Release the lock */
692     WsAsyncUnlock();
693 
694     /* Make the messed-up lParam reply */
695     lParam = WSAMAKEASYNCREPLY(BufferSize, ErrorCode);
696 
697     /* Sent it through the Upcall API */
698     WPUPostMessage(hWnd, wMsg, (WPARAM)TaskHandle, lParam);
699 }
700 
701 DWORD
702 WINAPI
703 WsAsyncThread(IN PVOID ThreadContext)
704 {
705     PWSASYNCCONTEXT Context = ThreadContext;
706     PWSASYNCBLOCK AsyncBlock;
707     PLIST_ENTRY Entry;
708     HANDLE AsyncEvent = Context->AsyncEvent;
709     PLIST_ENTRY ListHead = &Context->AsyncQueue;
710 
711     /* Set the blocking hook */
712     WSASetBlockingHook((FARPROC)WsAsyncThreadBlockingHook);
713 
714     /* Loop */
715     while (TRUE)
716     {
717         /* Wait for the event */
718         WaitForSingleObject(AsyncEvent, INFINITE);
719 
720         /* Get the lock */
721         WsAsyncLock();
722 
723         /* Process the queue */
724         while (!IsListEmpty(ListHead))
725         {
726             /* Remove this entry and get the async block */
727             Entry = RemoveHeadList(ListHead);
728             AsyncBlock = CONTAINING_RECORD(Entry, WSASYNCBLOCK, AsyncQueue);
729 
730             /* Save the current task handle */
731             WsAsyncCurrentTaskHandle = AsyncBlock->TaskHandle;
732 
733             /* Release the lock */
734             WsAsyncUnlock();
735 
736             /* Check which operation to do */
737             switch (AsyncBlock->Operation)
738             {
739                 /* Get Host by Y */
740                 case WsAsyncGetHostByAddr: case WsAsyncGetHostByName:
741 
742                     /* Call the handler */
743                     WsAsyncGetHost(AsyncBlock->TaskHandle,
744                                    AsyncBlock->Operation,
745                                    AsyncBlock->GetHost.hWnd,
746                                    AsyncBlock->GetHost.wMsg,
747                                    AsyncBlock->GetHost.ByWhat,
748                                    AsyncBlock->GetHost.Length,
749                                    AsyncBlock->GetHost.Type,
750                                    AsyncBlock->GetHost.Buffer,
751                                    AsyncBlock->GetHost.BufferLength);
752                     break;
753 
754                 /* Get Proto by Y */
755                 case WsAsyncGetProtoByNumber: case WsAsyncGetProtoByName:
756 
757                     /* Call the handler */
758                     WsAsyncGetProto(AsyncBlock->TaskHandle,
759                                     AsyncBlock->Operation,
760                                     AsyncBlock->GetProto.hWnd,
761                                     AsyncBlock->GetProto.wMsg,
762                                     AsyncBlock->GetHost.ByWhat,
763                                     AsyncBlock->GetProto.Buffer,
764                                     AsyncBlock->GetProto.BufferLength);
765                     break;
766 
767                 /* Get Serv by Y */
768                 case WsAsyncGetServByPort: case WsAsyncGetServByName:
769 
770                     /* Call the handler */
771                     WsAsyncGetServ(AsyncBlock->TaskHandle,
772                                    AsyncBlock->Operation,
773                                    AsyncBlock->GetServ.hWnd,
774                                    AsyncBlock->GetServ.wMsg,
775                                    AsyncBlock->GetServ.ByWhat,
776                                    AsyncBlock->GetServ.Protocol,
777                                    AsyncBlock->GetServ.Buffer,
778                                    AsyncBlock->GetServ.BufferLength);
779                     break;
780 
781                 /* Termination */
782                 case WsAsyncTerminate:
783 
784                     /* Clean up the extra reference */
785                     WSACleanup();
786 
787                     /* Free the context block */
788                     WsAsyncFreeBlock(AsyncBlock);
789 
790                     /* Acquire the lock */
791                     WsAsyncLock();
792 
793                     /* Loop the queue and flush it */
794                     while (!IsListEmpty(ListHead))
795                     {
796                         Entry = RemoveHeadList(ListHead);
797                         AsyncBlock = CONTAINING_RECORD(Entry,
798                                                        WSASYNCBLOCK,
799                                                        AsyncQueue);
800                         WsAsyncFreeBlock(AsyncBlock);
801                     }
802 
803                     /* Release lock */
804                     WsAsyncUnlock();
805 
806                     /* Close the event, free the Context */
807                     CloseHandle(AsyncEvent);
808                     HeapFree(WsSockHeap, 0, Context);
809 
810                     /* Remove the extra DLL reference and kill us */
811                     FreeLibraryAndExitThread(WsAsyncDllHandle, 0);
812 
813                 default:
814                     break;
815             }
816 
817             /* Done processing */
818             WsAsyncCurrentTaskHandle = NULL;
819 
820             /* Free this block, get lock and reloop */
821             WsAsyncFreeBlock(AsyncBlock);
822             WsAsyncLock();
823         }
824 
825         /* Release the lock */
826         WsAsyncUnlock();
827     }
828 }
829 
830 BOOL
831 WSAAPI
832 WsAsyncCheckAndInitThread(VOID)
833 {
834     HANDLE ThreadHandle;
835     DWORD Tid;
836     PWSASYNCCONTEXT Context = NULL;
837     WSADATA WsaData;
838 
839     /* Make sure we're not initialized */
840     if (WsAsyncThreadInitialized) return TRUE;
841 
842     /* Acquire the lock */
843     WsAsyncLock();
844 
845     /* Make sure we're not initialized */
846     if (!WsAsyncThreadInitialized)
847     {
848         /* Initialize Thread Context */
849         Context = HeapAlloc(WsSockHeap, 0, sizeof(*Context));
850         if (!Context)
851             goto Exit;
852 
853         /* Initialize the Queue and event */
854         WsAsyncQueue = &Context->AsyncQueue;
855         InitializeListHead(WsAsyncQueue);
856         Context->AsyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
857         WsAsyncEvent = Context->AsyncEvent;
858 
859         /* Prevent us from ever being killed while running */
860         if (WSAStartup(MAKEWORD(2,2), &WsaData) != ERROR_SUCCESS)
861             goto Fail;
862 
863         /* Create the thread */
864         ThreadHandle = CreateThread(NULL,
865                                     0,
866                                     WsAsyncThread,
867                                     Context,
868                                     0,
869                                     &Tid);
870         if (ThreadHandle == NULL)
871         {
872             /* Cleanup and fail */
873             WSACleanup();
874             goto Fail;
875         }
876 
877         /* Close the handle and set init */
878         CloseHandle(ThreadHandle);
879         WsAsyncThreadInitialized = TRUE;
880     }
881 
882 Exit:
883     /* Release the lock */
884     WsAsyncUnlock();
885     return WsAsyncThreadInitialized;
886 
887 Fail:
888     /* Close the event, free the Context */
889     if (Context->AsyncEvent)
890         CloseHandle(Context->AsyncEvent);
891     HeapFree(WsSockHeap, 0, Context);
892 
893     /* Bail out */
894     goto Exit;
895 }
896 
897 VOID
898 WSAAPI
899 WsAsyncTerminateThread(VOID)
900 {
901     PWSASYNCBLOCK AsyncBlock;
902 
903     /* Make sure we're initialized */
904     if (!WsAsyncThreadInitialized) return;
905 
906     /* Allocate a block */
907     AsyncBlock = WsAsyncAllocateBlock(0);
908 
909     /* Initialize it for termination */
910     AsyncBlock->Operation = WsAsyncTerminate;
911 
912     /* Queue the request and return */
913     WsAsyncQueueRequest(AsyncBlock);
914     WsAsyncThreadInitialized = FALSE;
915 }
916 
917 VOID
918 WSAAPI
919 WsAsyncQueueRequest(IN PWSASYNCBLOCK AsyncBlock)
920 {
921     /* Get the lock */
922     WsAsyncLock();
923 
924     /* Insert it into the queue */
925     InsertTailList(WsAsyncQueue, &AsyncBlock->AsyncQueue);
926 
927     /* Wake up the thread */
928     SetEvent(WsAsyncEvent);
929 
930     /* Release lock and return */
931     WsAsyncUnlock();
932 }
933 
934 INT
935 WSAAPI
936 WsAsyncCancelRequest(IN HANDLE TaskHandle)
937 {
938     PLIST_ENTRY Entry;
939     PWSASYNCBLOCK AsyncBlock;
940 
941     /* Make sure we're initialized */
942     if (!WsAsyncThreadInitialized) return WSAEINVAL;
943 
944     /* Acquire the lock */
945     WsAsyncLock();
946 
947     /* Check if we're cancelling the current task */
948     if (TaskHandle == WsAsyncCurrentTaskHandle)
949     {
950         /* Mark us as cancelled, the async thread will see this later */
951         WsAsyncCancelledTaskHandle = TaskHandle;
952 
953         /* Release lock and return */
954         WsAsyncUnlock();
955         return NO_ERROR;
956     }
957 
958     /* Loop the queue */
959     Entry = WsAsyncQueue->Flink;
960     while (Entry != WsAsyncQueue)
961     {
962         /* Get the Async Block */
963         AsyncBlock = CONTAINING_RECORD(Entry, WSASYNCBLOCK, AsyncQueue);
964 
965         /* Check if this is the one */
966         if (TaskHandle == AsyncBlock->TaskHandle)
967         {
968             /* It is, remove it */
969             RemoveEntryList(Entry);
970 
971             /* Release the lock, free the block, and return */
972             WsAsyncUnlock();
973             WsAsyncFreeBlock(AsyncBlock);
974             return NO_ERROR;
975         }
976 
977         /* Move to the next entry */
978         Entry = Entry->Flink;
979     }
980 
981     /* Nothing found, fail */
982     WsAsyncUnlock();
983     return WSAEINVAL;
984 }
985