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_private.h"
27 #include "ares_event.h"
28
29 #if defined(__ANDROID__) && defined(CARES_THREADS)
30
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)31 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
32 ares_event_thread_t *e)
33 {
34 (void)configchg;
35 (void)e;
36 /* No ability */
37 return ARES_ENOTIMP;
38 }
39
ares_event_configchg_destroy(ares_event_configchg_t * configchg)40 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
41 {
42 /* No-op */
43 (void)configchg;
44 }
45
46 #elif defined(__linux__) && defined(CARES_THREADS)
47
48 # include <sys/inotify.h>
49
50 struct ares_event_configchg {
51 int inotify_fd;
52 ares_event_thread_t *e;
53 };
54
ares_event_configchg_destroy(ares_event_configchg_t * configchg)55 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
56 {
57 if (configchg == NULL) {
58 return; /* LCOV_EXCL_LINE: DefensiveCoding */
59 }
60
61 /* Tell event system to stop monitoring for changes. This will cause the
62 * cleanup to be called */
63 ares_event_update(NULL, configchg->e, ARES_EVENT_FLAG_NONE, NULL,
64 configchg->inotify_fd, NULL, NULL, NULL);
65 }
66
ares_event_configchg_free(void * data)67 static void ares_event_configchg_free(void *data)
68 {
69 ares_event_configchg_t *configchg = data;
70 if (configchg == NULL) {
71 return; /* LCOV_EXCL_LINE: DefensiveCoding */
72 }
73
74 if (configchg->inotify_fd >= 0) {
75 close(configchg->inotify_fd);
76 configchg->inotify_fd = -1;
77 }
78
79 ares_free(configchg);
80 }
81
ares_event_configchg_cb(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)82 static void ares_event_configchg_cb(ares_event_thread_t *e, ares_socket_t fd,
83 void *data, ares_event_flags_t flags)
84 {
85 const ares_event_configchg_t *configchg = data;
86
87 /* Some systems cannot read integer variables if they are not
88 * properly aligned. On other systems, incorrect alignment may
89 * decrease performance. Hence, the buffer used for reading from
90 * the inotify file descriptor should have the same alignment as
91 * struct inotify_event. */
92 unsigned char buf[4096]
93 __attribute__((aligned(__alignof__(struct inotify_event))));
94 const struct inotify_event *event;
95 ssize_t len;
96 ares_bool_t triggered = ARES_FALSE;
97
98 (void)fd;
99 (void)flags;
100
101 while (1) {
102 const unsigned char *ptr;
103
104 len = read(configchg->inotify_fd, buf, sizeof(buf));
105 if (len <= 0) {
106 break;
107 }
108
109 /* Loop over all events in the buffer. Says kernel will check the buffer
110 * size provided, so I assume it won't ever return partial events. */
111 for (ptr = buf; ptr < buf + len;
112 ptr += sizeof(struct inotify_event) + event->len) {
113 event = (const struct inotify_event *)((const void *)ptr);
114
115 if (event->len == 0 || ares_strlen(event->name) == 0) {
116 continue;
117 }
118
119 if (ares_strcaseeq(event->name, "resolv.conf") ||
120 ares_strcaseeq(event->name, "nsswitch.conf")) {
121 triggered = ARES_TRUE;
122 }
123 }
124 }
125
126 /* Only process after all events are read. No need to process more often as
127 * we don't want to reload the config back to back */
128 if (triggered) {
129 ares_reinit(e->channel);
130 }
131 }
132
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)133 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
134 ares_event_thread_t *e)
135 {
136 ares_status_t status = ARES_SUCCESS;
137 ares_event_configchg_t *c;
138
139 (void)e;
140
141 /* Not used by this implementation */
142 *configchg = NULL;
143
144 c = ares_malloc_zero(sizeof(*c));
145 if (c == NULL) {
146 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
147 }
148
149 c->e = e;
150 c->inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
151 if (c->inotify_fd == -1) {
152 status = ARES_ESERVFAIL; /* LCOV_EXCL_LINE: UntestablePath */
153 goto done; /* LCOV_EXCL_LINE: UntestablePath */
154 }
155
156 /* We need to monitor /etc/resolv.conf, /etc/nsswitch.conf */
157 if (inotify_add_watch(c->inotify_fd, "/etc",
158 IN_CREATE | IN_MODIFY | IN_MOVED_TO | IN_ONLYDIR) ==
159 -1) {
160 status = ARES_ESERVFAIL; /* LCOV_EXCL_LINE: UntestablePath */
161 goto done; /* LCOV_EXCL_LINE: UntestablePath */
162 }
163
164 status =
165 ares_event_update(NULL, e, ARES_EVENT_FLAG_READ, ares_event_configchg_cb,
166 c->inotify_fd, c, ares_event_configchg_free, NULL);
167
168 done:
169 if (status != ARES_SUCCESS) {
170 ares_event_configchg_free(c);
171 } else {
172 *configchg = c;
173 }
174 return status;
175 }
176
177 #elif defined(USE_WINSOCK) && defined(CARES_THREADS)
178
179 # include <winsock2.h>
180 # include <iphlpapi.h>
181 # include <stdio.h>
182 # include <windows.h>
183
184 struct ares_event_configchg {
185 HANDLE ifchg_hnd;
186 HKEY regip4;
187 HANDLE regip4_event;
188 HANDLE regip4_wait;
189 HKEY regip6;
190 HANDLE regip6_event;
191 HANDLE regip6_wait;
192 ares_event_thread_t *e;
193 };
194
ares_event_configchg_destroy(ares_event_configchg_t * configchg)195 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
196 {
197 if (configchg == NULL) {
198 return;
199 }
200
201 # ifdef HAVE_NOTIFYIPINTERFACECHANGE
202 if (configchg->ifchg_hnd != NULL) {
203 CancelMibChangeNotify2(configchg->ifchg_hnd);
204 configchg->ifchg_hnd = NULL;
205 }
206 # endif
207
208 # ifdef HAVE_REGISTERWAITFORSINGLEOBJECT
209 if (configchg->regip4_wait != NULL) {
210 UnregisterWait(configchg->regip4_wait);
211 configchg->regip4_wait = NULL;
212 }
213
214 if (configchg->regip6_wait != NULL) {
215 UnregisterWait(configchg->regip6_wait);
216 configchg->regip6_wait = NULL;
217 }
218
219 if (configchg->regip4 != NULL) {
220 RegCloseKey(configchg->regip4);
221 configchg->regip4 = NULL;
222 }
223
224 if (configchg->regip6 != NULL) {
225 RegCloseKey(configchg->regip6);
226 configchg->regip6 = NULL;
227 }
228
229 if (configchg->regip4_event != NULL) {
230 CloseHandle(configchg->regip4_event);
231 configchg->regip4_event = NULL;
232 }
233
234 if (configchg->regip6_event != NULL) {
235 CloseHandle(configchg->regip6_event);
236 configchg->regip6_event = NULL;
237 }
238 # endif
239
240 ares_free(configchg);
241 }
242
243
244 # ifdef HAVE_NOTIFYIPINTERFACECHANGE
245 static void NETIOAPI_API_
ares_event_configchg_ip_cb(PVOID CallerContext,PMIB_IPINTERFACE_ROW Row,MIB_NOTIFICATION_TYPE NotificationType)246 ares_event_configchg_ip_cb(PVOID CallerContext, PMIB_IPINTERFACE_ROW Row,
247 MIB_NOTIFICATION_TYPE NotificationType)
248 {
249 ares_event_configchg_t *configchg = CallerContext;
250 (void)Row;
251 (void)NotificationType;
252 ares_reinit(configchg->e->channel);
253 }
254 # endif
255
256 static ares_bool_t
ares_event_configchg_regnotify(ares_event_configchg_t * configchg)257 ares_event_configchg_regnotify(ares_event_configchg_t *configchg)
258 {
259 # ifdef HAVE_REGISTERWAITFORSINGLEOBJECT
260 # if defined(__WATCOMC__) && !defined(REG_NOTIFY_THREAD_AGNOSTIC)
261 # define REG_NOTIFY_THREAD_AGNOSTIC 0x10000000L
262 # endif
263 DWORD flags = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET |
264 REG_NOTIFY_THREAD_AGNOSTIC;
265
266 if (RegNotifyChangeKeyValue(configchg->regip4, TRUE, flags,
267 configchg->regip4_event, TRUE) != ERROR_SUCCESS) {
268 return ARES_FALSE;
269 }
270
271 if (RegNotifyChangeKeyValue(configchg->regip6, TRUE, flags,
272 configchg->regip6_event, TRUE) != ERROR_SUCCESS) {
273 return ARES_FALSE;
274 }
275 # else
276 (void)configchg;
277 # endif
278 return ARES_TRUE;
279 }
280
ares_event_configchg_reg_cb(PVOID lpParameter,BOOLEAN TimerOrWaitFired)281 static VOID CALLBACK ares_event_configchg_reg_cb(PVOID lpParameter,
282 BOOLEAN TimerOrWaitFired)
283 {
284 ares_event_configchg_t *configchg = lpParameter;
285 (void)TimerOrWaitFired;
286
287 ares_reinit(configchg->e->channel);
288
289 /* Re-arm, as its single-shot. However, we don't know which one needs to
290 * be re-armed, so we just do both */
291 ares_event_configchg_regnotify(configchg);
292 }
293
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)294 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
295 ares_event_thread_t *e)
296 {
297 ares_status_t status = ARES_SUCCESS;
298 ares_event_configchg_t *c = NULL;
299
300 c = ares_malloc_zero(sizeof(**configchg));
301 if (c == NULL) {
302 return ARES_ENOMEM;
303 }
304
305 c->e = e;
306
307 # ifdef HAVE_NOTIFYIPINTERFACECHANGE
308 /* NOTE: If a user goes into the control panel and changes the network
309 * adapter DNS addresses manually, this will NOT trigger a notification.
310 * We've also tried listening on NotifyUnicastIpAddressChange(), but
311 * that didn't get triggered either.
312 */
313 if (NotifyIpInterfaceChange(AF_UNSPEC, ares_event_configchg_ip_cb, c, FALSE,
314 &c->ifchg_hnd) != NO_ERROR) {
315 status = ARES_ESERVFAIL;
316 goto done;
317 }
318 # endif
319
320 # ifdef HAVE_REGISTERWAITFORSINGLEOBJECT
321 /* Monitor HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces
322 * and HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
323 * for changes via RegNotifyChangeKeyValue() */
324 if (RegOpenKeyExW(
325 HKEY_LOCAL_MACHINE,
326 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces",
327 0, KEY_NOTIFY, &c->regip4) != ERROR_SUCCESS) {
328 status = ARES_ESERVFAIL;
329 goto done;
330 }
331
332 if (RegOpenKeyExW(
333 HKEY_LOCAL_MACHINE,
334 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces",
335 0, KEY_NOTIFY, &c->regip6) != ERROR_SUCCESS) {
336 status = ARES_ESERVFAIL;
337 goto done;
338 }
339
340 c->regip4_event = CreateEvent(NULL, TRUE, FALSE, NULL);
341 if (c->regip4_event == NULL) {
342 status = ARES_ESERVFAIL;
343 goto done;
344 }
345
346 c->regip6_event = CreateEvent(NULL, TRUE, FALSE, NULL);
347 if (c->regip6_event == NULL) {
348 status = ARES_ESERVFAIL;
349 goto done;
350 }
351
352 if (!RegisterWaitForSingleObject(&c->regip4_wait, c->regip4_event,
353 ares_event_configchg_reg_cb, c, INFINITE,
354 WT_EXECUTEDEFAULT)) {
355 status = ARES_ESERVFAIL;
356 goto done;
357 }
358
359 if (!RegisterWaitForSingleObject(&c->regip6_wait, c->regip6_event,
360 ares_event_configchg_reg_cb, c, INFINITE,
361 WT_EXECUTEDEFAULT)) {
362 status = ARES_ESERVFAIL;
363 goto done;
364 }
365 # endif
366
367 if (!ares_event_configchg_regnotify(c)) {
368 status = ARES_ESERVFAIL;
369 goto done;
370 }
371
372 done:
373 if (status != ARES_SUCCESS) {
374 ares_event_configchg_destroy(c);
375 } else {
376 *configchg = c;
377 }
378
379 return status;
380 }
381
382 #elif defined(__APPLE__) && defined(CARES_THREADS)
383
384 # include <sys/types.h>
385 # include <unistd.h>
386 # include <notify.h>
387 # include <dlfcn.h>
388 # include <fcntl.h>
389
390 struct ares_event_configchg {
391 int fd;
392 int token;
393 };
394
ares_event_configchg_destroy(ares_event_configchg_t * configchg)395 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
396 {
397 (void)configchg;
398
399 /* Cleanup happens automatically */
400 }
401
ares_event_configchg_free(void * data)402 static void ares_event_configchg_free(void *data)
403 {
404 ares_event_configchg_t *configchg = data;
405 if (configchg == NULL) {
406 return;
407 }
408
409 if (configchg->fd >= 0) {
410 notify_cancel(configchg->token);
411 /* automatically closes fd */
412 configchg->fd = -1;
413 }
414
415 ares_free(configchg);
416 }
417
ares_event_configchg_cb(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)418 static void ares_event_configchg_cb(ares_event_thread_t *e, ares_socket_t fd,
419 void *data, ares_event_flags_t flags)
420 {
421 ares_event_configchg_t *configchg = data;
422 ares_bool_t triggered = ARES_FALSE;
423
424 (void)fd;
425 (void)flags;
426
427 while (1) {
428 int t = 0;
429 ssize_t len;
430
431 len = read(configchg->fd, &t, sizeof(t));
432
433 if (len < (ssize_t)sizeof(t)) {
434 break;
435 }
436
437 /* Token is read in network byte order (yeah, docs don't mention this) */
438 t = (int)ntohl(t);
439
440 if (t != configchg->token) {
441 continue;
442 }
443
444 triggered = ARES_TRUE;
445 }
446
447 /* Only process after all events are read. No need to process more often as
448 * we don't want to reload the config back to back */
449 if (triggered) {
450 ares_reinit(e->channel);
451 }
452 }
453
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)454 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
455 ares_event_thread_t *e)
456 {
457 ares_status_t status = ARES_SUCCESS;
458 void *handle = NULL;
459 const char *(*pdns_configuration_notify_key)(void) = NULL;
460 const char *notify_key = NULL;
461 int flags;
462 size_t i;
463 const char *searchlibs[] = {
464 "/usr/lib/libSystem.dylib",
465 "/System/Library/Frameworks/SystemConfiguration.framework/"
466 "SystemConfiguration",
467 NULL
468 };
469
470 *configchg = ares_malloc_zero(sizeof(**configchg));
471 if (*configchg == NULL) {
472 return ARES_ENOMEM;
473 }
474
475 /* Load symbol as it isn't normally public */
476 for (i = 0; searchlibs[i] != NULL; i++) {
477 handle = dlopen(searchlibs[i], RTLD_LAZY);
478 if (handle == NULL) {
479 /* Fail, loop! */
480 continue;
481 }
482
483 pdns_configuration_notify_key =
484 (const char *(*)(void))dlsym(handle, "dns_configuration_notify_key");
485 if (pdns_configuration_notify_key != NULL) {
486 break;
487 }
488
489 /* Fail, loop! */
490 dlclose(handle);
491 handle = NULL;
492 }
493
494 if (pdns_configuration_notify_key == NULL) {
495 status = ARES_ESERVFAIL;
496 goto done;
497 }
498
499 notify_key = pdns_configuration_notify_key();
500 if (notify_key == NULL) {
501 status = ARES_ESERVFAIL;
502 goto done;
503 }
504
505 if (notify_register_file_descriptor(notify_key, &(*configchg)->fd, 0,
506 &(*configchg)->token) !=
507 NOTIFY_STATUS_OK) {
508 status = ARES_ESERVFAIL;
509 goto done;
510 }
511
512 /* Set file descriptor to non-blocking */
513 flags = fcntl((*configchg)->fd, F_GETFL, 0);
514 fcntl((*configchg)->fd, F_SETFL, flags | O_NONBLOCK);
515
516 /* Register file descriptor with event subsystem */
517 status = ares_event_update(NULL, e, ARES_EVENT_FLAG_READ,
518 ares_event_configchg_cb, (*configchg)->fd,
519 *configchg, ares_event_configchg_free, NULL);
520
521 done:
522 if (status != ARES_SUCCESS) {
523 ares_event_configchg_free(*configchg);
524 *configchg = NULL;
525 }
526
527 if (handle) {
528 dlclose(handle);
529 }
530
531 return status;
532 }
533
534 #elif defined(HAVE_STAT) && !defined(_WIN32) && defined(CARES_THREADS)
535 # ifdef HAVE_SYS_TYPES_H
536 # include <sys/types.h>
537 # endif
538 # ifdef HAVE_SYS_STAT_H
539 # include <sys/stat.h>
540 # endif
541
542 typedef struct {
543 size_t size;
544 time_t mtime;
545 } fileinfo_t;
546
547 struct ares_event_configchg {
548 ares_bool_t isup;
549 ares_thread_t *thread;
550 ares_htable_strvp_t *filestat;
551 ares_thread_mutex_t *lock;
552 ares_thread_cond_t *wake;
553 const char *resolvconf_path;
554 ares_event_thread_t *e;
555 };
556
config_change_check(ares_htable_strvp_t * filestat,const char * resolvconf_path)557 static ares_status_t config_change_check(ares_htable_strvp_t *filestat,
558 const char *resolvconf_path)
559 {
560 size_t i;
561 const char *configfiles[16];
562 ares_bool_t changed = ARES_FALSE;
563 size_t cnt = 0;
564
565 memset(configfiles, 0, sizeof(configfiles));
566
567 configfiles[cnt++] = resolvconf_path;
568 configfiles[cnt++] = "/etc/nsswitch.conf";
569 #ifdef _AIX
570 configfiles[cnt++] = "/etc/netsvc.conf";
571 #endif
572 #ifdef __osf /* Tru64 */
573 configfiles[cnt++] = "/etc/svc.conf";
574 #endif
575 #ifdef __QNX__
576 configfiles[cnt++] = "/etc/net.cfg";
577 #endif
578 configfiles[cnt++] = NULL;
579
580 for (i = 0; configfiles[i] != NULL; i++) {
581 fileinfo_t *fi = ares_htable_strvp_get_direct(filestat, configfiles[i]);
582 struct stat st;
583
584 if (stat(configfiles[i], &st) == 0) {
585 if (fi == NULL) {
586 fi = ares_malloc_zero(sizeof(*fi));
587 if (fi == NULL) {
588 return ARES_ENOMEM;
589 }
590 if (!ares_htable_strvp_insert(filestat, configfiles[i], fi)) {
591 ares_free(fi);
592 return ARES_ENOMEM;
593 }
594 }
595 if (fi->size != (size_t)st.st_size || fi->mtime != (time_t)st.st_mtime) {
596 changed = ARES_TRUE;
597 }
598 fi->size = (size_t)st.st_size;
599 fi->mtime = (time_t)st.st_mtime;
600 } else if (fi != NULL) {
601 /* File no longer exists, remove */
602 ares_htable_strvp_remove(filestat, configfiles[i]);
603 changed = ARES_TRUE;
604 }
605 }
606
607 if (changed) {
608 return ARES_SUCCESS;
609 }
610 return ARES_ENOTFOUND;
611 }
612
ares_event_configchg_thread(void * arg)613 static void *ares_event_configchg_thread(void *arg)
614 {
615 ares_event_configchg_t *c = arg;
616
617 ares_thread_mutex_lock(c->lock);
618 while (c->isup) {
619 ares_status_t status;
620
621 if (ares_thread_cond_timedwait(c->wake, c->lock, 30000) != ARES_ETIMEOUT) {
622 continue;
623 }
624
625 /* make sure status didn't change even though we got a timeout */
626 if (!c->isup) {
627 break;
628 }
629
630 status = config_change_check(c->filestat, c->resolvconf_path);
631 if (status == ARES_SUCCESS) {
632 ares_reinit(c->e->channel);
633 }
634 }
635
636 ares_thread_mutex_unlock(c->lock);
637 return NULL;
638 }
639
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)640 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
641 ares_event_thread_t *e)
642 {
643 ares_status_t status = ARES_SUCCESS;
644 ares_event_configchg_t *c = NULL;
645
646 *configchg = NULL;
647
648 c = ares_malloc_zero(sizeof(*c));
649 if (c == NULL) {
650 status = ARES_ENOMEM;
651 goto done;
652 }
653
654 c->e = e;
655
656 c->filestat = ares_htable_strvp_create(ares_free);
657 if (c->filestat == NULL) {
658 status = ARES_ENOMEM;
659 goto done;
660 }
661
662 c->wake = ares_thread_cond_create();
663 if (c->wake == NULL) {
664 status = ARES_ENOMEM;
665 goto done;
666 }
667
668 c->lock = ares_thread_mutex_create();
669 if (c->lock == NULL) {
670 status = ARES_ENOMEM;
671 goto done;
672 }
673
674 c->resolvconf_path = c->e->channel->resolvconf_path;
675 if (c->resolvconf_path == NULL) {
676 c->resolvconf_path = PATH_RESOLV_CONF;
677 }
678
679 status = config_change_check(c->filestat, c->resolvconf_path);
680 if (status == ARES_ENOMEM) {
681 goto done;
682 }
683
684 c->isup = ARES_TRUE;
685 status = ares_thread_create(&c->thread, ares_event_configchg_thread, c);
686
687 done:
688 if (status != ARES_SUCCESS) {
689 ares_event_configchg_destroy(c);
690 } else {
691 *configchg = c;
692 }
693 return status;
694 }
695
ares_event_configchg_destroy(ares_event_configchg_t * configchg)696 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
697 {
698 if (configchg == NULL) {
699 return;
700 }
701
702 if (configchg->lock) {
703 ares_thread_mutex_lock(configchg->lock);
704 }
705
706 configchg->isup = ARES_FALSE;
707 if (configchg->wake) {
708 ares_thread_cond_signal(configchg->wake);
709 }
710
711 if (configchg->lock) {
712 ares_thread_mutex_unlock(configchg->lock);
713 }
714
715 if (configchg->thread) {
716 void *rv = NULL;
717 ares_thread_join(configchg->thread, &rv);
718 }
719
720 ares_thread_mutex_destroy(configchg->lock);
721 ares_thread_cond_destroy(configchg->wake);
722 ares_htable_strvp_destroy(configchg->filestat);
723 ares_free(configchg);
724 }
725
726 #else
727
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)728 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
729 ares_event_thread_t *e)
730 {
731 (void)configchg;
732 (void)e;
733 /* No ability */
734 return ARES_ENOTIMP;
735 }
736
ares_event_configchg_destroy(ares_event_configchg_t * configchg)737 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
738 {
739 /* No-op */
740 (void)configchg;
741 }
742
743 #endif
744