]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/resolver.c
*: conform with COMMUNITY.md formatting rules, via 'make indent'
[mirror_frr.git] / nhrpd / 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
10#include <ares.h>
11#include <ares_version.h>
12
13#include "vector.h"
14#include "thread.h"
15#include "nhrpd.h"
16
17struct resolver_state {
18 ares_channel channel;
19 struct thread *timeout;
20 vector read_threads, write_threads;
21};
22
23static struct resolver_state state;
24
25#define THREAD_RUNNING ((struct thread *)-1)
26
27static void resolver_update_timeouts(struct resolver_state *r);
28
29static int resolver_cb_timeout(struct thread *t)
30{
31 struct resolver_state *r = THREAD_ARG(t);
32
33 r->timeout = THREAD_RUNNING;
34 ares_process(r->channel, NULL, NULL);
35 r->timeout = NULL;
36 resolver_update_timeouts(r);
37
38 return 0;
39}
40
41static int resolver_cb_socket_readable(struct thread *t)
42{
43 struct resolver_state *r = THREAD_ARG(t);
44 int fd = THREAD_FD(t);
45
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) {
49 t = NULL;
996c9314 50 thread_add_read(master, resolver_cb_socket_readable, r, fd, &t);
2fb975da
TT
51 vector_set_index(r->read_threads, fd, t);
52 }
53 resolver_update_timeouts(r);
54
55 return 0;
56}
57
58static int resolver_cb_socket_writable(struct thread *t)
59{
60 struct resolver_state *r = THREAD_ARG(t);
61 int fd = THREAD_FD(t);
62
63 vector_set_index(r->write_threads, fd, THREAD_RUNNING);
64 ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
65 if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
66 t = NULL;
ffa2c898
QY
67 thread_add_write(master, resolver_cb_socket_writable, r, fd,
68 &t);
2fb975da
TT
69 vector_set_index(r->write_threads, fd, t);
70 }
71 resolver_update_timeouts(r);
72
73 return 0;
74}
75
76static void resolver_update_timeouts(struct resolver_state *r)
77{
78 struct timeval *tv, tvbuf;
79
996c9314
LB
80 if (r->timeout == THREAD_RUNNING)
81 return;
2fb975da
TT
82
83 THREAD_OFF(r->timeout);
84 tv = ares_timeout(r->channel, NULL, &tvbuf);
85 if (tv) {
86 unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
996c9314
LB
87 thread_add_timer_msec(master, resolver_cb_timeout, r, timeoutms,
88 &r->timeout);
2fb975da
TT
89 }
90}
91
996c9314
LB
92static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
93 int writable)
2fb975da 94{
996c9314 95 struct resolver_state *r = (struct resolver_state *)data;
2fb975da
TT
96 struct thread *t;
97
98 if (readable) {
99 t = vector_lookup_ensure(r->read_threads, fd);
100 if (!t) {
996c9314
LB
101 thread_add_read(master, resolver_cb_socket_readable, r,
102 fd, &t);
2fb975da
TT
103 vector_set_index(r->read_threads, fd, t);
104 }
105 } else {
106 t = vector_lookup(r->read_threads, fd);
107 if (t) {
108 if (t != THREAD_RUNNING) {
109 THREAD_OFF(t);
110 }
111 vector_unset(r->read_threads, fd);
112 }
113 }
114
115 if (writable) {
116 t = vector_lookup_ensure(r->write_threads, fd);
117 if (!t) {
996c9314
LB
118 thread_add_read(master, resolver_cb_socket_writable, r,
119 fd, &t);
2fb975da
TT
120 vector_set_index(r->write_threads, fd, t);
121 }
122 } else {
123 t = vector_lookup(r->write_threads, fd);
124 if (t) {
125 if (t != THREAD_RUNNING) {
126 THREAD_OFF(t);
127 }
128 vector_unset(r->write_threads, fd);
129 }
130 }
131}
132
133void resolver_init(void)
134{
135 struct ares_options ares_opts;
136
137 state.read_threads = vector_init(1);
138 state.write_threads = vector_init(1);
139
996c9314 140 ares_opts = (struct ares_options){
2fb975da
TT
141 .sock_state_cb = &ares_socket_cb,
142 .sock_state_cb_data = &state,
143 .timeout = 2,
144 .tries = 3,
145 };
146
147 ares_init_options(&state.channel, &ares_opts,
996c9314
LB
148 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
149 | ARES_OPT_TRIES);
2fb975da
TT
150}
151
152
996c9314
LB
153static void ares_address_cb(void *arg, int status, int timeouts,
154 struct hostent *he)
2fb975da 155{
996c9314 156 struct resolver_query *query = (struct resolver_query *)arg;
2fb975da
TT
157 union sockunion addr[16];
158 size_t i;
159
160 if (status != ARES_SUCCESS) {
161 debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query);
162 query->callback(query, -1, NULL);
163 query->callback = NULL;
164 return;
165 }
166
167 for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); 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:
996c9314
LB
172 memcpy(&addr[i].sin.sin_addr,
173 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
174 break;
175 case AF_INET6:
996c9314
LB
176 memcpy(&addr[i].sin6.sin6_addr,
177 (uint8_t *)he->h_addr_list[i], he->h_length);
2fb975da
TT
178 break;
179 }
180 }
181
996c9314
LB
182 debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query,
183 (int)i);
2fb975da
TT
184 query->callback(query, i, &addr[0]);
185 query->callback = NULL;
186}
187
996c9314
LB
188void resolver_resolve(struct resolver_query *query, int af,
189 const char *hostname,
190 void (*callback)(struct resolver_query *, int,
191 union sockunion *))
2fb975da
TT
192{
193 if (query->callback != NULL) {
996c9314
LB
194 zlog_err(
195 "Trying to resolve '%s', but previous query was not finished yet",
196 hostname);
2fb975da
TT
197 return;
198 }
199
200 debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname);
201
202 query->callback = callback;
203 ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
204 resolver_update_timeouts(&state);
205}