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"
27 struct resolver_state
{
29 struct thread_master
*master
;
30 struct thread
*timeout
;
33 static struct resolver_state state
;
34 static bool resolver_debug
;
36 /* a FD doesn't necessarily map 1:1 to a request; we could be talking to
37 * multiple caches simultaneously, to see which responds fastest.
38 * Theoretically we could also be using the same fd for multiple lookups,
39 * but the c-ares API guarantees an n:1 mapping for fd => channel.
41 * Either way c-ares makes that decision and we just need to deal with
42 * whatever FDs it gives us.
45 DEFINE_MTYPE_STATIC(LIB
, ARES_FD
, "c-ares (DNS) file descriptor information");
46 PREDECL_HASH(resolver_fds
);
49 struct resolver_fds_item itm
;
52 struct resolver_state
*state
;
53 struct thread
*t_read
, *t_write
;
56 static int resolver_fd_cmp(const struct resolver_fd
*a
,
57 const struct resolver_fd
*b
)
59 return numcmp(a
->fd
, b
->fd
);
62 static uint32_t resolver_fd_hash(const struct resolver_fd
*item
)
64 return jhash_1word(item
->fd
, 0xacd04c9e);
67 DECLARE_HASH(resolver_fds
, struct resolver_fd
, itm
, resolver_fd_cmp
,
70 static struct resolver_fds_head resfds
[1] = {INIT_HASH(resfds
[0])};
72 static struct resolver_fd
*resolver_fd_get(int fd
,
73 struct resolver_state
*newstate
)
75 struct resolver_fd ref
= {.fd
= fd
}, *res
;
77 res
= resolver_fds_find(resfds
, &ref
);
78 if (!res
&& newstate
) {
79 res
= XCALLOC(MTYPE_ARES_FD
, sizeof(*res
));
81 res
->state
= newstate
;
82 resolver_fds_add(resfds
, res
);
85 zlog_debug("c-ares registered FD %d", fd
);
90 static void resolver_fd_drop_maybe(struct resolver_fd
*resfd
)
92 if (resfd
->t_read
|| resfd
->t_write
)
96 zlog_debug("c-ares unregistered FD %d", resfd
->fd
);
98 resolver_fds_del(resfds
, resfd
);
99 XFREE(MTYPE_ARES_FD
, resfd
);
102 /* end of FD housekeeping */
104 static void resolver_update_timeouts(struct resolver_state
*r
);
106 static int resolver_cb_timeout(struct thread
*t
)
108 struct resolver_state
*r
= THREAD_ARG(t
);
110 ares_process(r
->channel
, NULL
, NULL
);
111 resolver_update_timeouts(r
);
116 static int resolver_cb_socket_readable(struct thread
*t
)
118 struct resolver_fd
*resfd
= THREAD_ARG(t
);
119 struct resolver_state
*r
= resfd
->state
;
121 thread_add_read(r
->master
, resolver_cb_socket_readable
, resfd
,
122 resfd
->fd
, &resfd
->t_read
);
123 /* ^ ordering important:
124 * ares_process_fd may transitively call THREAD_OFF(resfd->t_read)
125 * combined with resolver_fd_drop_maybe, so resfd may be free'd after!
127 ares_process_fd(r
->channel
, resfd
->fd
, ARES_SOCKET_BAD
);
128 resolver_update_timeouts(r
);
133 static int resolver_cb_socket_writable(struct thread
*t
)
135 struct resolver_fd
*resfd
= THREAD_ARG(t
);
136 struct resolver_state
*r
= resfd
->state
;
138 thread_add_write(r
->master
, resolver_cb_socket_writable
, resfd
,
139 resfd
->fd
, &resfd
->t_write
);
140 /* ^ ordering important:
141 * ares_process_fd may transitively call THREAD_OFF(resfd->t_write)
142 * combined with resolver_fd_drop_maybe, so resfd may be free'd after!
144 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, resfd
->fd
);
145 resolver_update_timeouts(r
);
150 static void resolver_update_timeouts(struct resolver_state
*r
)
152 struct timeval
*tv
, tvbuf
;
154 THREAD_OFF(r
->timeout
);
155 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
157 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
159 thread_add_timer_msec(r
->master
, resolver_cb_timeout
, r
,
160 timeoutms
, &r
->timeout
);
164 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
167 struct resolver_state
*r
= (struct resolver_state
*)data
;
168 struct resolver_fd
*resfd
;
170 resfd
= resolver_fd_get(fd
, (readable
|| writable
) ? r
: NULL
);
174 assert(resfd
->state
== r
);
177 THREAD_OFF(resfd
->t_read
);
178 else if (!resfd
->t_read
)
179 thread_add_read(r
->master
, resolver_cb_socket_readable
, resfd
,
183 THREAD_OFF(resfd
->t_write
);
184 else if (!resfd
->t_write
)
185 thread_add_write(r
->master
, resolver_cb_socket_writable
, resfd
,
186 fd
, &resfd
->t_write
);
188 resolver_fd_drop_maybe(resfd
);
192 static void ares_address_cb(void *arg
, int status
, int timeouts
,
195 struct resolver_query
*query
= (struct resolver_query
*)arg
;
196 union sockunion addr
[16];
197 void (*callback
)(struct resolver_query
*, const char *, int,
201 callback
= query
->callback
;
202 query
->callback
= NULL
;
204 if (status
!= ARES_SUCCESS
) {
206 zlog_debug("[%p] Resolving failed (%s)",
207 query
, ares_strerror(status
));
209 callback(query
, ares_strerror(status
), -1, NULL
);
213 for (i
= 0; i
< array_size(addr
) && he
->h_addr_list
[i
] != NULL
; i
++) {
214 memset(&addr
[i
], 0, sizeof(addr
[i
]));
215 addr
[i
].sa
.sa_family
= he
->h_addrtype
;
216 switch (he
->h_addrtype
) {
218 memcpy(&addr
[i
].sin
.sin_addr
,
219 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
222 memcpy(&addr
[i
].sin6
.sin6_addr
,
223 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
229 zlog_debug("[%p] Resolved with %d results", query
, (int)i
);
231 callback(query
, NULL
, i
, &addr
[0]);
234 static int resolver_cb_literal(struct thread
*t
)
236 struct resolver_query
*query
= THREAD_ARG(t
);
237 void (*callback
)(struct resolver_query
*, const char *, int,
240 callback
= query
->callback
;
241 query
->callback
= NULL
;
243 callback(query
, ARES_SUCCESS
, 1, &query
->literal_addr
);
247 void resolver_resolve(struct resolver_query
*query
, int af
,
248 const char *hostname
,
249 void (*callback
)(struct resolver_query
*, const char *,
250 int, union sockunion
*))
254 if (query
->callback
!= NULL
) {
257 "Trying to resolve '%s', but previous query was not finished yet",
262 query
->callback
= callback
;
263 query
->literal_cb
= NULL
;
265 ret
= str2sockunion(hostname
, &query
->literal_addr
);
268 zlog_debug("[%p] Resolving '%s' (IP literal)",
271 /* for consistency with proper name lookup, don't call the
272 * callback immediately; defer to thread loop
274 thread_add_timer_msec(state
.master
, resolver_cb_literal
,
275 query
, 0, &query
->literal_cb
);
280 zlog_debug("[%p] Resolving '%s'", query
, hostname
);
282 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
283 resolver_update_timeouts(&state
);
286 DEFUN(debug_resolver
,
288 "[no] debug resolver",
291 "Debug DNS resolver actions\n")
293 resolver_debug
= (argc
== 2);
297 static int resolver_config_write_debug(struct vty
*vty
);
298 static struct cmd_node resolver_debug_node
= {
299 .name
= "resolver debug",
300 .node
= RESOLVER_DEBUG_NODE
,
302 .config_write
= resolver_config_write_debug
,
305 static int resolver_config_write_debug(struct vty
*vty
)
308 vty_out(vty
, "debug resolver\n");
313 void resolver_init(struct thread_master
*tm
)
315 struct ares_options ares_opts
;
319 ares_opts
= (struct ares_options
){
320 .sock_state_cb
= &ares_socket_cb
,
321 .sock_state_cb_data
= &state
,
326 ares_init_options(&state
.channel
, &ares_opts
,
327 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
330 install_node(&resolver_debug_node
);
331 install_element(CONFIG_NODE
, &debug_resolver_cmd
);
332 install_element(ENABLE_NODE
, &debug_resolver_cmd
);