]> git.proxmox.com Git - mirror_frr.git/blame - lib/resolver.c
Merge pull request #4849 from sworleys/Label-Append-Resolve_2
[mirror_frr.git] / lib / resolver.c
CommitLineData
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"
2fb975da
TT
22
23struct resolver_state {
24 ares_channel channel;
fe9e7b71 25 struct thread_master *master;
2fb975da
TT
26 struct thread *timeout;
27 vector read_threads, write_threads;
28};
29
30static struct resolver_state state;
fe9e7b71 31static bool resolver_debug;
2fb975da
TT
32
33#define THREAD_RUNNING ((struct thread *)-1)
34
35static void resolver_update_timeouts(struct resolver_state *r);
36
37static 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
49static 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;
fe9e7b71
DL
58 thread_add_read(r->master, resolver_cb_socket_readable, r, fd,
59 &t);
2fb975da
TT
60 vector_set_index(r->read_threads, fd, t);
61 }
62 resolver_update_timeouts(r);
63
64 return 0;
65}
66
67static 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;
fe9e7b71 76 thread_add_write(r->master, resolver_cb_socket_writable, r, fd,
ffa2c898 77 &t);
2fb975da
TT
78 vector_set_index(r->write_threads, fd, t);
79 }
80 resolver_update_timeouts(r);
81
82 return 0;
83}
84
85static void resolver_update_timeouts(struct resolver_state *r)
86{
87 struct timeval *tv, tvbuf;
88
996c9314
LB
89 if (r->timeout == THREAD_RUNNING)
90 return;
2fb975da
TT
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;
fe9e7b71
DL
96 thread_add_timer_msec(r->master, resolver_cb_timeout, r,
97 timeoutms, &r->timeout);
2fb975da
TT
98 }
99}
100
996c9314
LB
101static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
102 int writable)
2fb975da 103{
996c9314 104 struct resolver_state *r = (struct resolver_state *)data;
2fb975da
TT
105 struct thread *t;
106
107 if (readable) {
108 t = vector_lookup_ensure(r->read_threads, fd);
109 if (!t) {
fe9e7b71
DL
110 thread_add_read(r->master, resolver_cb_socket_readable,
111 r, fd, &t);
2fb975da
TT
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) {
fe9e7b71
DL
127 thread_add_read(r->master, resolver_cb_socket_writable,
128 r, fd, &t);
2fb975da
TT
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
2fb975da 142
996c9314
LB
143static void ares_address_cb(void *arg, int status, int timeouts,
144 struct hostent *he)
2fb975da 145{
996c9314 146 struct resolver_query *query = (struct resolver_query *)arg;
2fb975da 147 union sockunion addr[16];
50cdb6cf 148 void (*callback)(struct resolver_query *, int, union sockunion *);
2fb975da
TT
149 size_t i;
150
50cdb6cf
DL
151 callback = query->callback;
152 query->callback = NULL;
153
2fb975da 154 if (status != ARES_SUCCESS) {
fe9e7b71
DL
155 if (resolver_debug)
156 zlog_debug("[%p] Resolving failed", query);
157
50cdb6cf 158 callback(query, -1, NULL);
2fb975da
TT
159 return;
160 }
161
7e3a1ec7 162 for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
2fb975da
TT
163 memset(&addr[i], 0, sizeof(addr[i]));
164 addr[i].sa.sa_family = he->h_addrtype;
165 switch (he->h_addrtype) {
166 case AF_INET:
996c9314
LB
167 memcpy(&addr[i].sin.sin_addr,
168 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
169 break;
170 case AF_INET6:
996c9314
LB
171 memcpy(&addr[i].sin6.sin6_addr,
172 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
173 break;
174 }
175 }
176
fe9e7b71
DL
177 if (resolver_debug)
178 zlog_debug("[%p] Resolved with %d results", query, (int)i);
179
50cdb6cf 180 callback(query, i, &addr[0]);
2fb975da
TT
181}
182
996c9314
LB
183void resolver_resolve(struct resolver_query *query, int af,
184 const char *hostname,
185 void (*callback)(struct resolver_query *, int,
186 union sockunion *))
2fb975da
TT
187{
188 if (query->callback != NULL) {
1c50c1c0 189 flog_err(
fe9e7b71 190 EC_LIB_RESOLVER,
1c50c1c0
QY
191 "Trying to resolve '%s', but previous query was not finished yet",
192 hostname);
2fb975da
TT
193 return;
194 }
195
fe9e7b71
DL
196 if (resolver_debug)
197 zlog_debug("[%p] Resolving '%s'", query, hostname);
2fb975da
TT
198
199 query->callback = callback;
200 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
201 resolver_update_timeouts(&state);
202}
fe9e7b71
DL
203
204DEFUN(debug_resolver,
205 debug_resolver_cmd,
206 "[no] debug resolver",
207 NO_STR
208 DEBUG_STR
209 "Debug DNS resolver actions\n")
210{
211 resolver_debug = (argc == 2);
212 return CMD_SUCCESS;
213}
214
215static struct cmd_node resolver_debug_node = {RESOLVER_DEBUG_NODE, "", 1};
216
217static int resolver_config_write_debug(struct vty *vty)
218{
219 if (resolver_debug)
220 vty_out(vty, "debug resolver\n");
221 return 1;
222}
223
224
225void resolver_init(struct thread_master *tm)
226{
227 struct ares_options ares_opts;
228
229 state.master = tm;
230 state.read_threads = vector_init(1);
231 state.write_threads = vector_init(1);
232
233 ares_opts = (struct ares_options){
234 .sock_state_cb = &ares_socket_cb,
235 .sock_state_cb_data = &state,
236 .timeout = 2,
237 .tries = 3,
238 };
239
240 ares_init_options(&state.channel, &ares_opts,
241 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
242 | ARES_OPT_TRIES);
243
244 install_node(&resolver_debug_node, resolver_config_write_debug);
245 install_element(CONFIG_NODE, &debug_resolver_cmd);
246 install_element(ENABLE_NODE, &debug_resolver_cmd);
247}