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>
19 #include "lib_errors.h"
26 struct resolver_state
{
28 struct thread_master
*master
;
29 struct thread
*timeout
;
30 vector read_threads
, write_threads
;
33 static struct resolver_state state
;
34 static bool resolver_debug
;
36 #define THREAD_RUNNING ((struct thread *)-1)
38 static void resolver_update_timeouts(struct resolver_state
*r
);
40 static int resolver_cb_timeout(struct thread
*t
)
42 struct resolver_state
*r
= THREAD_ARG(t
);
44 r
->timeout
= THREAD_RUNNING
;
45 ares_process(r
->channel
, NULL
, NULL
);
47 resolver_update_timeouts(r
);
52 static int resolver_cb_socket_readable(struct thread
*t
)
54 struct resolver_state
*r
= THREAD_ARG(t
);
55 int fd
= THREAD_FD(t
);
57 vector_set_index(r
->read_threads
, fd
, THREAD_RUNNING
);
58 ares_process_fd(r
->channel
, fd
, ARES_SOCKET_BAD
);
59 if (vector_lookup(r
->read_threads
, fd
) == THREAD_RUNNING
) {
61 thread_add_read(r
->master
, resolver_cb_socket_readable
, r
, fd
,
63 vector_set_index(r
->read_threads
, fd
, t
);
65 resolver_update_timeouts(r
);
70 static int resolver_cb_socket_writable(struct thread
*t
)
72 struct resolver_state
*r
= THREAD_ARG(t
);
73 int fd
= THREAD_FD(t
);
75 vector_set_index(r
->write_threads
, fd
, THREAD_RUNNING
);
76 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, fd
);
77 if (vector_lookup(r
->write_threads
, fd
) == THREAD_RUNNING
) {
79 thread_add_write(r
->master
, resolver_cb_socket_writable
, r
, fd
,
81 vector_set_index(r
->write_threads
, fd
, t
);
83 resolver_update_timeouts(r
);
88 static void resolver_update_timeouts(struct resolver_state
*r
)
90 struct timeval
*tv
, tvbuf
;
92 if (r
->timeout
== THREAD_RUNNING
)
95 THREAD_OFF(r
->timeout
);
96 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
98 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
99 thread_add_timer_msec(r
->master
, resolver_cb_timeout
, r
,
100 timeoutms
, &r
->timeout
);
104 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
107 struct resolver_state
*r
= (struct resolver_state
*)data
;
111 t
= vector_lookup_ensure(r
->read_threads
, fd
);
113 thread_add_read(r
->master
, resolver_cb_socket_readable
,
115 vector_set_index(r
->read_threads
, fd
, t
);
118 t
= vector_lookup(r
->read_threads
, fd
);
120 if (t
!= THREAD_RUNNING
) {
123 vector_unset(r
->read_threads
, fd
);
128 t
= vector_lookup_ensure(r
->write_threads
, fd
);
130 thread_add_read(r
->master
, resolver_cb_socket_writable
,
132 vector_set_index(r
->write_threads
, fd
, t
);
135 t
= vector_lookup(r
->write_threads
, fd
);
137 if (t
!= THREAD_RUNNING
) {
140 vector_unset(r
->write_threads
, fd
);
146 static void ares_address_cb(void *arg
, int status
, int timeouts
,
149 struct resolver_query
*query
= (struct resolver_query
*)arg
;
150 union sockunion addr
[16];
151 void (*callback
)(struct resolver_query
*, const char *, int,
155 callback
= query
->callback
;
156 query
->callback
= NULL
;
158 if (status
!= ARES_SUCCESS
) {
160 zlog_debug("[%p] Resolving failed (%s)",
161 query
, ares_strerror(status
));
163 callback(query
, ares_strerror(status
), -1, NULL
);
167 for (i
= 0; i
< array_size(addr
) && he
->h_addr_list
[i
] != NULL
; i
++) {
168 memset(&addr
[i
], 0, sizeof(addr
[i
]));
169 addr
[i
].sa
.sa_family
= he
->h_addrtype
;
170 switch (he
->h_addrtype
) {
172 memcpy(&addr
[i
].sin
.sin_addr
,
173 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
176 memcpy(&addr
[i
].sin6
.sin6_addr
,
177 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
183 zlog_debug("[%p] Resolved with %d results", query
, (int)i
);
185 callback(query
, NULL
, i
, &addr
[0]);
188 static int resolver_cb_literal(struct thread
*t
)
190 struct resolver_query
*query
= THREAD_ARG(t
);
191 void (*callback
)(struct resolver_query
*, const char *, int,
194 callback
= query
->callback
;
195 query
->callback
= NULL
;
197 callback(query
, ARES_SUCCESS
, 1, &query
->literal_addr
);
201 void resolver_resolve(struct resolver_query
*query
, int af
,
202 const char *hostname
,
203 void (*callback
)(struct resolver_query
*, const char *,
204 int, union sockunion
*))
208 if (query
->callback
!= NULL
) {
211 "Trying to resolve '%s', but previous query was not finished yet",
216 query
->callback
= callback
;
217 query
->literal_cb
= NULL
;
219 ret
= str2sockunion(hostname
, &query
->literal_addr
);
222 zlog_debug("[%p] Resolving '%s' (IP literal)",
225 /* for consistency with proper name lookup, don't call the
226 * callback immediately; defer to thread loop
228 thread_add_timer_msec(state
.master
, resolver_cb_literal
,
229 query
, 0, &query
->literal_cb
);
234 zlog_debug("[%p] Resolving '%s'", query
, hostname
);
236 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
237 resolver_update_timeouts(&state
);
240 DEFUN(debug_resolver
,
242 "[no] debug resolver",
245 "Debug DNS resolver actions\n")
247 resolver_debug
= (argc
== 2);
251 static int resolver_config_write_debug(struct vty
*vty
);
252 static struct cmd_node resolver_debug_node
= {
253 .name
= "resolver debug",
254 .node
= RESOLVER_DEBUG_NODE
,
256 .config_write
= resolver_config_write_debug
,
259 static int resolver_config_write_debug(struct vty
*vty
)
262 vty_out(vty
, "debug resolver\n");
267 void resolver_init(struct thread_master
*tm
)
269 struct ares_options ares_opts
;
272 state
.read_threads
= vector_init(1);
273 state
.write_threads
= vector_init(1);
275 ares_opts
= (struct ares_options
){
276 .sock_state_cb
= &ares_socket_cb
,
277 .sock_state_cb_data
= &state
,
282 ares_init_options(&state
.channel
, &ares_opts
,
283 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
286 install_node(&resolver_debug_node
);
287 install_element(CONFIG_NODE
, &debug_resolver_cmd
);
288 install_element(ENABLE_NODE
, &debug_resolver_cmd
);