]> git.proxmox.com Git - mirror_frr.git/blob - lib/resolver.c
Merge pull request #13278 from FRRouting/mergify/bp/stable/8.5/pr-13269
[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 void 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
115 static void resolver_cb_socket_readable(struct thread *t)
116 {
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);
127 resolver_update_timeouts(r);
128 }
129
130 static void resolver_cb_socket_writable(struct thread *t)
131 {
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);
142 resolver_update_timeouts(r);
143 }
144
145 static void resolver_update_timeouts(struct resolver_state *r)
146 {
147 struct timeval *tv, tvbuf;
148
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;
153
154 thread_add_timer_msec(r->master, resolver_cb_timeout, r,
155 timeoutms, &r->timeout);
156 }
157 }
158
159 static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
160 int writable)
161 {
162 struct resolver_state *r = (struct resolver_state *)data;
163 struct resolver_fd *resfd;
164
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);
184 }
185
186
187 static void ares_address_cb(void *arg, int status, int timeouts,
188 struct hostent *he)
189 {
190 struct resolver_query *query = (struct resolver_query *)arg;
191 union sockunion addr[16];
192 void (*callback)(struct resolver_query *, const char *, int,
193 union sockunion *);
194 size_t i;
195
196 callback = query->callback;
197 query->callback = NULL;
198
199 if (status != ARES_SUCCESS) {
200 if (resolver_debug)
201 zlog_debug("[%p] Resolving failed (%s)",
202 query, ares_strerror(status));
203
204 callback(query, ares_strerror(status), -1, NULL);
205 return;
206 }
207
208 for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
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:
213 memcpy(&addr[i].sin.sin_addr,
214 (uint8_t *)he->h_addr_list[i], he->h_length);
215 break;
216 case AF_INET6:
217 memcpy(&addr[i].sin6.sin6_addr,
218 (uint8_t *)he->h_addr_list[i], he->h_length);
219 break;
220 }
221 }
222
223 if (resolver_debug)
224 zlog_debug("[%p] Resolved with %d results", query, (int)i);
225
226 callback(query, NULL, i, &addr[0]);
227 }
228
229 static void resolver_cb_literal(struct thread *t)
230 {
231 struct resolver_query *query = THREAD_ARG(t);
232 void (*callback)(struct resolver_query *, const char *, int,
233 union sockunion *);
234
235 callback = query->callback;
236 query->callback = NULL;
237
238 callback(query, ARES_SUCCESS, 1, &query->literal_addr);
239 }
240
241 void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
242 const char *hostname,
243 void (*callback)(struct resolver_query *, const char *,
244 int, union sockunion *))
245 {
246 int ret;
247
248 if (hostname == NULL)
249 return;
250
251 if (query->callback != NULL) {
252 flog_err(
253 EC_LIB_RESOLVER,
254 "Trying to resolve '%s', but previous query was not finished yet",
255 hostname);
256 return;
257 }
258
259 query->callback = callback;
260 query->literal_cb = NULL;
261
262 ret = str2sockunion(hostname, &query->literal_addr);
263 if (ret == 0) {
264 if (resolver_debug)
265 zlog_debug("[%p] Resolving '%s' (IP literal)",
266 query, hostname);
267
268 /* for consistency with proper name lookup, don't call the
269 * callback immediately; defer to thread loop
270 */
271 thread_add_timer_msec(state.master, resolver_cb_literal,
272 query, 0, &query->literal_cb);
273 return;
274 }
275
276 if (resolver_debug)
277 zlog_debug("[%p] Resolving '%s'", query, hostname);
278
279 ret = vrf_switch_to_netns(vrf_id);
280 if (ret < 0) {
281 flog_err_sys(EC_LIB_SOCKET, "%s: Can't switch to VRF %u (%s)",
282 __func__, vrf_id, safe_strerror(errno));
283 return;
284 }
285 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
286 ret = vrf_switchback_to_initial();
287 if (ret < 0)
288 flog_err_sys(EC_LIB_SOCKET,
289 "%s: Can't switchback from VRF %u (%s)", __func__,
290 vrf_id, safe_strerror(errno));
291 resolver_update_timeouts(&state);
292 }
293
294 DEFUN(debug_resolver,
295 debug_resolver_cmd,
296 "[no] debug resolver",
297 NO_STR
298 DEBUG_STR
299 "Debug DNS resolver actions\n")
300 {
301 resolver_debug = (argc == 2);
302 return CMD_SUCCESS;
303 }
304
305 static int resolver_config_write_debug(struct vty *vty);
306 static struct cmd_node resolver_debug_node = {
307 .name = "resolver debug",
308 .node = RESOLVER_DEBUG_NODE,
309 .prompt = "",
310 .config_write = resolver_config_write_debug,
311 };
312
313 static int resolver_config_write_debug(struct vty *vty)
314 {
315 if (resolver_debug)
316 vty_out(vty, "debug resolver\n");
317 return 1;
318 }
319
320
321 void resolver_init(struct thread_master *tm)
322 {
323 struct ares_options ares_opts;
324
325 state.master = tm;
326
327 ares_opts = (struct ares_options){
328 .sock_state_cb = &ares_socket_cb,
329 .sock_state_cb_data = &state,
330 .timeout = 2,
331 .tries = 3,
332 };
333
334 ares_init_options(&state.channel, &ares_opts,
335 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
336 | ARES_OPT_TRIES);
337
338 install_node(&resolver_debug_node);
339 install_element(CONFIG_NODE, &debug_resolver_cmd);
340 install_element(ENABLE_NODE, &debug_resolver_cmd);
341 }