1 /* MIT License
2 *
3 * Copyright (c) 2024 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26
27 #include "ares_setup.h"
28 #include "ares.h"
29 #include "ares_private.h"
30 #include "ares_event.h"
31 #include "ares_event_win32.h"
32 #ifdef HAVE_LIMITS_H
33 # include <limits.h>
34 #endif
35
36 #ifdef _WIN32
37
38 /* IMPLEMENTATION NOTES
39 * ====================
40 *
41 * This implementation uses some undocumented functionality within Windows for
42 * monitoring sockets. The Ancillary Function Driver (AFD) is the low level
43 * implementation that Winsock2 sits on top of. Winsock2 unfortunately does
44 * not expose the equivalent of epoll() or kqueue(), but it is possible to
45 * access AFD directly and use along with IOCP to simulate the functionality.
46 * We want to use IOCP if possible as it gives us the ability to monitor more
47 * than just sockets (WSAPoll is not an option), and perform arbitrary callbacks
48 * which means we can hook in non-socket related events.
49 *
50 * The information for this implementation was gathered from "wepoll" and
51 * "libuv" which both use slight variants on this, but this implementation
52 * doesn't directly follow either methodology.
53 *
54 * Initialization:
55 * 1. Dynamically load the NtDeviceIoControlFile and NtCancelIoFileEx internal
56 * symbols from ntdll.dll. These functions are used to submit the AFD POLL
57 * request and to cancel a prior request, respectively.
58 * 2. Create an IO Completion Port base handle via CreateIoCompletionPort()
59 * that all socket events will be delivered through.
60 * 3. Create a callback to be used to be able to interrupt waiting for IOCP
61 * events, this may be called for allowing enqueuing of additional socket
62 * events or removing socket events. PostQueuedCompletionStatus() is the
63 * obvious choice. Use the same container structure as used with a Socket
64 * but tagged indicating it is not as the CompletionKey (important!).
65 *
66 * Socket Add:
67 * 1. Create/Allocate a container for holding metadata about a socket:
68 * - SOCKET base_socket;
69 * - SOCKET peer_socket;
70 * - OVERLAPPED overlapped; -- Used by AFD POLL
71 * - AFD_POLL_INFO afd_poll_info; -- Used by AFD POLL
72 * 2. Call WSAIoctl(..., SIO_BASE_HANDLE, ...) to unwrap the SOCKET and get
73 * the "base socket" we can use for polling. It appears this may fail so
74 * we should call WSAIoctl(..., SIO_BSP_HANDLE_POLL, ...) as a fallback.
75 * 3. The SOCKET handle we have is most likely not capable of supporting
76 * OVERLAPPED, and we need to have a way to unbind a socket from IOCP
77 * (which is done via a simple closesocket()) so we need to duplicate the
78 * "base socket" using WSADuplicateSocketW() followed by
79 * WSASocketW(..., WSA_FLAG_OVERLAPPED) to create this "peer socket" for
80 * submitting AFD POLL requests.
81 * 4. Bind to IOCP using CreateIoCompletionPort() referencing the "peer
82 * socket" and the base IOCP handle from "Initialization". Use the
83 * pointer to the socket container as the "CompletionKey" which will be
84 * returned when an event occurs.
85 * 5. Submit AFD POLL request (see "AFD POLL Request" section)
86 *
87 * Socket Delete:
88 * 1. Call "AFD Poll Cancel" (see Section of same name)
89 * 2. If a cancel was requested (not bypassed due to no events, etc), tag the
90 * "container" for the socket as pending delete, and when the next IOCP
91 * event for the socket is dequeued, cleanup.
92 * 3. Otherwise, call closesocket(peer_socket) then free() the container
93 * which will officially delete it.
94 * NOTE: Deferring delete may be completely unnecessary. In theory closing
95 * the peer_socket() should guarantee no additional events will be
96 * delivered. But maybe if there's a pending event that hasn't been
97 * read yet but already trigggered it would be an issue, so this is
98 * "safer" unless we can prove its not necessary.
99 *
100 * Socket Modify:
101 * 1. Call "AFD Poll Cancel" (see Section of same name)
102 * 2. If a cancel was not enqueued because there is no pending request,
103 * submit AFD POLL request (see "AFD POLL Request" section), otherwise
104 * defer until next socket event.
105 *
106 * Event Wait:
107 * 1. Call GetQueuedCompletionStatusEx() with the base IOCP handle, a
108 * stack allocated array of OVERLAPPED_ENTRY's, and an appropriate
109 * timeout.
110 * 2. Iterate across returned events, the CompletionKey is a pointer to the
111 * container registered with CreateIoCompletionPort() or
112 * PostQueuedCompletionStatus()
113 * 3. If object indicates it is pending delete, go ahead and
114 * closesocket(peer_socket) and free() the container. Go to the next event.
115 * 4. Submit AFD POLL Request (see "AFD POLL Request"). We must re-enable
116 * the request each time we receive a response, it is not persistent.
117 * 5. Notify of any events received as indicated in the AFD_POLL_INFO
118 * Handles[0].Events (NOTE: check NumberOfHandles first, make sure it is
119 * > 0, otherwise we might not have events such as if our last request
120 * was cancelled).
121 *
122 * AFD Poll Request:
123 * 1. Initialize the AFD_POLL_INFO structure:
124 * Exclusive = TRUE; // Auto cancel duplicates for same socket
125 * NumberOfHandles = 1;
126 * Timeout.QuadPart = LLONG_MAX;
127 * Handles[0].Handle = (HANDLE)base_socket;
128 * Handles[0].Status = 0;
129 * Handles[0].Events = ... set as appropriate AFD_POLL_RECEIVE, etc;
130 * 2. Zero out the OVERLAPPED structure
131 * 3. Create an IO_STATUS_BLOCK pointer (iosb) and set it to the address of
132 * the OVERLAPPED "Internal" member.
133 * 4. Set the "Status" member of IO_STATUS_BLOCK to STATUS_PENDING
134 * 5. Call
135 * NtDeviceIoControlFile((HANDLE)peer_socket, NULL, NULL, &overlapped,
136 * iosb, IOCTL_AFD_POLL
137 * &afd_poll_info, sizeof(afd_poll_info),
138 * &afd_poll_info, sizeof(afd_poll_info));
139 * NOTE: Its not clear to me if the IO_STATUS_BLOCK pointing to OVERLAPPED
140 * is for efficiency or if its a requirement for AFD. This is what
141 * libuv does, so I'm doing it here too.
142 *
143 * AFD Poll Cancel:
144 * 1. Check to see if the IO_STATUS_BLOCK "Status" member for the socket
145 * is still STATUS_PENDING, if not, no cancel request is necessary.
146 * 2. Call
147 * NtCancelIoFileEx((HANDLE)peer_socket, iosb, &temp_iosb);
148 *
149 *
150 * References:
151 * - https://github.com/piscisaureus/wepoll/
152 * - https://github.com/libuv/libuv/
153 */
154
155 typedef struct {
156 /* Dynamically loaded symbols */
157 NtDeviceIoControlFile_t NtDeviceIoControlFile;
158 NtCancelIoFileEx_t NtCancelIoFileEx;
159
160 /* Implementation details */
161 HANDLE iocp_handle;
162 } ares_evsys_win32_t;
163
164 typedef struct {
165 /*! Pointer to parent event container */
166 ares_event_t *event;
167 /*! Socket passed in to monitor */
168 SOCKET socket;
169 /*! Base socket derived from provided socket */
170 SOCKET base_socket;
171 /*! New socket (duplicate base_socket handle) supporting OVERLAPPED operation
172 */
173 SOCKET peer_socket;
174 /*! Structure for submitting AFD POLL requests (Internals!) */
175 AFD_POLL_INFO afd_poll_info;
176 /*! Overlapped structure submitted with AFD POLL requests and returned with
177 * IOCP results */
178 OVERLAPPED overlapped;
179 } ares_evsys_win32_eventdata_t;
180
ares_iocpevent_signal(const ares_event_t * event)181 static void ares_iocpevent_signal(const ares_event_t *event)
182 {
183 ares_event_thread_t *e = event->e;
184 ares_evsys_win32_t *ew = e->ev_sys_data;
185
186 if (e == NULL) {
187 return;
188 }
189
190 PostQueuedCompletionStatus(ew->iocp_handle, 0, (ULONG_PTR)event->data, NULL);
191 }
192
ares_iocpevent_cb(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)193 static void ares_iocpevent_cb(ares_event_thread_t *e, ares_socket_t fd,
194 void *data, ares_event_flags_t flags)
195 {
196 (void)e;
197 (void)data;
198 (void)fd;
199 (void)flags;
200 }
201
ares_iocpevent_create(ares_event_thread_t * e)202 static ares_event_t *ares_iocpevent_create(ares_event_thread_t *e)
203 {
204 ares_event_t *event = NULL;
205 ares_status_t status;
206
207 status =
208 ares_event_update(&event, e, ARES_EVENT_FLAG_OTHER, ares_iocpevent_cb,
209 ARES_SOCKET_BAD, NULL, NULL, ares_iocpevent_signal);
210 if (status != ARES_SUCCESS) {
211 return NULL;
212 }
213
214 return event;
215 }
216
ares_evsys_win32_destroy(ares_event_thread_t * e)217 static void ares_evsys_win32_destroy(ares_event_thread_t *e)
218 {
219 ares_evsys_win32_t *ew = NULL;
220
221 if (e == NULL) {
222 return;
223 }
224
225 ew = e->ev_sys_data;
226 if (ew == NULL) {
227 return;
228 }
229
230 if (ew->iocp_handle != NULL) {
231 CloseHandle(ew->iocp_handle);
232 }
233
234 ares_free(ew);
235 e->ev_sys_data = NULL;
236 }
237
ares_evsys_win32_init(ares_event_thread_t * e)238 static ares_bool_t ares_evsys_win32_init(ares_event_thread_t *e)
239 {
240 ares_evsys_win32_t *ew = NULL;
241 HMODULE ntdll;
242
243 ew = ares_malloc_zero(sizeof(*ew));
244 if (ew == NULL) {
245 return ARES_FALSE;
246 }
247
248 e->ev_sys_data = ew;
249
250 /* All apps should have ntdll.dll already loaded, so just get a handle to
251 * this */
252 ntdll = GetModuleHandleA("ntdll.dll");
253 if (ntdll == NULL) {
254 goto fail;
255 }
256
257 /* Load Internal symbols not typically accessible */
258 ew->NtDeviceIoControlFile = (NtDeviceIoControlFile_t)(void *)GetProcAddress(
259 ntdll, "NtDeviceIoControlFile");
260 ew->NtCancelIoFileEx =
261 (NtCancelIoFileEx_t)(void *)GetProcAddress(ntdll, "NtCancelIoFileEx");
262
263 if (ew->NtCancelIoFileEx == NULL || ew->NtDeviceIoControlFile == NULL) {
264 goto fail;
265 }
266
267 ew->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
268 if (ew->iocp_handle == NULL) {
269 goto fail;
270 }
271
272 e->ev_signal = ares_iocpevent_create(e);
273 if (e->ev_signal == NULL) {
274 goto fail;
275 }
276
277 return ARES_TRUE;
278
279 fail:
280 ares_evsys_win32_destroy(e);
281 return ARES_FALSE;
282 }
283
ares_evsys_win32_basesocket(ares_socket_t socket)284 static ares_socket_t ares_evsys_win32_basesocket(ares_socket_t socket)
285 {
286 while (1) {
287 DWORD bytes; /* Not used */
288 ares_socket_t base_socket = ARES_SOCKET_BAD;
289 int rv;
290
291 rv = WSAIoctl(socket, SIO_BASE_HANDLE, NULL, 0, &base_socket,
292 sizeof(base_socket), &bytes, NULL, NULL);
293 if (rv != SOCKET_ERROR && base_socket != ARES_SOCKET_BAD) {
294 socket = base_socket;
295 break;
296 }
297
298 /* If we're here, an error occurred */
299 if (GetLastError() == WSAENOTSOCK) {
300 /* This is critical, exit */
301 return ARES_SOCKET_BAD;
302 }
303
304 /* Work around known bug in Komodia based LSPs, use ARES_BSP_HANDLE_POLL
305 * to retrieve the underlying socket to then loop and get the base socket:
306 * https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls
307 * https://www.komodia.com/newwiki/index.php?title=Komodia%27s_Redirector_bug_fixes#Version_2.2.2.6
308 */
309 base_socket = ARES_SOCKET_BAD;
310 rv = WSAIoctl(socket, SIO_BSP_HANDLE_POLL, NULL, 0, &base_socket,
311 sizeof(base_socket), &bytes, NULL, NULL);
312
313 if (rv != SOCKET_ERROR && base_socket != ARES_SOCKET_BAD &&
314 base_socket != socket) {
315 socket = base_socket;
316 continue; /* loop! */
317 }
318
319 return ARES_SOCKET_BAD;
320 }
321
322 return socket;
323 }
324
ares_evsys_win32_afd_enqueue(ares_event_t * event,ares_event_flags_t flags)325 static ares_bool_t ares_evsys_win32_afd_enqueue(ares_event_t *event,
326 ares_event_flags_t flags)
327 {
328 ares_event_thread_t *e = event->e;
329 ares_evsys_win32_t *ew = e->ev_sys_data;
330 ares_evsys_win32_eventdata_t *ed = event->data;
331 NTSTATUS status;
332 IO_STATUS_BLOCK *iosb_ptr;
333
334 if (e == NULL || ed == NULL || ew == NULL) {
335 return ARES_FALSE;
336 }
337
338 /* Enqueue AFD Poll */
339 ed->afd_poll_info.Exclusive = TRUE;
340 ed->afd_poll_info.NumberOfHandles = 1;
341 ed->afd_poll_info.Timeout.QuadPart = LLONG_MAX;
342 ed->afd_poll_info.Handles[0].Handle = (HANDLE)ed->base_socket;
343 ed->afd_poll_info.Handles[0].Status = 0;
344 ed->afd_poll_info.Handles[0].Events = 0;
345
346 if (flags & ARES_EVENT_FLAG_READ) {
347 ed->afd_poll_info.Handles[0].Events |=
348 (AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT |
349 AFD_POLL_ABORT);
350 }
351 if (flags & ARES_EVENT_FLAG_WRITE) {
352 ed->afd_poll_info.Handles[0].Events |=
353 (AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL);
354 }
355 if (flags == 0) {
356 ed->afd_poll_info.Handles[0].Events |= AFD_POLL_DISCONNECT;
357 }
358
359 memset(&ed->overlapped, 0, sizeof(ed->overlapped));
360 iosb_ptr = (IO_STATUS_BLOCK *)&ed->overlapped.Internal;
361 iosb_ptr->Status = STATUS_PENDING;
362
363 status = ew->NtDeviceIoControlFile(
364 (HANDLE)ed->peer_socket, NULL, NULL, &ed->overlapped, iosb_ptr,
365 IOCTL_AFD_POLL, &ed->afd_poll_info, sizeof(ed->afd_poll_info),
366 &ed->afd_poll_info, sizeof(ed->afd_poll_info));
367 if (status != STATUS_SUCCESS && status != STATUS_PENDING) {
368 printf("%s(): failed to perform IOCTL_AFD_POLL operation\n", __FUNCTION__);
369 fflush(stdout);
370 return ARES_FALSE;
371 }
372
373 return ARES_TRUE;
374 }
375
ares_evsys_win32_afd_cancel(ares_evsys_win32_eventdata_t * ed)376 static ares_bool_t ares_evsys_win32_afd_cancel(ares_evsys_win32_eventdata_t *ed)
377 {
378 IO_STATUS_BLOCK *iosb_ptr;
379 IO_STATUS_BLOCK cancel_iosb;
380 ares_evsys_win32_t *ew;
381 NTSTATUS status;
382
383 /* Detached due to destroy */
384 if (ed->event == NULL) {
385 return ARES_FALSE;
386 }
387
388 iosb_ptr = (IO_STATUS_BLOCK *)&ed->overlapped.Internal;
389 /* Not pending, nothing to do */
390 if (iosb_ptr->Status != STATUS_PENDING) {
391 return ARES_FALSE;
392 }
393
394 ew = ed->event->e->ev_sys_data;
395 status =
396 ew->NtCancelIoFileEx((HANDLE)ed->peer_socket, iosb_ptr, &cancel_iosb);
397
398 /* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed
399 * just before calling NtCancelIoFileEx(), but we have not yet received the
400 * notifiction (but it should be queued for the next IOCP event). */
401 if (status == STATUS_SUCCESS || status == STATUS_NOT_FOUND) {
402 return ARES_TRUE;
403 }
404
405 return ARES_FALSE;
406 }
407
ares_evsys_win32_eventdata_destroy(ares_evsys_win32_eventdata_t * ed)408 static void ares_evsys_win32_eventdata_destroy(ares_evsys_win32_eventdata_t *ed)
409 {
410 if (ed == NULL) {
411 return;
412 }
413
414 if (ed->peer_socket != ARES_SOCKET_BAD) {
415 closesocket(ed->peer_socket);
416 }
417
418 ares_free(ed);
419 }
420
ares_evsys_win32_event_add(ares_event_t * event)421 static ares_bool_t ares_evsys_win32_event_add(ares_event_t *event)
422 {
423 ares_event_thread_t *e = event->e;
424 ares_evsys_win32_t *ew = e->ev_sys_data;
425 ares_evsys_win32_eventdata_t *ed;
426 WSAPROTOCOL_INFOW protocol_info;
427
428 ed = ares_malloc_zero(sizeof(*ed));
429 ed->event = event;
430 ed->socket = event->fd;
431 ed->base_socket = ARES_SOCKET_BAD;
432 ed->peer_socket = ARES_SOCKET_BAD;
433
434 /* Likely a signal event, not something we will directly handle. We create
435 * the ares_evsys_win32_eventdata_t as the placeholder to use as the
436 * IOCP Completion Key */
437 if (ed->socket == ARES_SOCKET_BAD) {
438 event->data = ed;
439 return ARES_TRUE;
440 }
441
442 ed->base_socket = ares_evsys_win32_basesocket(ed->socket);
443 if (ed->base_socket == ARES_SOCKET_BAD) {
444 fprintf(stderr, "%s(): could not determine base socket for fd %d\n",
445 __FUNCTION__, (int)event->fd);
446 ares_evsys_win32_eventdata_destroy(ed);
447 return ARES_FALSE;
448 }
449
450 /* Create a peer socket that supports OVERLAPPED so we can use IOCP on the
451 * socket handle */
452 if (WSADuplicateSocketW(ed->base_socket, GetCurrentProcessId(),
453 &protocol_info) != 0) {
454 fprintf(stderr,
455 "%s(): could not retrieve protocol info for creating peer socket\n",
456 __FUNCTION__);
457 ares_evsys_win32_eventdata_destroy(ed);
458 return ARES_FALSE;
459 }
460
461 ed->peer_socket =
462 WSASocketW(protocol_info.iAddressFamily, protocol_info.iSocketType,
463 protocol_info.iProtocol, &protocol_info, 0, WSA_FLAG_OVERLAPPED);
464 if (ed->peer_socket == ARES_SOCKET_BAD) {
465 fprintf(stderr, "%s(): could not create peer socket\n", __FUNCTION__);
466 ares_evsys_win32_eventdata_destroy(ed);
467 return ARES_FALSE;
468 }
469
470 SetHandleInformation((HANDLE)ed->peer_socket, HANDLE_FLAG_INHERIT, 0);
471
472 if (CreateIoCompletionPort((HANDLE)ed->peer_socket, ew->iocp_handle,
473 (ULONG_PTR)ed, 0) == NULL) {
474 fprintf(stderr, "%s(): failed to bind peer socket to IOCP\n", __FUNCTION__);
475 ares_evsys_win32_eventdata_destroy(ed);
476 return ARES_FALSE;
477 }
478
479 event->data = ed;
480
481 if (!ares_evsys_win32_afd_enqueue(event, event->flags)) {
482 event->data = NULL;
483 ares_evsys_win32_eventdata_destroy(ed);
484 return ARES_FALSE;
485 }
486
487 return ARES_TRUE;
488 }
489
ares_evsys_win32_event_del(ares_event_t * event)490 static void ares_evsys_win32_event_del(ares_event_t *event)
491 {
492 ares_evsys_win32_eventdata_t *ed = event->data;
493 ares_event_thread_t *e = event->e;
494
495 if (event->fd == ARES_SOCKET_BAD || !e->isup || ed == NULL ||
496 !ares_evsys_win32_afd_cancel(ed)) {
497 /* Didn't need to enqueue a cancellation, for one of these reasons:
498 * - Not an IOCP socket
499 * - This is during shutdown of the event thread, no more signals can be
500 * delivered.
501 * - It has been determined there is no AFD POLL queued currently for the
502 * socket.
503 */
504 ares_evsys_win32_eventdata_destroy(ed);
505 event->data = NULL;
506 } else {
507 /* Detach from event, so when the cancel event comes through,
508 * it will clean up */
509 ed->event = NULL;
510 event->data = NULL;
511 }
512 }
513
ares_evsys_win32_event_mod(ares_event_t * event,ares_event_flags_t new_flags)514 static void ares_evsys_win32_event_mod(ares_event_t *event,
515 ares_event_flags_t new_flags)
516 {
517 ares_evsys_win32_eventdata_t *ed = event->data;
518
519 /* Not for us */
520 if (event->fd == ARES_SOCKET_BAD || ed == NULL) {
521 return;
522 }
523
524 /* Try to cancel any current outstanding poll, if one is not running,
525 * go ahead and queue it up */
526 if (!ares_evsys_win32_afd_cancel(ed)) {
527 ares_evsys_win32_afd_enqueue(event, new_flags);
528 }
529 }
530
ares_evsys_win32_wait(ares_event_thread_t * e,unsigned long timeout_ms)531 static size_t ares_evsys_win32_wait(ares_event_thread_t *e,
532 unsigned long timeout_ms)
533 {
534 ares_evsys_win32_t *ew = e->ev_sys_data;
535 OVERLAPPED_ENTRY entries[16];
536 ULONG nentries = sizeof(entries) / sizeof(*entries);
537 BOOL status;
538 size_t i;
539 size_t cnt = 0;
540
541 status = GetQueuedCompletionStatusEx(
542 ew->iocp_handle, entries, nentries, &nentries,
543 (timeout_ms == 0) ? INFINITE : (DWORD)timeout_ms, FALSE);
544
545 if (!status) {
546 return 0;
547 }
548
549 for (i = 0; i < (size_t)nentries; i++) {
550 ares_event_flags_t flags = 0;
551 ares_evsys_win32_eventdata_t *ed =
552 (ares_evsys_win32_eventdata_t *)entries[i].lpCompletionKey;
553 ares_event_t *event = ed->event;
554
555 if (ed->socket == ARES_SOCKET_BAD) {
556 /* Some sort of signal event */
557 flags = ARES_EVENT_FLAG_OTHER;
558 } else {
559 /* Process events */
560 if (ed->afd_poll_info.NumberOfHandles > 0) {
561 if (ed->afd_poll_info.Handles[0].Events &
562 (AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT |
563 AFD_POLL_ABORT)) {
564 flags |= ARES_EVENT_FLAG_READ;
565 }
566 if (ed->afd_poll_info.Handles[0].Events &
567 (AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL)) {
568 flags |= ARES_EVENT_FLAG_WRITE;
569 }
570
571 /* XXX: Handle ed->afd_poll_info.Handles[0].Events &
572 * AFD_POLL_LOCAL_CLOSE */
573 }
574
575 if (event == NULL) {
576 /* This means we need to cleanup the private event data as we've been
577 * detached */
578 ares_evsys_win32_eventdata_destroy(ed);
579 } else {
580 /* Re-enqueue so we can get more events on the socket */
581 ares_evsys_win32_afd_enqueue(event, event->flags);
582 }
583 }
584
585 if (event != NULL && flags != 0) {
586 cnt++;
587 event->cb(e, event->fd, event->data, flags);
588 }
589 }
590
591 return cnt;
592 }
593
594 const ares_event_sys_t ares_evsys_win32 = { "win32",
595 ares_evsys_win32_init,
596 ares_evsys_win32_destroy,
597 ares_evsys_win32_event_add,
598 ares_evsys_win32_event_del,
599 ares_evsys_win32_event_mod,
600 ares_evsys_win32_wait };
601 #endif
602