1 /* C-Ares integration to Quagga mainloop
2 * Copyright (c) 2014-2015 Timo Teräs
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.
15 #include <ares_version.h>
20 #include "lib_errors.h"
28 struct resolver_state
{
30 struct thread_master
*master
;
31 struct thread
*timeout
;
34 static struct resolver_state state
;
35 static bool resolver_debug
;
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.
42 * Either way c-ares makes that decision and we just need to deal with
43 * whatever FDs it gives us.
46 DEFINE_MTYPE_STATIC(LIB
, ARES_FD
, "c-ares (DNS) file descriptor information");
47 PREDECL_HASH(resolver_fds
);
50 struct resolver_fds_item itm
;
53 struct resolver_state
*state
;
54 struct thread
*t_read
, *t_write
;
57 static int resolver_fd_cmp(const struct resolver_fd
*a
,
58 const struct resolver_fd
*b
)
60 return numcmp(a
->fd
, b
->fd
);
63 static uint32_t resolver_fd_hash(const struct resolver_fd
*item
)
65 return jhash_1word(item
->fd
, 0xacd04c9e);
68 DECLARE_HASH(resolver_fds
, struct resolver_fd
, itm
, resolver_fd_cmp
,
71 static struct resolver_fds_head resfds
[1] = {INIT_HASH(resfds
[0])};
73 static struct resolver_fd
*resolver_fd_get(int fd
,
74 struct resolver_state
*newstate
)
76 struct resolver_fd ref
= {.fd
= fd
}, *res
;
78 res
= resolver_fds_find(resfds
, &ref
);
79 if (!res
&& newstate
) {
80 res
= XCALLOC(MTYPE_ARES_FD
, sizeof(*res
));
82 res
->state
= newstate
;
83 resolver_fds_add(resfds
, res
);
86 zlog_debug("c-ares registered FD %d", fd
);
91 static void resolver_fd_drop_maybe(struct resolver_fd
*resfd
)
93 if (resfd
->t_read
|| resfd
->t_write
)
97 zlog_debug("c-ares unregistered FD %d", resfd
->fd
);
99 resolver_fds_del(resfds
, resfd
);
100 XFREE(MTYPE_ARES_FD
, resfd
);
103 /* end of FD housekeeping */
105 static void resolver_update_timeouts(struct resolver_state
*r
);
107 static int resolver_cb_timeout(struct thread
*t
)
109 struct resolver_state
*r
= THREAD_ARG(t
);
111 ares_process(r
->channel
, NULL
, NULL
);
112 resolver_update_timeouts(r
);
117 static int resolver_cb_socket_readable(struct thread
*t
)
119 struct resolver_fd
*resfd
= THREAD_ARG(t
);
120 struct resolver_state
*r
= resfd
->state
;
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!
128 ares_process_fd(r
->channel
, resfd
->fd
, ARES_SOCKET_BAD
);
129 resolver_update_timeouts(r
);
134 static int resolver_cb_socket_writable(struct thread
*t
)
136 struct resolver_fd
*resfd
= THREAD_ARG(t
);
137 struct resolver_state
*r
= resfd
->state
;
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!
145 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, resfd
->fd
);
146 resolver_update_timeouts(r
);
151 static void resolver_update_timeouts(struct resolver_state
*r
)
153 struct timeval
*tv
, tvbuf
;
155 THREAD_OFF(r
->timeout
);
156 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
158 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
160 thread_add_timer_msec(r
->master
, resolver_cb_timeout
, r
,
161 timeoutms
, &r
->timeout
);
165 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
168 struct resolver_state
*r
= (struct resolver_state
*)data
;
169 struct resolver_fd
*resfd
;
171 resfd
= resolver_fd_get(fd
, (readable
|| writable
) ? r
: NULL
);
175 assert(resfd
->state
== r
);
178 THREAD_OFF(resfd
->t_read
);
179 else if (!resfd
->t_read
)
180 thread_add_read(r
->master
, resolver_cb_socket_readable
, resfd
,
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
);
189 resolver_fd_drop_maybe(resfd
);
193 static void ares_address_cb(void *arg
, int status
, int timeouts
,
196 struct resolver_query
*query
= (struct resolver_query
*)arg
;
197 union sockunion addr
[16];
198 void (*callback
)(struct resolver_query
*, const char *, int,
202 callback
= query
->callback
;
203 query
->callback
= NULL
;
205 if (status
!= ARES_SUCCESS
) {
207 zlog_debug("[%p] Resolving failed (%s)",
208 query
, ares_strerror(status
));
210 callback(query
, ares_strerror(status
), -1, NULL
);
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
) {
219 memcpy(&addr
[i
].sin
.sin_addr
,
220 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
223 memcpy(&addr
[i
].sin6
.sin6_addr
,
224 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
230 zlog_debug("[%p] Resolved with %d results", query
, (int)i
);
232 callback(query
, NULL
, i
, &addr
[0]);
235 static int resolver_cb_literal(struct thread
*t
)
237 struct resolver_query
*query
= THREAD_ARG(t
);
238 void (*callback
)(struct resolver_query
*, const char *, int,
241 callback
= query
->callback
;
242 query
->callback
= NULL
;
244 callback(query
, ARES_SUCCESS
, 1, &query
->literal_addr
);
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
*))
255 if (query
->callback
!= NULL
) {
258 "Trying to resolve '%s', but previous query was not finished yet",
263 query
->callback
= callback
;
264 query
->literal_cb
= NULL
;
266 ret
= str2sockunion(hostname
, &query
->literal_addr
);
269 zlog_debug("[%p] Resolving '%s' (IP literal)",
272 /* for consistency with proper name lookup, don't call the
273 * callback immediately; defer to thread loop
275 thread_add_timer_msec(state
.master
, resolver_cb_literal
,
276 query
, 0, &query
->literal_cb
);
281 zlog_debug("[%p] Resolving '%s'", query
, hostname
);
283 ret
= vrf_switch_to_netns(vrf_id
);
285 flog_err_sys(EC_LIB_SOCKET
, "%s: Can't switch to VRF %u (%s)",
286 __func__
, vrf_id
, safe_strerror(errno
));
289 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
290 ret
= vrf_switchback_to_initial();
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
);
298 DEFUN(debug_resolver
,
300 "[no] debug resolver",
303 "Debug DNS resolver actions\n")
305 resolver_debug
= (argc
== 2);
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
,
314 .config_write
= resolver_config_write_debug
,
317 static int resolver_config_write_debug(struct vty
*vty
)
320 vty_out(vty
, "debug resolver\n");
325 void resolver_init(struct thread_master
*tm
)
327 struct ares_options ares_opts
;
331 ares_opts
= (struct ares_options
){
332 .sock_state_cb
= &ares_socket_cb
,
333 .sock_state_cb_data
= &state
,
338 ares_init_options(&state
.channel
, &ares_opts
,
339 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
342 install_node(&resolver_debug_node
);
343 install_element(CONFIG_NODE
, &debug_resolver_cmd
);
344 install_element(ENABLE_NODE
, &debug_resolver_cmd
);