diff --git a/src/lib/ares_event.h b/src/lib/ares_event.h index add717c0e3..57968d96a3 100644 --- a/src/lib/ares_event.h +++ b/src/lib/ares_event.h @@ -79,7 +79,12 @@ typedef struct { size_t (*wait)(ares_event_thread_t *e, unsigned long timeout_ms); } ares_event_sys_t; -ares_status_t ares_event_configchg_init(ares_event_thread_t *e); +struct ares_event_configchg; +typedef struct ares_event_configchg ares_event_configchg_t; + +ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg, ares_event_thread_t *e); + +void ares_event_configchg_destroy(ares_event_configchg_t *configchg); struct ares_event_thread { /*! Whether the event thread should be online or not. Checked on every wake @@ -105,6 +110,8 @@ struct ares_event_thread { * file descriptors being waited on and to wake the event subsystem during * shutdown */ ares_event_t *ev_signal; + /*! Handle for configuration change monitoring */ + ares_event_configchg_t *configchg; /* Event subsystem callbacks */ const ares_event_sys_t *ev_sys; /* Event subsystem private data */ diff --git a/src/lib/ares_event_configchg.c b/src/lib/ares_event_configchg.c index 6ad23cf050..dc403ad050 100644 --- a/src/lib/ares_event_configchg.c +++ b/src/lib/ares_event_configchg.c @@ -37,9 +37,17 @@ static void ares_event_configchg_reload(ares_event_thread_t *e) # include -typedef struct { +struct ares_event_configchg { int inotify_fd; -} ares_event_configchg_t; +}; + +void ares_event_configchg_destroy(ares_event_configchg_t *configchg) +{ + (void)configchg; + + /* Cleanup happens automatically */ +} + static void ares_event_configchg_free(void *data) { @@ -141,18 +149,157 @@ ares_status_t ares_event_configchg_init(ares_event_thread_t *e) #elif defined(_WIN32) +# include +# include +# include +# include + +struct ares_event_configchg { + ares__thread_t *thread; + HANDLE terminate_event; + HANDLE ifchg_hnd; + OVERLAPPED ifchg_ol; + HANDLE route_hnd; + OVERLAPPED route_ol; +}; + +void ares_event_configchg_destroy(ares_event_configchg_t *configchg) +{ + if (configchg->terminate_event) { + SetEvent(configchg->terminate_event); + } + + if (configchg->thread) { + void *rv = NULL; + ares__thread_join(thread, &rv); + configchg->thread = NULL; + } + + + if (configchg->terminate_event != NULL) { + CloseHandle(configchg->terminate_event); + configchg->terminate_event = NULL; + } + + if (configchg->ifchg_hnd != NULL) { + CancelIPChangeNotify(configchg->ifchg_hnd); + configchg->ifchg_hnd = NULL; + } + + if (configchg->ifchg_ol.hEvent != NULL) { + DeleteEvent(configchg->ifchg_ol.hEvent); + configchg->ifchg_ol.hEvent = NULL; + } + if (configchg->route_hnd != NULL) { + CancelIPChangeNotify(configchg->route_hnd); + configchg->route_hnd = NULL; + } + + if (configchg->route_ol.hEvent != NULL) { + DeleteEvent(configchg->route_ol.hEvent); + configchg->route_ol.hEvent = NULL; + } + + ares_free(configchg); +} + +static void *ares_event_configchg_thread(void *arg) +{ + ares_event_configchg_t *configchg = arg; + HANDLE handles[3] = { configchg->terminate_event, configchg->ifchg_ol.hEvent, configchg->route_ol.hEvent }; + + while (1) { + ares_bool_t triggered = ARES_FALSE; + DWORD ret = WaitForMultipleObjects(3, handles, FALSE, INFINITE); + if (ret < WAIT_OBJECT_0) { + continue; + } + + /* Query each handle individually */ + if (WaitForSingleObject(configchg->terminate_event, 0) == WAIT_OBJECT_0) { + break; + } + + if (WaitForSingleObject(configchg->ifchg_ol.hEvent, 0) == WAIT_OBJECT_O) { + triggered = ARES_TRUE; + ResetEvent(configchg->ifchg_ol.hEvent); + } + + if (WaitForSingleObject(configchg->route_ol.hEvent, 0) == WAIT_OBJECT_O) { + triggered = ARES_TRUE; + ResetEvent(configchg->route_ol.hEvent); + } + + if (triggered) { + ares_event_configchg_reload(configchg->e); + } + } + + return NULL; +} + +ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg, ares_event_thread_t *e) +{ + ares_status_t status = ARES_SUCCESS; + + *configchg = ares_malloc_zero(sizeof(**configchg)); + if (*configchg == NULL) { + return ARES_ENOMEM; + } + + (*configchg)->e = e; + + (*configchg)->terminate_event = CreateEvent(); + if ((*configchg)->terminate_event == NULL) { + status = ARES_ESERVFAIL; + goto done; + } + + if (NotifyRouteChange(&(*configchg)->route_hnd, &(*configchg)->route_ol) != NO_ERROR) { + status = ARES_ESERVFAIL; + goto done; + } + + if (NotifyIpInterfaceChange(&(*configchg)->ifchg_hnd, &(*configchg)->ifchg_ol) != NO_ERROR) { + status = ARES_ESERVFAIL; + goto done; + } + + status = ares__thread_create(&(*configchg)->thread, ares_event_configchg_thread, *configchg); + if (status != ARES_SUCCESS) { + goto done; + } + +done: + if (status != ARES_SUCCESS) { + ares_event_configchg_destroy(*configchg); + *configchg = NULL; + } + + return status; +} #elif defined(__APPLE__) # include # include # include +# include + -typedef struct { +struct ares_event_configchg { int fd; int token; -} ares_event_configchg_t; +}; + +void ares_event_configchg_destroy(ares_event_configchg_t *configchg) +{ + (void)configchg; + + /* Cleanup happens automatically */ +} + static void ares_event_configchg_free(void *data) { @@ -201,39 +348,73 @@ static void ares_event_configchg_cb(ares_event_thread_t *e, ares_socket_t fd, } } -ares_status_t ares_event_configchg_init(ares_event_thread_t *e) +ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg, ares_event_thread_t *e) { - ares_status_t status = ARES_SUCCESS; - ares_event_configchg_t *configchg; + ares_status_t status = ARES_SUCCESS; + void *handle = NULL; + const char *(*pdns_configuration_notify_key)(void) = NULL; + const char *notify_key = NULL; - configchg = ares_malloc_zero(sizeof(*configchg)); - if (configchg == NULL) { + *configchg = ares_malloc_zero(sizeof(**configchg)); + if (*configchg == NULL) { return ARES_ENOMEM; } - if (notify_register_file_descriptor("com.apple.system.SystemConfiguration.dns_configuration", - &configchg->fd, 0, &configchg->token) != NOTIFY_STATUS_OK) { + /* Load symbol as it isn't normally public */ + handle = dlopen("/usr/lib/libSystem.dylib", RTLD_LAZY | RTLD_NOLOAD); + if (handle == NULL) { + status = ARES_ESERVFAIL; + goto done; + } + + pdns_configuration_notify_key = + dlsym(handle, "dns_configuration_notify_key"); + + if (pdns_configuration_notify_key == NULL) { + status = ARES_ESERVFAIL; + goto done; + } + + notify_key = pdns_configuration_notify_key(); + if (notify_key == NULL) { + status = ARES_ESERVFAIL; + goto done; + } + + if (notify_register_file_descriptor(notify_key, + &(*configchg)->fd, 0, &(*configchg)->token) != NOTIFY_STATUS_OK) { status = ARES_ESERVFAIL; goto done; } status = ares_event_update(NULL, e, ARES_EVENT_FLAG_READ, - ares_event_configchg_cb, configchg->fd, configchg, - ares_event_configchg_free, NULL); + ares_event_configchg_cb, (*configchg)->fd, + *configchg, ares_event_configchg_free, NULL); done: if (status != ARES_SUCCESS) { - ares_event_configchg_free(configchg); + ares_event_configchg_free(*configchg); + *configchg = NULL; + } + + if (handle) { + dlclose(handle); } + return status; } #else -ares_status_t ares_event_configchg_init(ares_event_thread_t *e) +ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg, ares_event_thread_t *e) { /* Not implemented yet, need to spawn thread */ return ARES_SUCCESS; } +void ares_event_configchg_destroy(ares_event_configchg_t *configchg) +{ + /* Todo */ +} + #endif diff --git a/src/lib/ares_event_thread.c b/src/lib/ares_event_thread.c index 8b3de7268e..898bae4831 100644 --- a/src/lib/ares_event_thread.c +++ b/src/lib/ares_event_thread.c @@ -298,6 +298,10 @@ static void ares_event_thread_destroy_int(ares_event_thread_t *e) { ares__llist_node_t *node; + /* Disable configuration change monitoring */ + ares_event_configchg_destroy(e->configchg); + e->configchg = NULL; + /* Wake thread and tell it to shutdown if it exists */ ares__thread_mutex_lock(e->mutex); if (e->isup) { @@ -457,7 +461,7 @@ ares_status_t ares_event_thread_init(ares_channel_t *channel) } /* Initialize monitor for configuration changes */ - status = ares_event_configchg_init(e); + status = ares_event_configchg_init(&e->configchg, e); if (status != ARES_SUCCESS) { ares_event_thread_destroy_int(e); channel->sock_state_cb = NULL; diff --git a/src/lib/ares_sysconfig_mac.c b/src/lib/ares_sysconfig_mac.c index a3b0110dbc..4161ba2b61 100644 --- a/src/lib/ares_sysconfig_mac.c +++ b/src/lib/ares_sysconfig_mac.c @@ -58,7 +58,6 @@ typedef struct { void *handle; - const char *(*dns_configuration_notify_key)(void); dns_config_t *(*dns_configuration_copy)(void); void (*dns_configuration_free)(dns_config_t *config); } dnsinfo_t; @@ -101,15 +100,12 @@ static ares_status_t dnsinfo_init(dnsinfo_t **dnsinfo_out) goto done; } - dnsinfo->dns_configuration_notify_key = - dlsym(dnsinfo->handle, "dns_configuration_notify_key"); dnsinfo->dns_configuration_copy = dlsym(dnsinfo->handle, "dns_configuration_copy"); dnsinfo->dns_configuration_free = dlsym(dnsinfo->handle, "dns_configuration_free"); - if (dnsinfo->dns_configuration_notify_key == NULL || - dnsinfo->dns_configuration_copy == NULL || + if (dnsinfo->dns_configuration_copy == NULL || dnsinfo->dns_configuration_free == NULL) { status = ARES_ESERVFAIL; goto done;