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