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
,
52 vector_set_index(r
->read_threads
, fd
, t
);
54 resolver_update_timeouts(r
);
59 static int resolver_cb_socket_writable(struct thread
*t
)
61 struct resolver_state
*r
= THREAD_ARG(t
);
62 int fd
= THREAD_FD(t
);
64 vector_set_index(r
->write_threads
, fd
, THREAD_RUNNING
);
65 ares_process_fd(r
->channel
, ARES_SOCKET_BAD
, fd
);
66 if (vector_lookup(r
->write_threads
, fd
) == THREAD_RUNNING
) {
68 thread_add_write(master
, resolver_cb_socket_writable
, r
, fd
,
70 vector_set_index(r
->write_threads
, fd
, t
);
72 resolver_update_timeouts(r
);
77 static void resolver_update_timeouts(struct resolver_state
*r
)
79 struct timeval
*tv
, tvbuf
;
81 if (r
->timeout
== THREAD_RUNNING
) return;
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
,
88 timeoutms
, &r
->timeout
);
92 static void ares_socket_cb(void *data
, ares_socket_t fd
, int readable
, int writable
)
94 struct resolver_state
*r
= (struct resolver_state
*) data
;
98 t
= vector_lookup_ensure(r
->read_threads
, fd
);
100 thread_add_read(master
, resolver_cb_socket_readable
,
102 vector_set_index(r
->read_threads
, fd
, t
);
105 t
= vector_lookup(r
->read_threads
, fd
);
107 if (t
!= THREAD_RUNNING
) {
110 vector_unset(r
->read_threads
, fd
);
115 t
= vector_lookup_ensure(r
->write_threads
, fd
);
117 thread_add_read(master
, resolver_cb_socket_writable
,
119 vector_set_index(r
->write_threads
, fd
, t
);
122 t
= vector_lookup(r
->write_threads
, fd
);
124 if (t
!= THREAD_RUNNING
) {
127 vector_unset(r
->write_threads
, fd
);
132 void resolver_init(void)
134 struct ares_options ares_opts
;
136 state
.read_threads
= vector_init(1);
137 state
.write_threads
= vector_init(1);
139 ares_opts
= (struct ares_options
) {
140 .sock_state_cb
= &ares_socket_cb
,
141 .sock_state_cb_data
= &state
,
146 ares_init_options(&state
.channel
, &ares_opts
,
147 ARES_OPT_SOCK_STATE_CB
| ARES_OPT_TIMEOUT
|
152 static void ares_address_cb(void *arg
, int status
, int timeouts
, struct hostent
*he
)
154 struct resolver_query
*query
= (struct resolver_query
*) arg
;
155 union sockunion addr
[16];
158 if (status
!= ARES_SUCCESS
) {
159 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving failed", query
);
160 query
->callback(query
, -1, NULL
);
161 query
->callback
= NULL
;
165 for (i
= 0; he
->h_addr_list
[i
] != NULL
&& i
< ZEBRA_NUM_OF(addr
); i
++) {
166 memset(&addr
[i
], 0, sizeof(addr
[i
]));
167 addr
[i
].sa
.sa_family
= he
->h_addrtype
;
168 switch (he
->h_addrtype
) {
170 memcpy(&addr
[i
].sin
.sin_addr
, (uint8_t *) he
->h_addr_list
[i
], he
->h_length
);
173 memcpy(&addr
[i
].sin6
.sin6_addr
, (uint8_t *) he
->h_addr_list
[i
], he
->h_length
);
178 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolved with %d results", query
, (int) i
);
179 query
->callback(query
, i
, &addr
[0]);
180 query
->callback
= NULL
;
183 void resolver_resolve(struct resolver_query
*query
, int af
, const char *hostname
, void (*callback
)(struct resolver_query
*, int, union sockunion
*))
185 if (query
->callback
!= NULL
) {
186 zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname
);
190 debugf(NHRP_DEBUG_COMMON
, "[%p] Resolving '%s'", query
, hostname
);
192 query
->callback
= callback
;
193 ares_gethostbyname(state
.channel
, hostname
, af
, ares_address_cb
, query
);
194 resolver_update_timeouts(&state
);