diff options
author | David Lamparter <equinox@diac24.net> | 2019-05-23 12:23:02 +0200 |
---|---|---|
committer | David Lamparter <equinox@diac24.net> | 2019-07-03 17:15:34 +0200 |
commit | fe9e7b71cf0930db1f8d6872f415466689511885 (patch) | |
tree | 42bb1f347ea8f9fbf93874f977dc57b7135307e1 /lib/resolver.c | |
parent | Merge pull request #4550 from manuhalo/fix_bgp_label_cb (diff) | |
download | frr-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.c | 245 |
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); +} |