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"
22 #include "nhrp_errors.h"
24 struct resolver_state
{
26 struct thread
*timeout
;
27 vector read_threads
, write_threads
;
30 static struct resolver_state state
;
32 #define THREAD_RUNNING ((struct thread *)-1)
34 static void resolver_update_timeouts(struct resolver_state
*r
);
36 static int resolver_cb_timeout(struct thread
*t
)
38 struct resolver_state
*r
= THREAD_ARG(t
);
40 r
->timeout
= THREAD_RUNNING
;
41 ares_process(r
->channel
, NULL
, NULL
);
43 resolver_update_timeouts(r
);
48 static int resolver_cb_socket_readable(struct thread
*t
)
50 struct resolver_state
*r
= THREAD_ARG(t
);
51 int fd
= THREAD_FD(t
);
53 vector_set_index(r
->read_threads
, fd
, THREAD_RUNNING
);
54 ares_process_fd(r
->channel
, fd
, ARES_SOCKET_BAD
);
55 if (vector_lookup(r
->read_threads
, fd
) == THREAD_RUNNING
) {
57 thread_add_read(master
, resolver_cb_socket_readable
, r
, fd
, &t
);
58 vector_set_index(r
->read_threads
, fd
, t
);
60 resolver_update_timeouts(r
);
65 static int resolver_cb_socket_writable(struct thread
*t
)
67 struct resolver_state
*r
= THREAD_ARG(t
);
68 int fd
= THREAD_FD(t
);
70 vector_set_index(r
->write_threads
, fd
, THREAD_RUNNING
);
71 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, fd
);
72 if (vector_lookup(r
->write_threads
, fd
) == THREAD_RUNNING
) {
74 thread_add_write(master
, resolver_cb_socket_writable
, r
, fd
,
76 vector_set_index(r
->write_threads
, fd
, t
);
78 resolver_update_timeouts(r
);
83 static void resolver_update_timeouts(struct resolver_state
*r
)
85 struct timeval
*tv
, tvbuf
;
87 if (r
->timeout
== THREAD_RUNNING
)
90 THREAD_OFF(r
->timeout
);
91 tv
= ares_timeout(r
->channel
, NULL
, &tvbuf
);
93 unsigned int timeoutms
= tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000;
94 thread_add_timer_msec(master
, resolver_cb_timeout
, r
, timeoutms
,
99 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
,
102 struct resolver_state
*r
= (struct resolver_state
*)data
;
106 t
= vector_lookup_ensure(r
->read_threads
, fd
);
108 thread_add_read(master
, resolver_cb_socket_readable
, r
,
110 vector_set_index(r
->read_threads
, fd
, t
);
113 t
= vector_lookup(r
->read_threads
, fd
);
115 if (t
!= THREAD_RUNNING
) {
118 vector_unset(r
->read_threads
, fd
);
123 t
= vector_lookup_ensure(r
->write_threads
, fd
);
125 thread_add_read(master
, resolver_cb_socket_writable
, r
,
127 vector_set_index(r
->write_threads
, fd
, t
);
130 t
= vector_lookup(r
->write_threads
, fd
);
132 if (t
!= THREAD_RUNNING
) {
135 vector_unset(r
->write_threads
, fd
);
140 void resolver_init(void)
142 struct ares_options ares_opts
;
144 state
.read_threads
= vector_init(1);
145 state
.write_threads
= vector_init(1);
147 ares_opts
= (struct ares_options
){
148 .sock_state_cb
= &ares_socket_cb
,
149 .sock_state_cb_data
= &state
,
154 ares_init_options(&state
.channel
, &ares_opts
,
155 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
160 static void ares_address_cb(void *arg
, int status
, int timeouts
,
163 struct resolver_query
*query
= (struct resolver_query
*)arg
;
164 union sockunion addr
[16];
167 if (status
!= ARES_SUCCESS
) {
168 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving failed", query
);
169 query
->callback(query
, -1, NULL
);
170 query
->callback
= NULL
;
174 for (i
= 0; i
< ZEBRA_NUM_OF(addr
) && he
->h_addr_list
[i
] != NULL
; i
++) {
175 memset(&addr
[i
], 0, sizeof(addr
[i
]));
176 addr
[i
].sa
.sa_family
= he
->h_addrtype
;
177 switch (he
->h_addrtype
) {
179 memcpy(&addr
[i
].sin
.sin_addr
,
180 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
183 memcpy(&addr
[i
].sin6
.sin6_addr
,
184 (uint8_t *)he
->h_addr_list
[i
], he
->h_length
);
189 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolved with %d results", query
,
191 query
->callback(query
, i
, &addr
[0]);
192 query
->callback
= NULL
;
195 void resolver_resolve(struct resolver_query
*query
, int af
,
196 const char *hostname
,
197 void (*callback
)(struct resolver_query
*, int,
200 if (query
->callback
!= NULL
) {
203 "Trying to resolve '%s', but previous query was not finished yet",
208 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving '%s'", query
, hostname
);
210 query
->callback
= callback
;
211 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
212 resolver_update_timeouts(&state
);