summaryrefslogtreecommitdiffstats
path: root/lib/resolver.c
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@diac24.net>2019-05-23 12:23:02 +0200
committerDavid Lamparter <equinox@diac24.net>2019-07-03 17:15:34 +0200
commitfe9e7b71cf0930db1f8d6872f415466689511885 (patch)
tree42bb1f347ea8f9fbf93874f977dc57b7135307e1 /lib/resolver.c
parentMerge pull request #4550 from manuhalo/fix_bgp_label_cb (diff)
downloadfrr-fe9e7b71cf0930db1f8d6872f415466689511885.tar.xz
frr-fe9e7b71cf0930db1f8d6872f415466689511885.zip
lib: split off c-ares code from nhrpd
This is useful in other places too, e.g. for BMP outbound connections. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'lib/resolver.c')
-rw-r--r--lib/resolver.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/lib/resolver.c b/lib/resolver.c
new file mode 100644
index 000000000..001c293df
--- /dev/null
+++ b/lib/resolver.c
@@ -0,0 +1,245 @@
+/* C-Ares integration to Quagga mainloop
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ares.h>
+#include <ares_version.h>
+
+#include "vector.h"
+#include "thread.h"
+#include "lib_errors.h"
+#include "resolver.h"
+#include "command.h"
+
+struct resolver_state {
+ ares_channel channel;
+ struct thread_master *master;
+ struct thread *timeout;
+ vector read_threads, write_threads;
+};
+
+static struct resolver_state state;
+static bool resolver_debug;
+
+#define THREAD_RUNNING ((struct thread *)-1)
+
+static void resolver_update_timeouts(struct resolver_state *r);
+
+static int resolver_cb_timeout(struct thread *t)
+{
+ struct resolver_state *r = THREAD_ARG(t);
+
+ r->timeout = THREAD_RUNNING;
+ ares_process(r->channel, NULL, NULL);
+ r->timeout = NULL;
+ resolver_update_timeouts(r);
+
+ return 0;
+}
+
+static int resolver_cb_socket_readable(struct thread *t)
+{
+ struct resolver_state *r = THREAD_ARG(t);
+ int fd = THREAD_FD(t);
+
+ vector_set_index(r->read_threads, fd, THREAD_RUNNING);
+ ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
+ if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
+ t = NULL;
+ thread_add_read(r->master, resolver_cb_socket_readable, r, fd,
+ &t);
+ vector_set_index(r->read_threads, fd, t);
+ }
+ resolver_update_timeouts(r);
+
+ return 0;
+}
+
+static int resolver_cb_socket_writable(struct thread *t)
+{
+ struct resolver_state *r = THREAD_ARG(t);
+ int fd = THREAD_FD(t);
+
+ vector_set_index(r->write_threads, fd, THREAD_RUNNING);
+ ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
+ if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
+ t = NULL;
+ thread_add_write(r->master, resolver_cb_socket_writable, r, fd,
+ &t);
+ vector_set_index(r->write_threads, fd, t);
+ }
+ resolver_update_timeouts(r);
+
+ return 0;
+}
+
+static void resolver_update_timeouts(struct resolver_state *r)
+{
+ struct timeval *tv, tvbuf;
+
+ if (r->timeout == THREAD_RUNNING)
+ return;
+
+ THREAD_OFF(r->timeout);
+ tv = ares_timeout(r->channel, NULL, &tvbuf);
+ if (tv) {
+ unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+ thread_add_timer_msec(r->master, resolver_cb_timeout, r,
+ timeoutms, &r->timeout);
+ }
+}
+
+static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
+ int writable)
+{
+ struct resolver_state *r = (struct resolver_state *)data;
+ struct thread *t;
+
+ if (readable) {
+ t = vector_lookup_ensure(r->read_threads, fd);
+ if (!t) {
+ thread_add_read(r->master, resolver_cb_socket_readable,
+ r, fd, &t);
+ vector_set_index(r->read_threads, fd, t);
+ }
+ } else {
+ t = vector_lookup(r->read_threads, fd);
+ if (t) {
+ if (t != THREAD_RUNNING) {
+ THREAD_OFF(t);
+ }
+ vector_unset(r->read_threads, fd);
+ }
+ }
+
+ if (writable) {
+ t = vector_lookup_ensure(r->write_threads, fd);
+ if (!t) {
+ thread_add_read(r->master, resolver_cb_socket_writable,
+ r, fd, &t);
+ vector_set_index(r->write_threads, fd, t);
+ }
+ } else {
+ t = vector_lookup(r->write_threads, fd);
+ if (t) {
+ if (t != THREAD_RUNNING) {
+ THREAD_OFF(t);
+ }
+ vector_unset(r->write_threads, fd);
+ }
+ }
+}
+
+
+static void ares_address_cb(void *arg, int status, int timeouts,
+ struct hostent *he)
+{
+ struct resolver_query *query = (struct resolver_query *)arg;
+ union sockunion addr[16];
+ size_t i;
+
+ if (status != ARES_SUCCESS) {
+ if (resolver_debug)
+ zlog_debug("[%p] Resolving failed", query);
+
+ query->callback(query, -1, NULL);
+ query->callback = NULL;
+ return;
+ }
+
+ for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
+ memset(&addr[i], 0, sizeof(addr[i]));
+ addr[i].sa.sa_family = he->h_addrtype;
+ switch (he->h_addrtype) {
+ case AF_INET:
+ memcpy(&addr[i].sin.sin_addr,
+ (uint8_t *)he->h_addr_list[i], he->h_length);
+ break;
+ case AF_INET6:
+ memcpy(&addr[i].sin6.sin6_addr,
+ (uint8_t *)he->h_addr_list[i], he->h_length);
+ break;
+ }
+ }
+
+ if (resolver_debug)
+ zlog_debug("[%p] Resolved with %d results", query, (int)i);
+
+ query->callback(query, i, &addr[0]);
+ query->callback = NULL;
+}
+
+void resolver_resolve(struct resolver_query *query, int af,
+ const char *hostname,
+ void (*callback)(struct resolver_query *, int,
+ union sockunion *))
+{
+ if (query->callback != NULL) {
+ flog_err(
+ EC_LIB_RESOLVER,
+ "Trying to resolve '%s', but previous query was not finished yet",
+ hostname);
+ return;
+ }
+
+ if (resolver_debug)
+ zlog_debug("[%p] Resolving '%s'", query, hostname);
+
+ query->callback = callback;
+ ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
+ resolver_update_timeouts(&state);
+}
+
+DEFUN(debug_resolver,
+ debug_resolver_cmd,
+ "[no] debug resolver",
+ NO_STR
+ DEBUG_STR
+ "Debug DNS resolver actions\n")
+{
+ resolver_debug = (argc == 2);
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node resolver_debug_node = {RESOLVER_DEBUG_NODE, "", 1};
+
+static int resolver_config_write_debug(struct vty *vty)
+{
+ if (resolver_debug)
+ vty_out(vty, "debug resolver\n");
+ return 1;
+}
+
+
+void resolver_init(struct thread_master *tm)
+{
+ struct ares_options ares_opts;
+
+ state.master = tm;
+ state.read_threads = vector_init(1);
+ state.write_threads = vector_init(1);
+
+ ares_opts = (struct ares_options){
+ .sock_state_cb = &ares_socket_cb,
+ .sock_state_cb_data = &state,
+ .timeout = 2,
+ .tries = 3,
+ };
+
+ ares_init_options(&state.channel, &ares_opts,
+ ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
+ | ARES_OPT_TRIES);
+
+ install_node(&resolver_debug_node, resolver_config_write_debug);
+ install_element(CONFIG_NODE, &debug_resolver_cmd);
+ install_element(ENABLE_NODE, &debug_resolver_cmd);
+}