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