]> git.proxmox.com Git - mirror_frr.git/blame - lib/resolver.c
Merge pull request #10767 from donaldsharp/some_fixes
[mirror_frr.git] / lib / resolver.c
CommitLineData
2fb975da
TT
1/* C-Ares integration to Quagga mainloop
2 * Copyright (c) 2014-2015 Timo Teräs
3 *
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.
8 */
9
b45ac5f5
DL
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
2fb975da
TT
14#include <ares.h>
15#include <ares_version.h>
16
faf079ff
DL
17#include "typesafe.h"
18#include "jhash.h"
2fb975da 19#include "thread.h"
aed07011 20#include "lib_errors.h"
fe9e7b71
DL
21#include "resolver.h"
22#include "command.h"
b2fa8c0f 23#include "xref.h"
c742573b 24#include "vrf.h"
b2fa8c0f 25
80413c20 26XREF_SETUP();
2fb975da
TT
27
28struct resolver_state {
29 ares_channel channel;
fe9e7b71 30 struct thread_master *master;
2fb975da 31 struct thread *timeout;
2fb975da
TT
32};
33
34static struct resolver_state state;
fe9e7b71 35static bool resolver_debug;
2fb975da 36
faf079ff
DL
37/* a FD doesn't necessarily map 1:1 to a request; we could be talking to
38 * multiple caches simultaneously, to see which responds fastest.
39 * Theoretically we could also be using the same fd for multiple lookups,
40 * but the c-ares API guarantees an n:1 mapping for fd => channel.
41 *
42 * Either way c-ares makes that decision and we just need to deal with
43 * whatever FDs it gives us.
44 */
45
46DEFINE_MTYPE_STATIC(LIB, ARES_FD, "c-ares (DNS) file descriptor information");
47PREDECL_HASH(resolver_fds);
48
49struct resolver_fd {
50 struct resolver_fds_item itm;
51
52 int fd;
53 struct resolver_state *state;
54 struct thread *t_read, *t_write;
55};
56
57static int resolver_fd_cmp(const struct resolver_fd *a,
58 const struct resolver_fd *b)
59{
60 return numcmp(a->fd, b->fd);
61}
62
63static uint32_t resolver_fd_hash(const struct resolver_fd *item)
64{
65 return jhash_1word(item->fd, 0xacd04c9e);
66}
67
68DECLARE_HASH(resolver_fds, struct resolver_fd, itm, resolver_fd_cmp,
69 resolver_fd_hash);
70
71static struct resolver_fds_head resfds[1] = {INIT_HASH(resfds[0])};
72
73static struct resolver_fd *resolver_fd_get(int fd,
74 struct resolver_state *newstate)
75{
76 struct resolver_fd ref = {.fd = fd}, *res;
77
78 res = resolver_fds_find(resfds, &ref);
79 if (!res && newstate) {
80 res = XCALLOC(MTYPE_ARES_FD, sizeof(*res));
81 res->fd = fd;
82 res->state = newstate;
83 resolver_fds_add(resfds, res);
84
85 if (resolver_debug)
86 zlog_debug("c-ares registered FD %d", fd);
87 }
88 return res;
89}
90
91static void resolver_fd_drop_maybe(struct resolver_fd *resfd)
92{
93 if (resfd->t_read || resfd->t_write)
94 return;
95
96 if (resolver_debug)
97 zlog_debug("c-ares unregistered FD %d", resfd->fd);
98
99 resolver_fds_del(resfds, resfd);
100 XFREE(MTYPE_ARES_FD, resfd);
101}
102
103/* end of FD housekeeping */
2fb975da
TT
104
105static void resolver_update_timeouts(struct resolver_state *r);
106
cc9f21da 107static void resolver_cb_timeout(struct thread *t)
2fb975da
TT
108{
109 struct resolver_state *r = THREAD_ARG(t);
110
2fb975da 111 ares_process(r->channel, NULL, NULL);
2fb975da 112 resolver_update_timeouts(r);
2fb975da
TT
113}
114
cc9f21da 115static void resolver_cb_socket_readable(struct thread *t)
2fb975da 116{
faf079ff
DL
117 struct resolver_fd *resfd = THREAD_ARG(t);
118 struct resolver_state *r = resfd->state;
119
120 thread_add_read(r->master, resolver_cb_socket_readable, resfd,
121 resfd->fd, &resfd->t_read);
122 /* ^ ordering important:
123 * ares_process_fd may transitively call THREAD_OFF(resfd->t_read)
124 * combined with resolver_fd_drop_maybe, so resfd may be free'd after!
125 */
126 ares_process_fd(r->channel, resfd->fd, ARES_SOCKET_BAD);
2fb975da 127 resolver_update_timeouts(r);
2fb975da
TT
128}
129
cc9f21da 130static void resolver_cb_socket_writable(struct thread *t)
2fb975da 131{
faf079ff
DL
132 struct resolver_fd *resfd = THREAD_ARG(t);
133 struct resolver_state *r = resfd->state;
134
135 thread_add_write(r->master, resolver_cb_socket_writable, resfd,
136 resfd->fd, &resfd->t_write);
137 /* ^ ordering important:
138 * ares_process_fd may transitively call THREAD_OFF(resfd->t_write)
139 * combined with resolver_fd_drop_maybe, so resfd may be free'd after!
140 */
141 ares_process_fd(r->channel, ARES_SOCKET_BAD, resfd->fd);
2fb975da 142 resolver_update_timeouts(r);
2fb975da
TT
143}
144
145static void resolver_update_timeouts(struct resolver_state *r)
146{
147 struct timeval *tv, tvbuf;
148
2fb975da
TT
149 THREAD_OFF(r->timeout);
150 tv = ares_timeout(r->channel, NULL, &tvbuf);
151 if (tv) {
152 unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
faf079ff 153
fe9e7b71
DL
154 thread_add_timer_msec(r->master, resolver_cb_timeout, r,
155 timeoutms, &r->timeout);
2fb975da
TT
156 }
157}
158
996c9314
LB
159static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
160 int writable)
2fb975da 161{
996c9314 162 struct resolver_state *r = (struct resolver_state *)data;
faf079ff 163 struct resolver_fd *resfd;
2fb975da 164
faf079ff
DL
165 resfd = resolver_fd_get(fd, (readable || writable) ? r : NULL);
166 if (!resfd)
167 return;
168
169 assert(resfd->state == r);
170
171 if (!readable)
172 THREAD_OFF(resfd->t_read);
173 else if (!resfd->t_read)
174 thread_add_read(r->master, resolver_cb_socket_readable, resfd,
175 fd, &resfd->t_read);
176
177 if (!writable)
178 THREAD_OFF(resfd->t_write);
179 else if (!resfd->t_write)
180 thread_add_write(r->master, resolver_cb_socket_writable, resfd,
181 fd, &resfd->t_write);
182
183 resolver_fd_drop_maybe(resfd);
2fb975da
TT
184}
185
2fb975da 186
996c9314
LB
187static void ares_address_cb(void *arg, int status, int timeouts,
188 struct hostent *he)
2fb975da 189{
996c9314 190 struct resolver_query *query = (struct resolver_query *)arg;
2fb975da 191 union sockunion addr[16];
3286ca07
DL
192 void (*callback)(struct resolver_query *, const char *, int,
193 union sockunion *);
2fb975da
TT
194 size_t i;
195
50cdb6cf
DL
196 callback = query->callback;
197 query->callback = NULL;
198
2fb975da 199 if (status != ARES_SUCCESS) {
fe9e7b71 200 if (resolver_debug)
3286ca07
DL
201 zlog_debug("[%p] Resolving failed (%s)",
202 query, ares_strerror(status));
fe9e7b71 203
3286ca07 204 callback(query, ares_strerror(status), -1, NULL);
2fb975da
TT
205 return;
206 }
207
7e3a1ec7 208 for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
2fb975da
TT
209 memset(&addr[i], 0, sizeof(addr[i]));
210 addr[i].sa.sa_family = he->h_addrtype;
211 switch (he->h_addrtype) {
212 case AF_INET:
996c9314
LB
213 memcpy(&addr[i].sin.sin_addr,
214 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
215 break;
216 case AF_INET6:
996c9314
LB
217 memcpy(&addr[i].sin6.sin6_addr,
218 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
219 break;
220 }
221 }
222
fe9e7b71
DL
223 if (resolver_debug)
224 zlog_debug("[%p] Resolved with %d results", query, (int)i);
225
3286ca07 226 callback(query, NULL, i, &addr[0]);
2fb975da
TT
227}
228
cc9f21da 229static void resolver_cb_literal(struct thread *t)
125dc952
DL
230{
231 struct resolver_query *query = THREAD_ARG(t);
3286ca07
DL
232 void (*callback)(struct resolver_query *, const char *, int,
233 union sockunion *);
125dc952
DL
234
235 callback = query->callback;
236 query->callback = NULL;
237
3286ca07 238 callback(query, ARES_SUCCESS, 1, &query->literal_addr);
125dc952
DL
239}
240
c742573b 241void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
996c9314 242 const char *hostname,
3286ca07
DL
243 void (*callback)(struct resolver_query *, const char *,
244 int, union sockunion *))
2fb975da 245{
125dc952
DL
246 int ret;
247
2fb975da 248 if (query->callback != NULL) {
1c50c1c0 249 flog_err(
fe9e7b71 250 EC_LIB_RESOLVER,
1c50c1c0
QY
251 "Trying to resolve '%s', but previous query was not finished yet",
252 hostname);
2fb975da
TT
253 return;
254 }
255
125dc952
DL
256 query->callback = callback;
257 query->literal_cb = NULL;
258
259 ret = str2sockunion(hostname, &query->literal_addr);
260 if (ret == 0) {
261 if (resolver_debug)
262 zlog_debug("[%p] Resolving '%s' (IP literal)",
263 query, hostname);
264
265 /* for consistency with proper name lookup, don't call the
266 * callback immediately; defer to thread loop
267 */
268 thread_add_timer_msec(state.master, resolver_cb_literal,
269 query, 0, &query->literal_cb);
270 return;
271 }
272
fe9e7b71
DL
273 if (resolver_debug)
274 zlog_debug("[%p] Resolving '%s'", query, hostname);
2fb975da 275
c742573b
PG
276 ret = vrf_switch_to_netns(vrf_id);
277 if (ret < 0) {
278 flog_err_sys(EC_LIB_SOCKET, "%s: Can't switch to VRF %u (%s)",
279 __func__, vrf_id, safe_strerror(errno));
280 return;
281 }
2fb975da 282 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
c742573b
PG
283 ret = vrf_switchback_to_initial();
284 if (ret < 0)
285 flog_err_sys(EC_LIB_SOCKET,
286 "%s: Can't switchback from VRF %u (%s)", __func__,
287 vrf_id, safe_strerror(errno));
2fb975da
TT
288 resolver_update_timeouts(&state);
289}
fe9e7b71
DL
290
291DEFUN(debug_resolver,
292 debug_resolver_cmd,
293 "[no] debug resolver",
294 NO_STR
295 DEBUG_STR
296 "Debug DNS resolver actions\n")
297{
298 resolver_debug = (argc == 2);
299 return CMD_SUCCESS;
300}
301
612c2c15 302static int resolver_config_write_debug(struct vty *vty);
62b346ee 303static struct cmd_node resolver_debug_node = {
f4b8291f 304 .name = "resolver debug",
62b346ee
DL
305 .node = RESOLVER_DEBUG_NODE,
306 .prompt = "",
612c2c15 307 .config_write = resolver_config_write_debug,
62b346ee 308};
fe9e7b71
DL
309
310static int resolver_config_write_debug(struct vty *vty)
311{
312 if (resolver_debug)
313 vty_out(vty, "debug resolver\n");
314 return 1;
315}
316
317
318void resolver_init(struct thread_master *tm)
319{
320 struct ares_options ares_opts;
321
322 state.master = tm;
fe9e7b71
DL
323
324 ares_opts = (struct ares_options){
325 .sock_state_cb = &ares_socket_cb,
326 .sock_state_cb_data = &state,
327 .timeout = 2,
328 .tries = 3,
329 };
330
331 ares_init_options(&state.channel, &ares_opts,
332 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
333 | ARES_OPT_TRIES);
334
612c2c15 335 install_node(&resolver_debug_node);
fe9e7b71
DL
336 install_element(CONFIG_NODE, &debug_resolver_cmd);
337 install_element(ENABLE_NODE, &debug_resolver_cmd);
338}