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.
11 #include <ares_version.h>
15 #include "lib_errors.h"
18 #include "nhrp_errors.h"
20 struct resolver_state
{
22 struct thread
*timeout
;
23 vector read_threads
, write_threads
;
26 static struct resolver_state state
;
28 #define THREAD_RUNNING ((struct thread *)-1)
30 static void resolver_update_timeouts(struct resolver_state
*r
);
32 static int resolver_cb_timeout(struct thread
*t
)
34 struct resolver_state
*r
= THREAD_ARG(t
);
36 r
->timeout
= THREAD_RUNNING
;
37 ares_process(r
->channel
, NULL
, NULL
);
39 resolver_update_timeouts(r
);
44 static int resolver_cb_socket_readable(struct thread
*t
)
46 struct resolver_state
*r
= THREAD_ARG(t
);
47 int fd
= THREAD_FD(t
);
49 vector_set_index(r
->read_threads
, fd
, THREAD_RUNNING
);
50 ares_process_fd(r
->channel
, fd
, ARES_SOCKET_BAD
);
51 if (vector_lookup(r
->read_threads
, fd
) == THREAD_RUNNING
) {
53 thread_add_read(master
, resolver_cb_socket_readable
, r
, fd
, &t
);
54 vector_set_index(r
->read_threads
, fd
, t
);
56 resolver_update_timeouts(r
);
61 static int resolver_cb_socket_writable(struct thread
*t
)
63 struct resolver_state
*r
= THREAD_ARG(t
);
64 int fd
= THREAD_FD(t
);
66 vector_set_index(r
->write_threads
, fd
, THREAD_RUNNING
);
67 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, fd
);
68 if (vector_lookup(r
->write_threads
, fd
) == THREAD_RUNNING
) {
70 thread_add_write(master
, resolver_cb_socket_writable
, r
, fd
,
72 vector_set_index(r
->write_threads
, fd
, t
);
74 resolver_update_timeouts(r
);
79 static void resolver_update_timeouts(struct resolver_state
*r
)
81 struct timeval
*tv
, tvbuf
;
83 if (r
->timeout
== THREAD_RUNNING
)
86 THREAD_OFF(r
->timeout
);
87 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
89 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
90 thread_add_timer_msec(master
, resolver_cb_timeout
, r
, timeoutms
,
95 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
98 struct resolver_state
*r
= (struct resolver_state
*)data
;
102 t
= vector_lookup_ensure(r
->read_threads
, fd
);
104 thread_add_read(master
, resolver_cb_socket_readable
, r
,
106 vector_set_index(r
->read_threads
, fd
, t
);
109 t
= vector_lookup(r
->read_threads
, fd
);
111 if (t
!= THREAD_RUNNING
) {
114 vector_unset(r
->read_threads
, fd
);
119 t
= vector_lookup_ensure(r
->write_threads
, fd
);
121 thread_add_read(master
, resolver_cb_socket_writable
, r
,
123 vector_set_index(r
->write_threads
, fd
, t
);
126 t
= vector_lookup(r
->write_threads
, fd
);
128 if (t
!= THREAD_RUNNING
) {
131 vector_unset(r
->write_threads
, fd
);
136 void resolver_init(void)
138 struct ares_options ares_opts
;
140 state
.read_threads
= vector_init(1);
141 state
.write_threads
= vector_init(1);
143 ares_opts
= (struct ares_options
){
144 .sock_state_cb
= &ares_socket_cb
,
145 .sock_state_cb_data
= &state
,
150 ares_init_options(&state
.channel
, &ares_opts
,
151 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
156 static void ares_address_cb(void *arg
, int status
, int timeouts
,
159 struct resolver_query
*query
= (struct resolver_query
*)arg
;
160 union sockunion addr
[16];
163 if (status
!= ARES_SUCCESS
) {
164 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving failed", query
);
165 query
->callback(query
, -1, NULL
);
166 query
->callback
= NULL
;
170 for (i
= 0; i
< ZEBRA_NUM_OF(addr
) && he
->h_addr_list
[i
] != NULL
; i
++) {
171 memset(&addr
[i
], 0, sizeof(addr
[i
]));
172 addr
[i
].sa
.sa_family
= he
->h_addrtype
;
173 switch (he
->h_addrtype
) {
175 memcpy(&addr
[i
].sin
.sin_addr
,
176 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
179 memcpy(&addr
[i
].sin6
.sin6_addr
,
180 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
185 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolved with %d results", query
,
187 query
->callback(query
, i
, &addr
[0]);
188 query
->callback
= NULL
;
191 void resolver_resolve(struct resolver_query
*query
, int af
,
192 const char *hostname
,
193 void (*callback
)(struct resolver_query
*, int,
196 if (query
->callback
!= NULL
) {
197 flog_err(NHRP_ERR_RESOLVER
,
198 "Trying to resolve '%s', but previous query was not finished yet",
203 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving '%s'", query
, hostname
);
205 query
->callback
= callback
;
206 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
207 resolver_update_timeouts(&state
);