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