]> git.proxmox.com Git - mirror_frr.git/blob - lib/resolver.c
Merge pull request #8008 from chiragshah6/yang_nb5
[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 #include "xref.h"
23
24 XREF_SETUP()
25
26 struct resolver_state {
27 ares_channel channel;
28 struct thread_master *master;
29 struct thread *timeout;
30 vector read_threads, write_threads;
31 };
32
33 static struct resolver_state state;
34 static bool resolver_debug;
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);
56
57 vector_set_index(r->read_threads, fd, THREAD_RUNNING);
58 ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
59 if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
60 t = NULL;
61 thread_add_read(r->master, resolver_cb_socket_readable, r, fd,
62 &t);
63 vector_set_index(r->read_threads, fd, t);
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);
74
75 vector_set_index(r->write_threads, fd, THREAD_RUNNING);
76 ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
77 if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
78 t = NULL;
79 thread_add_write(r->master, resolver_cb_socket_writable, r, fd,
80 &t);
81 vector_set_index(r->write_threads, fd, t);
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
92 if (r->timeout == THREAD_RUNNING)
93 return;
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;
99 thread_add_timer_msec(r->master, resolver_cb_timeout, r,
100 timeoutms, &r->timeout);
101 }
102 }
103
104 static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
105 int writable)
106 {
107 struct resolver_state *r = (struct resolver_state *)data;
108 struct thread *t;
109
110 if (readable) {
111 t = vector_lookup_ensure(r->read_threads, fd);
112 if (!t) {
113 thread_add_read(r->master, resolver_cb_socket_readable,
114 r, fd, &t);
115 vector_set_index(r->read_threads, fd, t);
116 }
117 } else {
118 t = vector_lookup(r->read_threads, fd);
119 if (t) {
120 if (t != THREAD_RUNNING) {
121 THREAD_OFF(t);
122 }
123 vector_unset(r->read_threads, fd);
124 }
125 }
126
127 if (writable) {
128 t = vector_lookup_ensure(r->write_threads, fd);
129 if (!t) {
130 thread_add_read(r->master, resolver_cb_socket_writable,
131 r, fd, &t);
132 vector_set_index(r->write_threads, fd, t);
133 }
134 } else {
135 t = vector_lookup(r->write_threads, fd);
136 if (t) {
137 if (t != THREAD_RUNNING) {
138 THREAD_OFF(t);
139 }
140 vector_unset(r->write_threads, fd);
141 }
142 }
143 }
144
145
146 static void ares_address_cb(void *arg, int status, int timeouts,
147 struct hostent *he)
148 {
149 struct resolver_query *query = (struct resolver_query *)arg;
150 union sockunion addr[16];
151 void (*callback)(struct resolver_query *, const char *, int,
152 union sockunion *);
153 size_t i;
154
155 callback = query->callback;
156 query->callback = NULL;
157
158 if (status != ARES_SUCCESS) {
159 if (resolver_debug)
160 zlog_debug("[%p] Resolving failed (%s)",
161 query, ares_strerror(status));
162
163 callback(query, ares_strerror(status), -1, NULL);
164 return;
165 }
166
167 for (i = 0; i < array_size(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) {
171 case AF_INET:
172 memcpy(&addr[i].sin.sin_addr,
173 (uint8_t *)he->h_addr_list[i], he->h_length);
174 break;
175 case AF_INET6:
176 memcpy(&addr[i].sin6.sin6_addr,
177 (uint8_t *)he->h_addr_list[i], he->h_length);
178 break;
179 }
180 }
181
182 if (resolver_debug)
183 zlog_debug("[%p] Resolved with %d results", query, (int)i);
184
185 callback(query, NULL, i, &addr[0]);
186 }
187
188 static int resolver_cb_literal(struct thread *t)
189 {
190 struct resolver_query *query = THREAD_ARG(t);
191 void (*callback)(struct resolver_query *, const char *, int,
192 union sockunion *);
193
194 callback = query->callback;
195 query->callback = NULL;
196
197 callback(query, ARES_SUCCESS, 1, &query->literal_addr);
198 return 0;
199 }
200
201 void resolver_resolve(struct resolver_query *query, int af,
202 const char *hostname,
203 void (*callback)(struct resolver_query *, const char *,
204 int, union sockunion *))
205 {
206 int ret;
207
208 if (query->callback != NULL) {
209 flog_err(
210 EC_LIB_RESOLVER,
211 "Trying to resolve '%s', but previous query was not finished yet",
212 hostname);
213 return;
214 }
215
216 query->callback = callback;
217 query->literal_cb = NULL;
218
219 ret = str2sockunion(hostname, &query->literal_addr);
220 if (ret == 0) {
221 if (resolver_debug)
222 zlog_debug("[%p] Resolving '%s' (IP literal)",
223 query, hostname);
224
225 /* for consistency with proper name lookup, don't call the
226 * callback immediately; defer to thread loop
227 */
228 thread_add_timer_msec(state.master, resolver_cb_literal,
229 query, 0, &query->literal_cb);
230 return;
231 }
232
233 if (resolver_debug)
234 zlog_debug("[%p] Resolving '%s'", query, hostname);
235
236 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
237 resolver_update_timeouts(&state);
238 }
239
240 DEFUN(debug_resolver,
241 debug_resolver_cmd,
242 "[no] debug resolver",
243 NO_STR
244 DEBUG_STR
245 "Debug DNS resolver actions\n")
246 {
247 resolver_debug = (argc == 2);
248 return CMD_SUCCESS;
249 }
250
251 static int resolver_config_write_debug(struct vty *vty);
252 static struct cmd_node resolver_debug_node = {
253 .name = "resolver debug",
254 .node = RESOLVER_DEBUG_NODE,
255 .prompt = "",
256 .config_write = resolver_config_write_debug,
257 };
258
259 static int resolver_config_write_debug(struct vty *vty)
260 {
261 if (resolver_debug)
262 vty_out(vty, "debug resolver\n");
263 return 1;
264 }
265
266
267 void resolver_init(struct thread_master *tm)
268 {
269 struct ares_options ares_opts;
270
271 state.master = tm;
272 state.read_threads = vector_init(1);
273 state.write_threads = vector_init(1);
274
275 ares_opts = (struct ares_options){
276 .sock_state_cb = &ares_socket_cb,
277 .sock_state_cb_data = &state,
278 .timeout = 2,
279 .tries = 3,
280 };
281
282 ares_init_options(&state.channel, &ares_opts,
283 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
284 | ARES_OPT_TRIES);
285
286 install_node(&resolver_debug_node);
287 install_element(CONFIG_NODE, &debug_resolver_cmd);
288 install_element(ENABLE_NODE, &debug_resolver_cmd);
289 }