1 /* C-Ares integration to Quagga mainloop
2 * Copyright (c) 2014-2015 Timo Teräs
4 * This file is free software: you may copy, redistribute and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
11 #include <ares_version.h>
17 struct resolver_state {
19 struct thread *timeout;
20 vector read_threads, write_threads;
23 static struct resolver_state state;
25 #define THREAD_RUNNING ((struct thread *)-1)
27 static void resolver_update_timeouts(struct resolver_state *r);
29 static int resolver_cb_timeout(struct thread *t)
31 struct resolver_state *r = THREAD_ARG(t);
33 r->timeout = THREAD_RUNNING;
34 ares_process(r->channel, NULL, NULL);
36 resolver_update_timeouts(r);
41 static int resolver_cb_socket_readable(struct thread *t)
43 struct resolver_state *r = THREAD_ARG(t);
44 int fd = THREAD_FD(t);
46 vector_set_index(r->read_threads, fd, THREAD_RUNNING);
47 ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
48 if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
50 THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
51 vector_set_index(r->read_threads, fd, t);
53 resolver_update_timeouts(r);
58 static int resolver_cb_socket_writable(struct thread *t)
60 struct resolver_state *r = THREAD_ARG(t);
61 int fd = THREAD_FD(t);
63 vector_set_index(r->write_threads, fd, THREAD_RUNNING);
64 ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
65 if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
67 THREAD_WRITE_ON(master, t, resolver_cb_socket_writable, r, fd);
68 vector_set_index(r->write_threads, fd, t);
70 resolver_update_timeouts(r);
75 static void resolver_update_timeouts(struct resolver_state *r)
77 struct timeval *tv, tvbuf;
79 if (r->timeout == THREAD_RUNNING) return;
81 THREAD_OFF(r->timeout);
82 tv = ares_timeout(r->channel, NULL, &tvbuf);
84 unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
85 THREAD_TIMER_MSEC_ON(master, r->timeout, resolver_cb_timeout, r, timeoutms);
89 static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable)
91 struct resolver_state *r = (struct resolver_state *) data;
95 t = vector_lookup_ensure(r->read_threads, fd);
97 THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
98 vector_set_index(r->read_threads, fd, t);
101 t = vector_lookup(r->read_threads, fd);
103 if (t != THREAD_RUNNING) {
106 vector_unset(r->read_threads, fd);
111 t = vector_lookup_ensure(r->write_threads, fd);
113 THREAD_READ_ON(master, t, resolver_cb_socket_writable, r, fd);
114 vector_set_index(r->write_threads, fd, t);
117 t = vector_lookup(r->write_threads, fd);
119 if (t != THREAD_RUNNING) {
122 vector_unset(r->write_threads, fd);
127 void resolver_init(void)
129 struct ares_options ares_opts;
131 state.read_threads = vector_init(1);
132 state.write_threads = vector_init(1);
134 ares_opts = (struct ares_options) {
135 .sock_state_cb = &ares_socket_cb,
136 .sock_state_cb_data = &state,
141 ares_init_options(&state.channel, &ares_opts,
142 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT |
147 static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he)
149 struct resolver_query *query = (struct resolver_query *) arg;
150 union sockunion addr[16];
153 if (status != ARES_SUCCESS) {
154 debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query);
155 query->callback(query, -1, NULL);
156 query->callback = NULL;
160 for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); i++) {
161 memset(&addr[i], 0, sizeof(addr[i]));
162 addr[i].sa.sa_family = he->h_addrtype;
163 switch (he->h_addrtype) {
165 memcpy(&addr[i].sin.sin_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
168 memcpy(&addr[i].sin6.sin6_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
173 debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, (int) i);
174 query->callback(query, i, &addr[0]);
175 query->callback = NULL;
178 void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*callback)(struct resolver_query *, int, union sockunion *))
180 if (query->callback != NULL) {
181 zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname);
185 debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname);
187 query->callback = callback;
188 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
189 resolver_update_timeouts(&state);