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 void 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
);
115 static void resolver_cb_socket_readable(struct thread
*t
)
117 struct resolver_fd
*resfd
= THREAD_ARG(t
);
118 struct resolver_state
*r
= resfd
->state
;
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!
126 ares_process_fd(r
->channel
, resfd
->fd
, ARES_SOCKET_BAD
);
127 resolver_update_timeouts(r
);
130 static void resolver_cb_socket_writable(struct thread
*t
)
132 struct resolver_fd
*resfd
= THREAD_ARG(t
);
133 struct resolver_state
*r
= resfd
->state
;
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!
141 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, resfd
->fd
);
142 resolver_update_timeouts(r
);
145 static void resolver_update_timeouts(struct resolver_state
*r
)
147 struct timeval
*tv
, tvbuf
;
149 THREAD_OFF(r
->timeout
);
150 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
152 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
154 thread_add_timer_msec(r
->master
, resolver_cb_timeout
, r
,
155 timeoutms
, &r
->timeout
);
159 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
162 struct resolver_state
*r
= (struct resolver_state
*)data
;
163 struct resolver_fd
*resfd
;
165 resfd
= resolver_fd_get(fd
, (readable
|| writable
) ? r
: NULL
);
169 assert(resfd
->state
== r
);
172 THREAD_OFF(resfd
->t_read
);
173 else if (!resfd
->t_read
)
174 thread_add_read(r
->master
, resolver_cb_socket_readable
, resfd
,
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
);
183 resolver_fd_drop_maybe(resfd
);
187 static void ares_address_cb(void *arg
, int status
, int timeouts
,
190 struct resolver_query
*query
= (struct resolver_query
*)arg
;
191 union sockunion addr
[16];
192 void (*callback
)(struct resolver_query
*, const char *, int,
196 callback
= query
->callback
;
197 query
->callback
= NULL
;
199 if (status
!= ARES_SUCCESS
) {
201 zlog_debug("[%p] Resolving failed (%s)",
202 query
, ares_strerror(status
));
204 callback(query
, ares_strerror(status
), -1, NULL
);
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
) {
213 memcpy(&addr
[i
].sin
.sin_addr
,
214 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
217 memcpy(&addr
[i
].sin6
.sin6_addr
,
218 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
224 zlog_debug("[%p] Resolved with %d results", query
, (int)i
);
226 callback(query
, NULL
, i
, &addr
[0]);
229 static void resolver_cb_literal(struct thread
*t
)
231 struct resolver_query
*query
= THREAD_ARG(t
);
232 void (*callback
)(struct resolver_query
*, const char *, int,
235 callback
= query
->callback
;
236 query
->callback
= NULL
;
238 callback(query
, ARES_SUCCESS
, 1, &query
->literal_addr
);
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
*))
248 if (hostname
== NULL
)
251 if (query
->callback
!= NULL
) {
254 "Trying to resolve '%s', but previous query was not finished yet",
259 query
->callback
= callback
;
260 query
->literal_cb
= NULL
;
262 ret
= str2sockunion(hostname
, &query
->literal_addr
);
265 zlog_debug("[%p] Resolving '%s' (IP literal)",
268 /* for consistency with proper name lookup, don't call the
269 * callback immediately; defer to thread loop
271 thread_add_timer_msec(state
.master
, resolver_cb_literal
,
272 query
, 0, &query
->literal_cb
);
277 zlog_debug("[%p] Resolving '%s'", query
, hostname
);
279 ret
= vrf_switch_to_netns(vrf_id
);
281 flog_err_sys(EC_LIB_SOCKET
, "%s: Can't switch to VRF %u (%s)",
282 __func__
, vrf_id
, safe_strerror(errno
));
285 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
286 ret
= vrf_switchback_to_initial();
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
);
294 DEFUN(debug_resolver
,
296 "[no] debug resolver",
299 "Debug DNS resolver actions\n")
301 resolver_debug
= (argc
== 2);
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
,
310 .config_write
= resolver_config_write_debug
,
313 static int resolver_config_write_debug(struct vty
*vty
)
316 vty_out(vty
, "debug resolver\n");
321 void resolver_init(struct thread_master
*tm
)
323 struct ares_options ares_opts
;
327 ares_opts
= (struct ares_options
){
328 .sock_state_cb
= &ares_socket_cb
,
329 .sock_state_cb_data
= &state
,
334 ares_init_options(&state
.channel
, &ares_opts
,
335 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
338 install_node(&resolver_debug_node
);
339 install_element(CONFIG_NODE
, &debug_resolver_cmd
);
340 install_element(ENABLE_NODE
, &debug_resolver_cmd
);