From 18a8bcc29e78aa7633846f0f990d36c1c45b47f0 Mon Sep 17 00:00:00 2001 From: Brad House Date: Wed, 15 May 2024 10:55:44 -0700 Subject: [PATCH] dns name compression write failure Due to a logic flaw dns name compression writing was not properly implemented which would result in the name prefix not being written for a partial match. Fixes Bug: #757 Fix By: Brad House (@bradh352) --- src/lib/ares_dns_name.c | 8 +- src/lib/ares_event_configchg.c | 152 +++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/lib/ares_event_configchg.c diff --git a/src/lib/ares_dns_name.c b/src/lib/ares_dns_name.c index f4085ab2..85e26cd2 100644 --- a/src/lib/ares_dns_name.c +++ b/src/lib/ares_dns_name.c @@ -362,6 +362,7 @@ ares_status_t ares__dns_name_write(ares__buf_t *buf, ares__llist_t **list, { const ares_nameoffset_t *off = NULL; size_t name_len; + size_t orig_name_len; size_t pos = ares__buf_len(buf); ares_dns_labels_t labels; char name_copy[512]; @@ -375,7 +376,8 @@ ares_status_t ares__dns_name_write(ares__buf_t *buf, ares__llist_t **list, /* NOTE: due to possible escaping, name_copy buffer is > 256 to allow for * this */ - name_len = ares_strcpy(name_copy, name, sizeof(name_copy)); + name_len = ares_strcpy(name_copy, name, sizeof(name_copy)); + orig_name_len = name_len; /* Find longest match */ if (list != NULL) { @@ -388,7 +390,7 @@ ares_status_t ares__dns_name_write(ares__buf_t *buf, ares__llist_t **list, } /* Output labels */ - if (off == NULL || off->name_len != name_len) { + if (off == NULL || off->name_len != orig_name_len) { size_t i; status = ares_split_dns_name(&labels, validate_hostname, name_copy); @@ -432,7 +434,7 @@ ares_status_t ares__dns_name_write(ares__buf_t *buf, ares__llist_t **list, /* Store pointer for future jumps as long as its not an exact match for * a prior entry */ - if (list != NULL && (off == NULL || off->name_len != name_len) && + if (list != NULL && (off == NULL || off->name_len != orig_name_len) && name_len > 0) { status = ares__nameoffset_create(list, name /* not truncated copy! */, pos); if (status != ARES_SUCCESS) { diff --git a/src/lib/ares_event_configchg.c b/src/lib/ares_event_configchg.c new file mode 100644 index 00000000..11149ad4 --- /dev/null +++ b/src/lib/ares_event_configchg.c @@ -0,0 +1,152 @@ +/* MIT License + * + * Copyright (c) 2024 Brad House + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ +#include "ares_setup.h" +#include "ares.h" +#include "ares_private.h" +#include "ares_event.h" + +static void ares_event_configchg_reload(ares_channel_t *channel) +{ + /* XXX: dispatch via thread so we don't block events */ + ares_reinit(channel); +} + +#ifdef __LINUX__ +# include +struct ares_event_configchg { + int inotify_fd; + ares_event_t *event; +}; + +void ares_event_configchg_destroy(ares_event_configchg_t *configchg, , ares_event_thread *e) +{ + if (configchg == NULL || configchg->inotify_fd == -1) { + return; + } + ares_event_update(NULL, e, ARES_EVENT_FLAG_NONE, NULL, configchg->inotify_fd, NULL, NULL); +} + +static void ares_event_configchg_free(void *data) +{ + ares_event_configchg_t *configchg = data; + if (configchg == NULL) + return; + + if (configchg->inotify_fd >= 0) { + close(configchg->inotify_fd); + configchg->inotify_fd = -1; + } + + ares_free(configchg); +} + +void ares_event_configchg_cb(ares_event_thread_t *e, ares_socket_t fd, + void *data, ares_event_flags_t flags) +{ + ares_event_configchg_t *configchg = data; + /* Some systems cannot read integer variables if they are not + properly aligned. On other systems, incorrect alignment may + decrease performance. Hence, the buffer used for reading from + the inotify file descriptor should have the same alignment as + struct inotify_event. */ + char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event)))); + const struct inotify_event *event; + ssize_t len; + ares_bool_t triggered = ARES_FALSE; + + while (1) { + const char *ptr; + + len = read(fd, buf, sizeof(buf)); + if (len <= 0) { + break; + } + + /* Loop over all events in the buffer. */ + for (ptr = buf; ptr < buf + len; + ptr += sizeof(struct inotify_event) + event->len) { + + event = (const struct inotify_event *)ptr; + + if (event->name == NULL) + continue; + + if (strcasecmp(name, "resolv.conf") == 0 || + strcasecmp(name, "nsswitch.conf") == 0) { + triggered = ARES_TRUE; + } + } + } + if (triggered) { + ares_event_configchg_reload(e->channel); + } +} + + +ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg, ares_event_thread *e) +{ + ares_status_t status = ARES_SUCCESS; + + *configchg = ares_malloc_zero(sizeof(**configchg)); + if (*configchg == NULL) { + return ARES_ENOMEM; + } + + *configchg->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (*configchg->inotify_fd == -1) { + status = ARES_ESERVFAIL; + goto done; + } + + + /* We need to monitor /etc/resolv.conf, /etc/nsswitch.conf */ + if (inotify_add_watch(*configchg->inotify_fd, "/etc", IN_CREATE|IN_MODIFY|IN_MOVED_TO|IN_ONLYDIR) == -1) { + status = ARES_ESERVFAIL; + goto done; + } + + status = ares_event_update(&(*configchg)->event, e, ARES_EVENT_FLAG_READ, + cb, *configchg->inotify_fd, + ares_event_configchg_free, NULL); + +done: + if (status != ARES_SUCCESS) { + ares_event_configchg_destroy(*configchg); + *configchg = NULL; + } + return status; +} + + + + +#elif defined(_WIN32) + +#elif defined(__APPLE__) + +#else + +#endif