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"
23 struct resolver_state
{
25 struct thread_master
*master
;
26 struct thread
*timeout
;
27 vector read_threads
, write_threads
;
30 static struct resolver_state state
;
31 static bool resolver_debug
;
33 #define THREAD_RUNNING ((struct thread *)-1)
35 static void resolver_update_timeouts(struct resolver_state
*r
);
37 static int resolver_cb_timeout(struct thread
*t
)
39 struct resolver_state
*r
= THREAD_ARG(t
);
41 r
->timeout
= THREAD_RUNNING
;
42 ares_process(r
->channel
, NULL
, NULL
);
44 resolver_update_timeouts(r
);
49 static int resolver_cb_socket_readable(struct thread
*t
)
51 struct resolver_state
*r
= THREAD_ARG(t
);
52 int fd
= THREAD_FD(t
);
54 vector_set_index(r
->read_threads
, fd
, THREAD_RUNNING
);
55 ares_process_fd(r
->channel
, fd
, ARES_SOCKET_BAD
);
56 if (vector_lookup(r
->read_threads
, fd
) == THREAD_RUNNING
) {
58 thread_add_read(r
->master
, resolver_cb_socket_readable
, r
, fd
,
60 vector_set_index(r
->read_threads
, fd
, t
);
62 resolver_update_timeouts(r
);
67 static int resolver_cb_socket_writable(struct thread
*t
)
69 struct resolver_state
*r
= THREAD_ARG(t
);
70 int fd
= THREAD_FD(t
);
72 vector_set_index(r
->write_threads
, fd
, THREAD_RUNNING
);
73 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, fd
);
74 if (vector_lookup(r
->write_threads
, fd
) == THREAD_RUNNING
) {
76 thread_add_write(r
->master
, resolver_cb_socket_writable
, r
, fd
,
78 vector_set_index(r
->write_threads
, fd
, t
);
80 resolver_update_timeouts(r
);
85 static void resolver_update_timeouts(struct resolver_state
*r
)
87 struct timeval
*tv
, tvbuf
;
89 if (r
->timeout
== THREAD_RUNNING
)
92 THREAD_OFF(r
->timeout
);
93 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
95 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
96 thread_add_timer_msec(r
->master
, resolver_cb_timeout
, r
,
97 timeoutms
, &r
->timeout
);
101 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
104 struct resolver_state
*r
= (struct resolver_state
*)data
;
108 t
= vector_lookup_ensure(r
->read_threads
, fd
);
110 thread_add_read(r
->master
, resolver_cb_socket_readable
,
112 vector_set_index(r
->read_threads
, fd
, t
);
115 t
= vector_lookup(r
->read_threads
, fd
);
117 if (t
!= THREAD_RUNNING
) {
120 vector_unset(r
->read_threads
, fd
);
125 t
= vector_lookup_ensure(r
->write_threads
, fd
);
127 thread_add_read(r
->master
, resolver_cb_socket_writable
,
129 vector_set_index(r
->write_threads
, fd
, t
);
132 t
= vector_lookup(r
->write_threads
, fd
);
134 if (t
!= THREAD_RUNNING
) {
137 vector_unset(r
->write_threads
, fd
);
143 static void ares_address_cb(void *arg
, int status
, int timeouts
,
146 struct resolver_query
*query
= (struct resolver_query
*)arg
;
147 union sockunion addr
[16];
148 void (*callback
)(struct resolver_query
*, const char *, int,
152 callback
= query
->callback
;
153 query
->callback
= NULL
;
155 if (status
!= ARES_SUCCESS
) {
157 zlog_debug("[%p] Resolving failed (%s)",
158 query
, ares_strerror(status
));
160 callback(query
, ares_strerror(status
), -1, NULL
);
164 for (i
= 0; i
< array_size(addr
) && he
->h_addr_list
[i
] != NULL
; i
++) {
165 memset(&addr
[i
], 0, sizeof(addr
[i
]));
166 addr
[i
].sa
.sa_family
= he
->h_addrtype
;
167 switch (he
->h_addrtype
) {
169 memcpy(&addr
[i
].sin
.sin_addr
,
170 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
173 memcpy(&addr
[i
].sin6
.sin6_addr
,
174 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
180 zlog_debug("[%p] Resolved with %d results", query
, (int)i
);
182 callback(query
, NULL
, i
, &addr
[0]);
185 static int resolver_cb_literal(struct thread
*t
)
187 struct resolver_query
*query
= THREAD_ARG(t
);
188 void (*callback
)(struct resolver_query
*, const char *, int,
191 callback
= query
->callback
;
192 query
->callback
= NULL
;
194 callback(query
, ARES_SUCCESS
, 1, &query
->literal_addr
);
198 void resolver_resolve(struct resolver_query
*query
, int af
,
199 const char *hostname
,
200 void (*callback
)(struct resolver_query
*, const char *,
201 int, union sockunion
*))
205 if (query
->callback
!= NULL
) {
208 "Trying to resolve '%s', but previous query was not finished yet",
213 query
->callback
= callback
;
214 query
->literal_cb
= NULL
;
216 ret
= str2sockunion(hostname
, &query
->literal_addr
);
219 zlog_debug("[%p] Resolving '%s' (IP literal)",
222 /* for consistency with proper name lookup, don't call the
223 * callback immediately; defer to thread loop
225 thread_add_timer_msec(state
.master
, resolver_cb_literal
,
226 query
, 0, &query
->literal_cb
);
231 zlog_debug("[%p] Resolving '%s'", query
, hostname
);
233 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
234 resolver_update_timeouts(&state
);
237 DEFUN(debug_resolver
,
239 "[no] debug resolver",
242 "Debug DNS resolver actions\n")
244 resolver_debug
= (argc
== 2);
248 static struct cmd_node resolver_debug_node
= {RESOLVER_DEBUG_NODE
, "", 1};
250 static int resolver_config_write_debug(struct vty
*vty
)
253 vty_out(vty
, "debug resolver\n");
258 void resolver_init(struct thread_master
*tm
)
260 struct ares_options ares_opts
;
263 state
.read_threads
= vector_init(1);
264 state
.write_threads
= vector_init(1);
266 ares_opts
= (struct ares_options
){
267 .sock_state_cb
= &ares_socket_cb
,
268 .sock_state_cb_data
= &state
,
273 ares_init_options(&state
.channel
, &ares_opts
,
274 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
277 install_node(&resolver_debug_node
, resolver_config_write_debug
);
278 install_element(CONFIG_NODE
, &debug_resolver_cmd
);
279 install_element(ENABLE_NODE
, &debug_resolver_cmd
);