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 #include "ares_setup.h"
27 #include "ares.h"
28 #include "ares_private.h"
29 #include "ares_event.h"
30
ares_event_destroy_cb(void * arg)31 static void ares_event_destroy_cb(void *arg)
32 {
33 ares_event_t *event = arg;
34 if (event == NULL) {
35 return;
36 }
37
38 /* Unregister from the event thread if it was registered with one */
39 if (event->e) {
40 const ares_event_thread_t *e = event->e;
41 e->ev_sys->event_del(event);
42 event->e = NULL;
43 }
44
45 if (event->free_data_cb && event->data) {
46 event->free_data_cb(event->data);
47 }
48
49 ares_free(event);
50 }
51
52 /* See if a pending update already exists. We don't want to enqueue multiple
53 * updates for the same event handle. Right now this is O(n) based on number
54 * of updates already enqueued. In the future, it might make sense to make
55 * this O(1) with a hashtable.
56 * NOTE: in some cases a delete then re-add of the same fd, but really pointing
57 * to a different destination can happen due to a quick close of a
58 * connection then creation of a new one. So we need to look at the
59 * flags and ignore any delete events when finding a match since we
60 * need to process the delete always, it can't be combined with other
61 * updates. */
ares_event_update_find(ares_event_thread_t * e,ares_socket_t fd,const void * data)62 static ares_event_t *ares_event_update_find(ares_event_thread_t *e,
63 ares_socket_t fd, const void *data)
64 {
65 ares__llist_node_t *node;
66
67 for (node = ares__llist_node_first(e->ev_updates); node != NULL;
68 node = ares__llist_node_next(node)) {
69 ares_event_t *ev = ares__llist_node_val(node);
70
71 if (fd != ARES_SOCKET_BAD && fd == ev->fd && ev->flags != 0) {
72 return ev;
73 }
74
75 if (fd == ARES_SOCKET_BAD && ev->fd == ARES_SOCKET_BAD &&
76 data == ev->data && ev->flags != 0) {
77 return ev;
78 }
79 }
80
81 return NULL;
82 }
83
ares_event_update(ares_event_t ** event,ares_event_thread_t * e,ares_event_flags_t flags,ares_event_cb_t cb,ares_socket_t fd,void * data,ares_event_free_data_t free_data_cb,ares_event_signal_cb_t signal_cb)84 ares_status_t ares_event_update(ares_event_t **event, ares_event_thread_t *e,
85 ares_event_flags_t flags, ares_event_cb_t cb,
86 ares_socket_t fd, void *data,
87 ares_event_free_data_t free_data_cb,
88 ares_event_signal_cb_t signal_cb)
89 {
90 ares_event_t *ev = NULL;
91
92 if (e == NULL || cb == NULL) {
93 return ARES_EFORMERR;
94 }
95
96 if (event != NULL) {
97 *event = NULL;
98 }
99
100 /* Validate flags */
101 if (fd == ARES_SOCKET_BAD) {
102 if (flags & (ARES_EVENT_FLAG_READ | ARES_EVENT_FLAG_WRITE)) {
103 return ARES_EFORMERR;
104 }
105 if (!(flags & ARES_EVENT_FLAG_OTHER)) {
106 return ARES_EFORMERR;
107 }
108 } else {
109 if (flags & ARES_EVENT_FLAG_OTHER) {
110 return ARES_EFORMERR;
111 }
112 }
113
114 /* That's all the validation we can really do */
115
116 /* See if we have a queued update already */
117 ev = ares_event_update_find(e, fd, data);
118 if (ev == NULL) {
119 /* Allocate a new one */
120 ev = ares_malloc_zero(sizeof(*ev));
121 if (ev == NULL) {
122 return ARES_ENOMEM;
123 }
124
125 if (ares__llist_insert_last(e->ev_updates, ev) == NULL) {
126 ares_free(ev);
127 return ARES_ENOMEM;
128 }
129 }
130
131 ev->flags = flags;
132 ev->fd = fd;
133 if (ev->cb == NULL) {
134 ev->cb = cb;
135 }
136 if (ev->data == NULL) {
137 ev->data = data;
138 }
139 if (ev->free_data_cb == NULL) {
140 ev->free_data_cb = free_data_cb;
141 }
142 if (ev->signal_cb == NULL) {
143 ev->signal_cb = signal_cb;
144 }
145
146 if (event != NULL) {
147 *event = ev;
148 }
149
150 return ARES_SUCCESS;
151 }
152
ares_event_signal(const ares_event_t * event)153 static void ares_event_signal(const ares_event_t *event)
154 {
155 if (event == NULL || event->signal_cb == NULL) {
156 return;
157 }
158 event->signal_cb(event);
159 }
160
ares_event_thread_wake(const ares_event_thread_t * e)161 static void ares_event_thread_wake(const ares_event_thread_t *e)
162 {
163 if (e == NULL) {
164 return;
165 }
166
167 ares_event_signal(e->ev_signal);
168 }
169
ares_event_thread_process_fd(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)170 static void ares_event_thread_process_fd(ares_event_thread_t *e,
171 ares_socket_t fd, void *data,
172 ares_event_flags_t flags)
173 {
174 (void)data;
175
176 ares_process_fd(e->channel,
177 (flags & ARES_EVENT_FLAG_READ) ? fd : ARES_SOCKET_BAD,
178 (flags & ARES_EVENT_FLAG_WRITE) ? fd : ARES_SOCKET_BAD);
179 }
180
ares_event_thread_sockstate_cb(void * data,ares_socket_t socket_fd,int readable,int writable)181 static void ares_event_thread_sockstate_cb(void *data, ares_socket_t socket_fd,
182 int readable, int writable)
183 {
184 ares_event_thread_t *e = data;
185 ares_event_flags_t flags = ARES_EVENT_FLAG_NONE;
186
187 if (readable) {
188 flags |= ARES_EVENT_FLAG_READ;
189 }
190
191 if (writable) {
192 flags |= ARES_EVENT_FLAG_WRITE;
193 }
194
195 /* Update channel fd */
196 ares__thread_mutex_lock(e->mutex);
197 ares_event_update(NULL, e, flags, ares_event_thread_process_fd, socket_fd,
198 NULL, NULL, NULL);
199
200 /* Wake the event thread so it properly enqueues any updates */
201 ares_event_thread_wake(e);
202
203 ares__thread_mutex_unlock(e->mutex);
204 }
205
ares_event_process_updates(ares_event_thread_t * e)206 static void ares_event_process_updates(ares_event_thread_t *e)
207 {
208 ares__llist_node_t *node;
209
210 /* Iterate across all updates and apply to internal list, removing from update
211 * list */
212 while ((node = ares__llist_node_first(e->ev_updates)) != NULL) {
213 ares_event_t *newev = ares__llist_node_claim(node);
214 ares_event_t *oldev =
215 ares__htable_asvp_get_direct(e->ev_handles, newev->fd);
216
217 /* Adding new */
218 if (oldev == NULL) {
219 newev->e = e;
220 /* Don't try to add a new event if all flags are cleared, that's basically
221 * someone trying to delete something already deleted. Also if it fails
222 * to add, cleanup. */
223 if (newev->flags == ARES_EVENT_FLAG_NONE ||
224 !e->ev_sys->event_add(newev)) {
225 newev->e = NULL;
226 ares_event_destroy_cb(newev);
227 } else {
228 ares__htable_asvp_insert(e->ev_handles, newev->fd, newev);
229 }
230 continue;
231 }
232
233 /* Removal request */
234 if (newev->flags == ARES_EVENT_FLAG_NONE) {
235 /* the callback for the removal will call e->ev_sys->event_del(e, event)
236 */
237 ares__htable_asvp_remove(e->ev_handles, newev->fd);
238 ares_free(newev);
239 continue;
240 }
241
242 /* Modify request -- only flags can be changed */
243 e->ev_sys->event_mod(oldev, newev->flags);
244 oldev->flags = newev->flags;
245 ares_free(newev);
246 }
247 }
248
ares_event_thread(void * arg)249 static void *ares_event_thread(void *arg)
250 {
251 ares_event_thread_t *e = arg;
252 ares__thread_mutex_lock(e->mutex);
253
254 while (e->isup) {
255 struct timeval tv;
256 const struct timeval *tvout;
257 unsigned long timeout_ms = 0; /* 0 = unlimited */
258
259 tvout = ares_timeout(e->channel, NULL, &tv);
260 if (tvout != NULL) {
261 timeout_ms =
262 (unsigned long)((tvout->tv_sec * 1000) + (tvout->tv_usec / 1000) + 1);
263 }
264
265 ares_event_process_updates(e);
266
267 /* Don't hold a mutex while waiting on events */
268 ares__thread_mutex_unlock(e->mutex);
269 e->ev_sys->wait(e, timeout_ms);
270
271 /* Each iteration should do timeout processing */
272 if (e->isup) {
273 ares_process_fd(e->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
274 }
275
276 /* Relock before we loop again */
277 ares__thread_mutex_lock(e->mutex);
278 }
279
280 ares__thread_mutex_unlock(e->mutex);
281 return NULL;
282 }
283
ares_event_thread_destroy_int(ares_event_thread_t * e)284 static void ares_event_thread_destroy_int(ares_event_thread_t *e)
285 {
286 ares__llist_node_t *node;
287
288 /* Wake thread and tell it to shutdown if it exists */
289 ares__thread_mutex_lock(e->mutex);
290 if (e->isup) {
291 e->isup = ARES_FALSE;
292 ares_event_thread_wake(e);
293 }
294 ares__thread_mutex_unlock(e->mutex);
295
296 /* Wait for thread to shutdown */
297 if (e->thread) {
298 ares__thread_join(e->thread, NULL);
299 e->thread = NULL;
300 }
301
302 /* Manually free any updates that weren't processed */
303 while ((node = ares__llist_node_first(e->ev_updates)) != NULL) {
304 ares_event_destroy_cb(ares__llist_node_claim(node));
305 }
306 ares__llist_destroy(e->ev_updates);
307 e->ev_updates = NULL;
308
309 ares__htable_asvp_destroy(e->ev_handles);
310 e->ev_handles = NULL;
311
312 if (e->ev_sys->destroy) {
313 e->ev_sys->destroy(e);
314 }
315
316 ares__thread_mutex_destroy(e->mutex);
317 e->mutex = NULL;
318
319 ares_free(e);
320 }
321
ares_event_thread_destroy(ares_channel_t * channel)322 void ares_event_thread_destroy(ares_channel_t *channel)
323 {
324 ares_event_thread_t *e = channel->sock_state_cb_data;
325
326 if (e == NULL) {
327 return;
328 }
329
330 ares_event_thread_destroy_int(e);
331 }
332
ares_event_fetch_sys(ares_evsys_t evsys)333 static const ares_event_sys_t *ares_event_fetch_sys(ares_evsys_t evsys)
334 {
335 switch (evsys) {
336 case ARES_EVSYS_WIN32:
337 #if defined(_WIN32)
338 return &ares_evsys_win32;
339 #else
340 return NULL;
341 #endif
342
343 case ARES_EVSYS_EPOLL:
344 #if defined(HAVE_EPOLL)
345 return &ares_evsys_epoll;
346 #else
347 return NULL;
348 #endif
349
350 case ARES_EVSYS_KQUEUE:
351 #if defined(HAVE_KQUEUE)
352 return &ares_evsys_kqueue;
353 #else
354 return NULL;
355 #endif
356
357 case ARES_EVSYS_POLL:
358 #if defined(HAVE_POLL)
359 return &ares_evsys_poll;
360 #else
361 return NULL;
362 #endif
363
364 case ARES_EVSYS_SELECT:
365 #if defined(HAVE_PIPE)
366 return &ares_evsys_select;
367 #else
368 return NULL;
369 #endif
370
371 /* case ARES_EVSYS_DEFAULT: */
372 default:
373 #if defined(_WIN32)
374 return &ares_evsys_win32;
375 #elif defined(HAVE_KQUEUE)
376 return &ares_evsys_kqueue;
377 #elif defined(HAVE_EPOLL)
378 return &ares_evsys_epoll;
379 #elif defined(HAVE_POLL)
380 return &ares_evsys_poll;
381 #elif defined(HAVE_PIPE)
382 return &ares_evsys_select;
383 #else
384 break;
385 #endif
386 }
387
388 return NULL;
389 }
390
ares_event_thread_init(ares_channel_t * channel)391 ares_status_t ares_event_thread_init(ares_channel_t *channel)
392 {
393 ares_event_thread_t *e;
394
395 e = ares_malloc_zero(sizeof(*e));
396 if (e == NULL) {
397 return ARES_ENOMEM;
398 }
399
400 e->mutex = ares__thread_mutex_create();
401 if (e->mutex == NULL) {
402 ares_event_thread_destroy_int(e);
403 return ARES_ENOMEM;
404 }
405
406 e->ev_updates = ares__llist_create(NULL);
407 if (e->ev_updates == NULL) {
408 ares_event_thread_destroy_int(e);
409 return ARES_ENOMEM;
410 }
411
412 e->ev_handles = ares__htable_asvp_create(ares_event_destroy_cb);
413 if (e->ev_handles == NULL) {
414 ares_event_thread_destroy_int(e);
415 return ARES_ENOMEM;
416 }
417
418 e->channel = channel;
419 e->isup = ARES_TRUE;
420 e->ev_sys = ares_event_fetch_sys(channel->evsys);
421 if (e->ev_sys == NULL) {
422 ares_event_thread_destroy_int(e);
423 return ARES_ENOTIMP;
424 }
425
426 channel->sock_state_cb = ares_event_thread_sockstate_cb;
427 channel->sock_state_cb_data = e;
428
429 if (!e->ev_sys->init(e)) {
430 ares_event_thread_destroy_int(e);
431 channel->sock_state_cb = NULL;
432 channel->sock_state_cb_data = NULL;
433 return ARES_ESERVFAIL;
434 }
435
436 /* Before starting the thread, process any possible events the initialization
437 * might have enqueued as we may actually depend on these being valid
438 * immediately upon return, which may mean before the thread is fully spawned
439 * and processed the list itself. We don't want any sort of race conditions
440 * (like the event system wake handle itself). */
441 ares_event_process_updates(e);
442
443 /* Start thread */
444 if (ares__thread_create(&e->thread, ares_event_thread, e) != ARES_SUCCESS) {
445 ares_event_thread_destroy_int(e);
446 channel->sock_state_cb = NULL;
447 channel->sock_state_cb_data = NULL;
448 return ARES_ESERVFAIL;
449 }
450
451 return ARES_SUCCESS;
452 }
453