]> git.proxmox.com Git - mirror_frr.git/blob - lib/resolver.c
Merge pull request #9292 from mobash-rasool/ospfv3-bug2
[mirror_frr.git] / lib / resolver.c
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
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13
14 #include <ares.h>
15 #include <ares_version.h>
16
17 #include "typesafe.h"
18 #include "jhash.h"
19 #include "thread.h"
20 #include "lib_errors.h"
21 #include "resolver.h"
22 #include "command.h"
23 #include "xref.h"
24 #include "vrf.h"
25
26 XREF_SETUP();
27
28 struct resolver_state {
29 ares_channel channel;
30 struct thread_master *master;
31 struct thread *timeout;
32 };
33
34 static struct resolver_state state;
35 static bool resolver_debug;
36
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
46 DEFINE_MTYPE_STATIC(LIB, ARES_FD, "c-ares (DNS) file descriptor information");
47 PREDECL_HASH(resolver_fds);
48
49 struct 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
57 static 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
63 static uint32_t resolver_fd_hash(const struct resolver_fd *item)
64 {
65 return jhash_1word(item->fd, 0xacd04c9e);
66 }
67
68 DECLARE_HASH(resolver_fds, struct resolver_fd, itm, resolver_fd_cmp,
69 resolver_fd_hash);
70
71 static struct resolver_fds_head resfds[1] = {INIT_HASH(resfds[0])};
72
73 static 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
91 static 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 */
104
105 static void resolver_update_timeouts(struct resolver_state *r);
106
107 static int resolver_cb_timeout(struct thread *t)
108 {
109 struct resolver_state *r = THREAD_ARG(t);
110
111 ares_process(r->channel, NULL, NULL);
112 resolver_update_timeouts(r);
113
114 return 0;
115 }
116
117 static int resolver_cb_socket_readable(struct thread *t)
118 {
119 struct resolver_fd *resfd = THREAD_ARG(t);
120 struct resolver_state *r = resfd->state;
121
122 thread_add_read(r->master, resolver_cb_socket_readable, resfd,
123 resfd->fd, &resfd->t_read);
124 /* ^ ordering important:
125 * ares_process_fd may transitively call THREAD_OFF(resfd->t_read)
126 * combined with resolver_fd_drop_maybe, so resfd may be free'd after!
127 */
128 ares_process_fd(r->channel, resfd->fd, ARES_SOCKET_BAD);
129 resolver_update_timeouts(r);
130
131 return 0;
132 }
133
134 static int resolver_cb_socket_writable(struct thread *t)
135 {
136 struct resolver_fd *resfd = THREAD_ARG(t);
137 struct resolver_state *r = resfd->state;
138
139 thread_add_write(r->master, resolver_cb_socket_writable, resfd,
140 resfd->fd, &resfd->t_write);
141 /* ^ ordering important:
142 * ares_process_fd may transitively call THREAD_OFF(resfd->t_write)
143 * combined with resolver_fd_drop_maybe, so resfd may be free'd after!
144 */
145 ares_process_fd(r->channel, ARES_SOCKET_BAD, resfd->fd);
146 resolver_update_timeouts(r);
147
148 return 0;
149 }
150
151 static void resolver_update_timeouts(struct resolver_state *r)
152 {
153 struct timeval *tv, tvbuf;
154
155 THREAD_OFF(r->timeout);
156 tv = ares_timeout(r->channel, NULL, &tvbuf);
157 if (tv) {
158 unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
159
160 thread_add_timer_msec(r->master, resolver_cb_timeout, r,
161 timeoutms, &r->timeout);
162 }
163 }
164
165 static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
166 int writable)
167 {
168 struct resolver_state *r = (struct resolver_state *)data;
169 struct resolver_fd *resfd;
170
171 resfd = resolver_fd_get(fd, (readable || writable) ? r : NULL);
172 if (!resfd)
173 return;
174
175 assert(resfd->state == r);
176
177 if (!readable)
178 THREAD_OFF(resfd->t_read);
179 else if (!resfd->t_read)
180 thread_add_read(r->master, resolver_cb_socket_readable, resfd,
181 fd, &resfd->t_read);
182
183 if (!writable)
184 THREAD_OFF(resfd->t_write);
185 else if (!resfd->t_write)
186 thread_add_write(r->master, resolver_cb_socket_writable, resfd,
187 fd, &resfd->t_write);
188
189 resolver_fd_drop_maybe(resfd);
190 }
191
192
193 static void ares_address_cb(void *arg, int status, int timeouts,
194 struct hostent *he)
195 {
196 struct resolver_query *query = (struct resolver_query *)arg;
197 union sockunion addr[16];
198 void (*callback)(struct resolver_query *, const char *, int,
199 union sockunion *);
200 size_t i;
201
202 callback = query->callback;
203 query->callback = NULL;
204
205 if (status != ARES_SUCCESS) {
206 if (resolver_debug)
207 zlog_debug("[%p] Resolving failed (%s)",
208 query, ares_strerror(status));
209
210 callback(query, ares_strerror(status), -1, NULL);
211 return;
212 }
213
214 for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
215 memset(&addr[i], 0, sizeof(addr[i]));
216 addr[i].sa.sa_family = he->h_addrtype;
217 switch (he->h_addrtype) {
218 case AF_INET:
219 memcpy(&addr[i].sin.sin_addr,
220 (uint8_t *)he->h_addr_list[i], he->h_length);
221 break;
222 case AF_INET6:
223 memcpy(&addr[i].sin6.sin6_addr,
224 (uint8_t *)he->h_addr_list[i], he->h_length);
225 break;
226 }
227 }
228
229 if (resolver_debug)
230 zlog_debug("[%p] Resolved with %d results", query, (int)i);
231
232 callback(query, NULL, i, &addr[0]);
233 }
234
235 static int resolver_cb_literal(struct thread *t)
236 {
237 struct resolver_query *query = THREAD_ARG(t);
238 void (*callback)(struct resolver_query *, const char *, int,
239 union sockunion *);
240
241 callback = query->callback;
242 query->callback = NULL;
243
244 callback(query, ARES_SUCCESS, 1, &query->literal_addr);
245 return 0;
246 }
247
248 void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
249 const char *hostname,
250 void (*callback)(struct resolver_query *, const char *,
251 int, union sockunion *))
252 {
253 int ret;
254
255 if (query->callback != NULL) {
256 flog_err(
257 EC_LIB_RESOLVER,
258 "Trying to resolve '%s', but previous query was not finished yet",
259 hostname);
260 return;
261 }
262
263 query->callback = callback;
264 query->literal_cb = NULL;
265
266 ret = str2sockunion(hostname, &query->literal_addr);
267 if (ret == 0) {
268 if (resolver_debug)
269 zlog_debug("[%p] Resolving '%s' (IP literal)",
270 query, hostname);
271
272 /* for consistency with proper name lookup, don't call the
273 * callback immediately; defer to thread loop
274 */
275 thread_add_timer_msec(state.master, resolver_cb_literal,
276 query, 0, &query->literal_cb);
277 return;
278 }
279
280 if (resolver_debug)
281 zlog_debug("[%p] Resolving '%s'", query, hostname);
282
283 ret = vrf_switch_to_netns(vrf_id);
284 if (ret < 0) {
285 flog_err_sys(EC_LIB_SOCKET, "%s: Can't switch to VRF %u (%s)",
286 __func__, vrf_id, safe_strerror(errno));
287 return;
288 }
289 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
290 ret = vrf_switchback_to_initial();
291 if (ret < 0)
292 flog_err_sys(EC_LIB_SOCKET,
293 "%s: Can't switchback from VRF %u (%s)", __func__,
294 vrf_id, safe_strerror(errno));
295 resolver_update_timeouts(&state);
296 }
297
298 DEFUN(debug_resolver,
299 debug_resolver_cmd,
300 "[no] debug resolver",
301 NO_STR
302 DEBUG_STR
303 "Debug DNS resolver actions\n")
304 {
305 resolver_debug = (argc == 2);
306 return CMD_SUCCESS;
307 }
308
309 static int resolver_config_write_debug(struct vty *vty);
310 static struct cmd_node resolver_debug_node = {
311 .name = "resolver debug",
312 .node = RESOLVER_DEBUG_NODE,
313 .prompt = "",
314 .config_write = resolver_config_write_debug,
315 };
316
317 static int resolver_config_write_debug(struct vty *vty)
318 {
319 if (resolver_debug)
320 vty_out(vty, "debug resolver\n");
321 return 1;
322 }
323
324
325 void resolver_init(struct thread_master *tm)
326 {
327 struct ares_options ares_opts;
328
329 state.master = tm;
330
331 ares_opts = (struct ares_options){
332 .sock_state_cb = &ares_socket_cb,
333 .sock_state_cb_data = &state,
334 .timeout = 2,
335 .tries = 3,
336 };
337
338 ares_init_options(&state.channel, &ares_opts,
339 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
340 | ARES_OPT_TRIES);
341
342 install_node(&resolver_debug_node);
343 install_element(CONFIG_NODE, &debug_resolver_cmd);
344 install_element(ENABLE_NODE, &debug_resolver_cmd);
345 }