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>
17 struct resolver_state
{
19 struct thread
*timeout
;
20 vector read_threads
, write_threads
;
23 static struct resolver_state state
;
25 #define THREAD_RUNNING ((struct thread *)-1)
27 static void resolver_update_timeouts(struct resolver_state
*r
);
29 static int resolver_cb_timeout(struct thread
*t
)
31 struct resolver_state
*r
= THREAD_ARG(t
);
33 r
->timeout
= THREAD_RUNNING
;
34 ares_process(r
->channel
, NULL
, NULL
);
36 resolver_update_timeouts(r
);
41 static int resolver_cb_socket_readable(struct thread
*t
)
43 struct resolver_state
*r
= THREAD_ARG(t
);
44 int fd
= THREAD_FD(t
);
46 vector_set_index(r
->read_threads
, fd
, THREAD_RUNNING
);
47 ares_process_fd(r
->channel
, fd
, ARES_SOCKET_BAD
);
48 if (vector_lookup(r
->read_threads
, fd
) == THREAD_RUNNING
) {
50 thread_add_read(master
, resolver_cb_socket_readable
, r
, fd
, &t
);
51 vector_set_index(r
->read_threads
, fd
, t
);
53 resolver_update_timeouts(r
);
58 static int resolver_cb_socket_writable(struct thread
*t
)
60 struct resolver_state
*r
= THREAD_ARG(t
);
61 int fd
= THREAD_FD(t
);
63 vector_set_index(r
->write_threads
, fd
, THREAD_RUNNING
);
64 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, fd
);
65 if (vector_lookup(r
->write_threads
, fd
) == THREAD_RUNNING
) {
67 thread_add_write(master
, resolver_cb_socket_writable
, r
, fd
,
69 vector_set_index(r
->write_threads
, fd
, t
);
71 resolver_update_timeouts(r
);
76 static void resolver_update_timeouts(struct resolver_state
*r
)
78 struct timeval
*tv
, tvbuf
;
80 if (r
->timeout
== THREAD_RUNNING
)
83 THREAD_OFF(r
->timeout
);
84 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
86 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
87 thread_add_timer_msec(master
, resolver_cb_timeout
, r
, timeoutms
,
92 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
95 struct resolver_state
*r
= (struct resolver_state
*)data
;
99 t
= vector_lookup_ensure(r
->read_threads
, fd
);
101 thread_add_read(master
, resolver_cb_socket_readable
, r
,
103 vector_set_index(r
->read_threads
, fd
, t
);
106 t
= vector_lookup(r
->read_threads
, fd
);
108 if (t
!= THREAD_RUNNING
) {
111 vector_unset(r
->read_threads
, fd
);
116 t
= vector_lookup_ensure(r
->write_threads
, fd
);
118 thread_add_read(master
, resolver_cb_socket_writable
, r
,
120 vector_set_index(r
->write_threads
, fd
, t
);
123 t
= vector_lookup(r
->write_threads
, fd
);
125 if (t
!= THREAD_RUNNING
) {
128 vector_unset(r
->write_threads
, fd
);
133 void resolver_init(void)
135 struct ares_options ares_opts
;
137 state
.read_threads
= vector_init(1);
138 state
.write_threads
= vector_init(1);
140 ares_opts
= (struct ares_options
){
141 .sock_state_cb
= &ares_socket_cb
,
142 .sock_state_cb_data
= &state
,
147 ares_init_options(&state
.channel
, &ares_opts
,
148 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
153 static void ares_address_cb(void *arg
, int status
, int timeouts
,
156 struct resolver_query
*query
= (struct resolver_query
*)arg
;
157 union sockunion addr
[16];
160 if (status
!= ARES_SUCCESS
) {
161 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving failed", query
);
162 query
->callback(query
, -1, NULL
);
163 query
->callback
= NULL
;
167 for (i
= 0; i
< ZEBRA_NUM_OF(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
);
182 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolved with %d results", query
,
184 query
->callback(query
, i
, &addr
[0]);
185 query
->callback
= NULL
;
188 void resolver_resolve(struct resolver_query
*query
, int af
,
189 const char *hostname
,
190 void (*callback
)(struct resolver_query
*, int,
193 if (query
->callback
!= NULL
) {
195 "Trying to resolve '%s', but previous query was not finished yet",
200 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving '%s'", query
, hostname
);
202 query
->callback
= callback
;
203 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
204 resolver_update_timeouts(&state
);