]> git.proxmox.com Git - mirror_frr.git/blame - lib/resolver.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / resolver.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
2fb975da
TT
2/* C-Ares integration to Quagga mainloop
3 * Copyright (c) 2014-2015 Timo Teräs
2fb975da
TT
4 */
5
b45ac5f5
DL
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif
9
2fb975da
TT
10#include <ares.h>
11#include <ares_version.h>
12
faf079ff
DL
13#include "typesafe.h"
14#include "jhash.h"
24a58196 15#include "frrevent.h"
aed07011 16#include "lib_errors.h"
fe9e7b71
DL
17#include "resolver.h"
18#include "command.h"
b2fa8c0f 19#include "xref.h"
c742573b 20#include "vrf.h"
b2fa8c0f 21
80413c20 22XREF_SETUP();
2fb975da
TT
23
24struct resolver_state {
25 ares_channel channel;
cd9d0537 26 struct event_loop *master;
e6685141 27 struct event *timeout;
2fb975da
TT
28};
29
30static struct resolver_state state;
fe9e7b71 31static bool resolver_debug;
2fb975da 32
faf079ff
DL
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
42DEFINE_MTYPE_STATIC(LIB, ARES_FD, "c-ares (DNS) file descriptor information");
43PREDECL_HASH(resolver_fds);
44
45struct resolver_fd {
46 struct resolver_fds_item itm;
47
48 int fd;
49 struct resolver_state *state;
e6685141 50 struct event *t_read, *t_write;
faf079ff
DL
51};
52
53static 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
59static uint32_t resolver_fd_hash(const struct resolver_fd *item)
60{
61 return jhash_1word(item->fd, 0xacd04c9e);
62}
63
64DECLARE_HASH(resolver_fds, struct resolver_fd, itm, resolver_fd_cmp,
65 resolver_fd_hash);
66
67static struct resolver_fds_head resfds[1] = {INIT_HASH(resfds[0])};
68
69static 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
87static 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 */
2fb975da
TT
100
101static void resolver_update_timeouts(struct resolver_state *r);
102
e6685141 103static void resolver_cb_timeout(struct event *t)
2fb975da 104{
e16d030c 105 struct resolver_state *r = EVENT_ARG(t);
2fb975da 106
2fb975da 107 ares_process(r->channel, NULL, NULL);
2fb975da 108 resolver_update_timeouts(r);
2fb975da
TT
109}
110
e6685141 111static void resolver_cb_socket_readable(struct event *t)
2fb975da 112{
e16d030c 113 struct resolver_fd *resfd = EVENT_ARG(t);
faf079ff
DL
114 struct resolver_state *r = resfd->state;
115
907a2395
DS
116 event_add_read(r->master, resolver_cb_socket_readable, resfd, resfd->fd,
117 &resfd->t_read);
faf079ff 118 /* ^ ordering important:
e16d030c 119 * ares_process_fd may transitively call EVENT_OFF(resfd->t_read)
faf079ff
DL
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);
2fb975da 123 resolver_update_timeouts(r);
2fb975da
TT
124}
125
e6685141 126static void resolver_cb_socket_writable(struct event *t)
2fb975da 127{
e16d030c 128 struct resolver_fd *resfd = EVENT_ARG(t);
faf079ff
DL
129 struct resolver_state *r = resfd->state;
130
907a2395
DS
131 event_add_write(r->master, resolver_cb_socket_writable, resfd,
132 resfd->fd, &resfd->t_write);
faf079ff 133 /* ^ ordering important:
e16d030c 134 * ares_process_fd may transitively call EVENT_OFF(resfd->t_write)
faf079ff
DL
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);
2fb975da 138 resolver_update_timeouts(r);
2fb975da
TT
139}
140
141static void resolver_update_timeouts(struct resolver_state *r)
142{
143 struct timeval *tv, tvbuf;
144
e16d030c 145 EVENT_OFF(r->timeout);
2fb975da
TT
146 tv = ares_timeout(r->channel, NULL, &tvbuf);
147 if (tv) {
148 unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
faf079ff 149
907a2395
DS
150 event_add_timer_msec(r->master, resolver_cb_timeout, r,
151 timeoutms, &r->timeout);
2fb975da
TT
152 }
153}
154
996c9314
LB
155static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
156 int writable)
2fb975da 157{
996c9314 158 struct resolver_state *r = (struct resolver_state *)data;
faf079ff 159 struct resolver_fd *resfd;
2fb975da 160
faf079ff
DL
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)
e16d030c 168 EVENT_OFF(resfd->t_read);
faf079ff 169 else if (!resfd->t_read)
907a2395
DS
170 event_add_read(r->master, resolver_cb_socket_readable, resfd,
171 fd, &resfd->t_read);
faf079ff
DL
172
173 if (!writable)
e16d030c 174 EVENT_OFF(resfd->t_write);
faf079ff 175 else if (!resfd->t_write)
907a2395
DS
176 event_add_write(r->master, resolver_cb_socket_writable, resfd,
177 fd, &resfd->t_write);
faf079ff
DL
178
179 resolver_fd_drop_maybe(resfd);
2fb975da
TT
180}
181
2fb975da 182
996c9314
LB
183static void ares_address_cb(void *arg, int status, int timeouts,
184 struct hostent *he)
2fb975da 185{
996c9314 186 struct resolver_query *query = (struct resolver_query *)arg;
2fb975da 187 union sockunion addr[16];
3286ca07
DL
188 void (*callback)(struct resolver_query *, const char *, int,
189 union sockunion *);
2fb975da
TT
190 size_t i;
191
50cdb6cf
DL
192 callback = query->callback;
193 query->callback = NULL;
194
2fb975da 195 if (status != ARES_SUCCESS) {
fe9e7b71 196 if (resolver_debug)
3286ca07
DL
197 zlog_debug("[%p] Resolving failed (%s)",
198 query, ares_strerror(status));
fe9e7b71 199
3286ca07 200 callback(query, ares_strerror(status), -1, NULL);
2fb975da
TT
201 return;
202 }
203
7e3a1ec7 204 for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
2fb975da
TT
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:
996c9314
LB
209 memcpy(&addr[i].sin.sin_addr,
210 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
211 break;
212 case AF_INET6:
996c9314
LB
213 memcpy(&addr[i].sin6.sin6_addr,
214 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
215 break;
216 }
217 }
218
fe9e7b71
DL
219 if (resolver_debug)
220 zlog_debug("[%p] Resolved with %d results", query, (int)i);
221
3286ca07 222 callback(query, NULL, i, &addr[0]);
2fb975da
TT
223}
224
e6685141 225static void resolver_cb_literal(struct event *t)
125dc952 226{
e16d030c 227 struct resolver_query *query = EVENT_ARG(t);
3286ca07
DL
228 void (*callback)(struct resolver_query *, const char *, int,
229 union sockunion *);
125dc952
DL
230
231 callback = query->callback;
232 query->callback = NULL;
233
3286ca07 234 callback(query, ARES_SUCCESS, 1, &query->literal_addr);
125dc952
DL
235}
236
c742573b 237void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
996c9314 238 const char *hostname,
3286ca07
DL
239 void (*callback)(struct resolver_query *, const char *,
240 int, union sockunion *))
2fb975da 241{
125dc952
DL
242 int ret;
243
30220d1e 244 if (hostname == NULL)
245 return;
246
2fb975da 247 if (query->callback != NULL) {
1c50c1c0 248 flog_err(
fe9e7b71 249 EC_LIB_RESOLVER,
1c50c1c0
QY
250 "Trying to resolve '%s', but previous query was not finished yet",
251 hostname);
2fb975da
TT
252 return;
253 }
254
125dc952
DL
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 */
907a2395
DS
267 event_add_timer_msec(state.master, resolver_cb_literal, query,
268 0, &query->literal_cb);
125dc952
DL
269 return;
270 }
271
fe9e7b71
DL
272 if (resolver_debug)
273 zlog_debug("[%p] Resolving '%s'", query, hostname);
2fb975da 274
c742573b
PG
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 }
2fb975da 281 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
c742573b
PG
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));
2fb975da
TT
287 resolver_update_timeouts(&state);
288}
fe9e7b71
DL
289
290DEFUN(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
612c2c15 301static int resolver_config_write_debug(struct vty *vty);
62b346ee 302static struct cmd_node resolver_debug_node = {
f4b8291f 303 .name = "resolver debug",
62b346ee
DL
304 .node = RESOLVER_DEBUG_NODE,
305 .prompt = "",
612c2c15 306 .config_write = resolver_config_write_debug,
62b346ee 307};
fe9e7b71
DL
308
309static 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
cd9d0537 317void resolver_init(struct event_loop *tm)
fe9e7b71
DL
318{
319 struct ares_options ares_opts;
320
321 state.master = tm;
fe9e7b71
DL
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
612c2c15 334 install_node(&resolver_debug_node);
fe9e7b71
DL
335 install_element(CONFIG_NODE, &debug_resolver_cmd);
336 install_element(ENABLE_NODE, &debug_resolver_cmd);
337}