]>
Commit | Line | Data |
---|---|---|
2fb975da TT |
1 | /* C-Ares integration to Quagga mainloop |
2 | * Copyright (c) 2014-2015 Timo Teräs | |
3 | * | |
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. | |
8 | */ | |
9 | ||
b45ac5f5 DL |
10 | #ifdef HAVE_CONFIG_H |
11 | #include "config.h" | |
12 | #endif | |
13 | ||
2fb975da TT |
14 | #include <ares.h> |
15 | #include <ares_version.h> | |
16 | ||
17 | #include "vector.h" | |
18 | #include "thread.h" | |
aed07011 | 19 | #include "lib_errors.h" |
fe9e7b71 DL |
20 | #include "resolver.h" |
21 | #include "command.h" | |
b2fa8c0f DL |
22 | #include "xref.h" |
23 | ||
80413c20 | 24 | XREF_SETUP(); |
2fb975da TT |
25 | |
26 | struct resolver_state { | |
27 | ares_channel channel; | |
fe9e7b71 | 28 | struct thread_master *master; |
2fb975da TT |
29 | struct thread *timeout; |
30 | vector read_threads, write_threads; | |
31 | }; | |
32 | ||
33 | static struct resolver_state state; | |
fe9e7b71 | 34 | static bool resolver_debug; |
2fb975da TT |
35 | |
36 | #define THREAD_RUNNING ((struct thread *)-1) | |
37 | ||
38 | static void resolver_update_timeouts(struct resolver_state *r); | |
39 | ||
40 | static int resolver_cb_timeout(struct thread *t) | |
41 | { | |
42 | struct resolver_state *r = THREAD_ARG(t); | |
43 | ||
44 | r->timeout = THREAD_RUNNING; | |
45 | ares_process(r->channel, NULL, NULL); | |
46 | r->timeout = NULL; | |
47 | resolver_update_timeouts(r); | |
48 | ||
49 | return 0; | |
50 | } | |
51 | ||
52 | static int resolver_cb_socket_readable(struct thread *t) | |
53 | { | |
54 | struct resolver_state *r = THREAD_ARG(t); | |
55 | int fd = THREAD_FD(t); | |
72618ba8 | 56 | struct thread **t_ptr; |
2fb975da TT |
57 | |
58 | vector_set_index(r->read_threads, fd, THREAD_RUNNING); | |
59 | ares_process_fd(r->channel, fd, ARES_SOCKET_BAD); | |
60 | if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) { | |
72618ba8 | 61 | t_ptr = (struct thread **)vector_get_index(r->read_threads, fd); |
fe9e7b71 | 62 | thread_add_read(r->master, resolver_cb_socket_readable, r, fd, |
72618ba8 | 63 | t_ptr); |
2fb975da TT |
64 | } |
65 | resolver_update_timeouts(r); | |
66 | ||
67 | return 0; | |
68 | } | |
69 | ||
70 | static int resolver_cb_socket_writable(struct thread *t) | |
71 | { | |
72 | struct resolver_state *r = THREAD_ARG(t); | |
73 | int fd = THREAD_FD(t); | |
72618ba8 | 74 | struct thread **t_ptr; |
2fb975da TT |
75 | |
76 | vector_set_index(r->write_threads, fd, THREAD_RUNNING); | |
77 | ares_process_fd(r->channel, ARES_SOCKET_BAD, fd); | |
78 | if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) { | |
72618ba8 | 79 | t_ptr = (struct thread **)vector_get_index(r->write_threads, fd); |
fe9e7b71 | 80 | thread_add_write(r->master, resolver_cb_socket_writable, r, fd, |
72618ba8 | 81 | t_ptr); |
2fb975da TT |
82 | } |
83 | resolver_update_timeouts(r); | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
88 | static void resolver_update_timeouts(struct resolver_state *r) | |
89 | { | |
90 | struct timeval *tv, tvbuf; | |
91 | ||
996c9314 LB |
92 | if (r->timeout == THREAD_RUNNING) |
93 | return; | |
2fb975da TT |
94 | |
95 | THREAD_OFF(r->timeout); | |
96 | tv = ares_timeout(r->channel, NULL, &tvbuf); | |
97 | if (tv) { | |
98 | unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000; | |
fe9e7b71 DL |
99 | thread_add_timer_msec(r->master, resolver_cb_timeout, r, |
100 | timeoutms, &r->timeout); | |
2fb975da TT |
101 | } |
102 | } | |
103 | ||
996c9314 LB |
104 | static void ares_socket_cb(void *data, ares_socket_t fd, int readable, |
105 | int writable) | |
2fb975da | 106 | { |
996c9314 | 107 | struct resolver_state *r = (struct resolver_state *)data; |
72618ba8 | 108 | struct thread *t, **t_ptr; |
2fb975da TT |
109 | |
110 | if (readable) { | |
72618ba8 | 111 | t = vector_lookup(r->read_threads, fd); |
2fb975da | 112 | if (!t) { |
72618ba8 IR |
113 | t_ptr = (struct thread **)vector_get_index( |
114 | r->read_threads, fd); | |
fe9e7b71 | 115 | thread_add_read(r->master, resolver_cb_socket_readable, |
72618ba8 | 116 | r, fd, t_ptr); |
2fb975da TT |
117 | } |
118 | } else { | |
119 | t = vector_lookup(r->read_threads, fd); | |
120 | if (t) { | |
121 | if (t != THREAD_RUNNING) { | |
122 | THREAD_OFF(t); | |
123 | } | |
124 | vector_unset(r->read_threads, fd); | |
125 | } | |
126 | } | |
127 | ||
128 | if (writable) { | |
72618ba8 | 129 | t = vector_lookup(r->write_threads, fd); |
2fb975da | 130 | if (!t) { |
72618ba8 IR |
131 | t_ptr = (struct thread **)vector_get_index( |
132 | r->write_threads, fd); | |
fe9e7b71 | 133 | thread_add_read(r->master, resolver_cb_socket_writable, |
72618ba8 | 134 | r, fd, t_ptr); |
2fb975da TT |
135 | } |
136 | } else { | |
137 | t = vector_lookup(r->write_threads, fd); | |
138 | if (t) { | |
139 | if (t != THREAD_RUNNING) { | |
140 | THREAD_OFF(t); | |
141 | } | |
142 | vector_unset(r->write_threads, fd); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
2fb975da | 147 | |
996c9314 LB |
148 | static void ares_address_cb(void *arg, int status, int timeouts, |
149 | struct hostent *he) | |
2fb975da | 150 | { |
996c9314 | 151 | struct resolver_query *query = (struct resolver_query *)arg; |
2fb975da | 152 | union sockunion addr[16]; |
3286ca07 DL |
153 | void (*callback)(struct resolver_query *, const char *, int, |
154 | union sockunion *); | |
2fb975da TT |
155 | size_t i; |
156 | ||
50cdb6cf DL |
157 | callback = query->callback; |
158 | query->callback = NULL; | |
159 | ||
2fb975da | 160 | if (status != ARES_SUCCESS) { |
fe9e7b71 | 161 | if (resolver_debug) |
3286ca07 DL |
162 | zlog_debug("[%p] Resolving failed (%s)", |
163 | query, ares_strerror(status)); | |
fe9e7b71 | 164 | |
3286ca07 | 165 | callback(query, ares_strerror(status), -1, NULL); |
2fb975da TT |
166 | return; |
167 | } | |
168 | ||
7e3a1ec7 | 169 | for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) { |
2fb975da TT |
170 | memset(&addr[i], 0, sizeof(addr[i])); |
171 | addr[i].sa.sa_family = he->h_addrtype; | |
172 | switch (he->h_addrtype) { | |
173 | case AF_INET: | |
996c9314 LB |
174 | memcpy(&addr[i].sin.sin_addr, |
175 | (uint8_t *)he->h_addr_list[i], he->h_length); | |
2fb975da TT |
176 | break; |
177 | case AF_INET6: | |
996c9314 LB |
178 | memcpy(&addr[i].sin6.sin6_addr, |
179 | (uint8_t *)he->h_addr_list[i], he->h_length); | |
2fb975da TT |
180 | break; |
181 | } | |
182 | } | |
183 | ||
fe9e7b71 DL |
184 | if (resolver_debug) |
185 | zlog_debug("[%p] Resolved with %d results", query, (int)i); | |
186 | ||
3286ca07 | 187 | callback(query, NULL, i, &addr[0]); |
2fb975da TT |
188 | } |
189 | ||
125dc952 DL |
190 | static int resolver_cb_literal(struct thread *t) |
191 | { | |
192 | struct resolver_query *query = THREAD_ARG(t); | |
3286ca07 DL |
193 | void (*callback)(struct resolver_query *, const char *, int, |
194 | union sockunion *); | |
125dc952 DL |
195 | |
196 | callback = query->callback; | |
197 | query->callback = NULL; | |
198 | ||
3286ca07 | 199 | callback(query, ARES_SUCCESS, 1, &query->literal_addr); |
125dc952 DL |
200 | return 0; |
201 | } | |
202 | ||
996c9314 LB |
203 | void resolver_resolve(struct resolver_query *query, int af, |
204 | const char *hostname, | |
3286ca07 DL |
205 | void (*callback)(struct resolver_query *, const char *, |
206 | int, union sockunion *)) | |
2fb975da | 207 | { |
125dc952 DL |
208 | int ret; |
209 | ||
2fb975da | 210 | if (query->callback != NULL) { |
1c50c1c0 | 211 | flog_err( |
fe9e7b71 | 212 | EC_LIB_RESOLVER, |
1c50c1c0 QY |
213 | "Trying to resolve '%s', but previous query was not finished yet", |
214 | hostname); | |
2fb975da TT |
215 | return; |
216 | } | |
217 | ||
125dc952 DL |
218 | query->callback = callback; |
219 | query->literal_cb = NULL; | |
220 | ||
221 | ret = str2sockunion(hostname, &query->literal_addr); | |
222 | if (ret == 0) { | |
223 | if (resolver_debug) | |
224 | zlog_debug("[%p] Resolving '%s' (IP literal)", | |
225 | query, hostname); | |
226 | ||
227 | /* for consistency with proper name lookup, don't call the | |
228 | * callback immediately; defer to thread loop | |
229 | */ | |
230 | thread_add_timer_msec(state.master, resolver_cb_literal, | |
231 | query, 0, &query->literal_cb); | |
232 | return; | |
233 | } | |
234 | ||
fe9e7b71 DL |
235 | if (resolver_debug) |
236 | zlog_debug("[%p] Resolving '%s'", query, hostname); | |
2fb975da | 237 | |
2fb975da TT |
238 | ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query); |
239 | resolver_update_timeouts(&state); | |
240 | } | |
fe9e7b71 DL |
241 | |
242 | DEFUN(debug_resolver, | |
243 | debug_resolver_cmd, | |
244 | "[no] debug resolver", | |
245 | NO_STR | |
246 | DEBUG_STR | |
247 | "Debug DNS resolver actions\n") | |
248 | { | |
249 | resolver_debug = (argc == 2); | |
250 | return CMD_SUCCESS; | |
251 | } | |
252 | ||
612c2c15 | 253 | static int resolver_config_write_debug(struct vty *vty); |
62b346ee | 254 | static struct cmd_node resolver_debug_node = { |
f4b8291f | 255 | .name = "resolver debug", |
62b346ee DL |
256 | .node = RESOLVER_DEBUG_NODE, |
257 | .prompt = "", | |
612c2c15 | 258 | .config_write = resolver_config_write_debug, |
62b346ee | 259 | }; |
fe9e7b71 DL |
260 | |
261 | static int resolver_config_write_debug(struct vty *vty) | |
262 | { | |
263 | if (resolver_debug) | |
264 | vty_out(vty, "debug resolver\n"); | |
265 | return 1; | |
266 | } | |
267 | ||
268 | ||
269 | void resolver_init(struct thread_master *tm) | |
270 | { | |
271 | struct ares_options ares_opts; | |
272 | ||
273 | state.master = tm; | |
274 | state.read_threads = vector_init(1); | |
275 | state.write_threads = vector_init(1); | |
276 | ||
277 | ares_opts = (struct ares_options){ | |
278 | .sock_state_cb = &ares_socket_cb, | |
279 | .sock_state_cb_data = &state, | |
280 | .timeout = 2, | |
281 | .tries = 3, | |
282 | }; | |
283 | ||
284 | ares_init_options(&state.channel, &ares_opts, | |
285 | ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT | |
286 | | ARES_OPT_TRIES); | |
287 | ||
612c2c15 | 288 | install_node(&resolver_debug_node); |
fe9e7b71 DL |
289 | install_element(CONFIG_NODE, &debug_resolver_cmd); |
290 | install_element(ENABLE_NODE, &debug_resolver_cmd); | |
291 | } |