]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - nhrpd/resolver.c
New upstream release and new maintainer
[quagga-debian.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_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
51                 vector_set_index(r->read_threads, fd, t);
52         }
53         resolver_update_timeouts(r);
54
55         return 0;
56 }
57
58 static 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;
67                 THREAD_WRITE_ON(master, t, resolver_cb_socket_writable, r, fd);
68                 vector_set_index(r->write_threads, fd, t);
69         }
70         resolver_update_timeouts(r);
71
72         return 0;
73 }
74
75 static void resolver_update_timeouts(struct resolver_state *r)
76 {
77         struct timeval *tv, tvbuf;
78
79         if (r->timeout == THREAD_RUNNING) return;
80
81         THREAD_OFF(r->timeout);
82         tv = ares_timeout(r->channel, NULL, &tvbuf);
83         if (tv) {
84                 unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
85                 THREAD_TIMER_MSEC_ON(master, r->timeout, resolver_cb_timeout, r, timeoutms);
86         }
87 }
88
89 static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable)
90 {
91         struct resolver_state *r = (struct resolver_state *) data;
92         struct thread *t;
93
94         if (readable) {
95                 t = vector_lookup_ensure(r->read_threads, fd);
96                 if (!t) {
97                         THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
98                         vector_set_index(r->read_threads, fd, t);
99                 }
100         } else {
101                 t = vector_lookup(r->read_threads, fd);
102                 if (t) {
103                         if (t != THREAD_RUNNING) {
104                                 THREAD_OFF(t);
105                         }
106                         vector_unset(r->read_threads, fd);
107                 }
108         }
109
110         if (writable) {
111                 t = vector_lookup_ensure(r->write_threads, fd);
112                 if (!t) {
113                         THREAD_READ_ON(master, t, resolver_cb_socket_writable, r, fd);
114                         vector_set_index(r->write_threads, fd, t);
115                 }
116         } else {
117                 t = vector_lookup(r->write_threads, fd);
118                 if (t) {
119                         if (t != THREAD_RUNNING) {
120                                 THREAD_OFF(t);
121                         }
122                         vector_unset(r->write_threads, fd);
123                 }
124         }
125 }
126
127 void resolver_init(void)
128 {
129         struct ares_options ares_opts;
130
131         state.read_threads = vector_init(1);
132         state.write_threads = vector_init(1);
133
134         ares_opts = (struct ares_options) {
135                 .sock_state_cb = &ares_socket_cb,
136                 .sock_state_cb_data = &state,
137                 .timeout = 2,
138                 .tries = 3,
139         };
140
141         ares_init_options(&state.channel, &ares_opts,
142                 ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT |
143                 ARES_OPT_TRIES);
144 }
145
146
147 static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he)
148 {
149         struct resolver_query *query = (struct resolver_query *) arg;
150         union sockunion addr[16];
151         size_t i;
152
153         if (status != ARES_SUCCESS) {
154                 debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query);
155                 query->callback(query, -1, NULL);
156                 query->callback = NULL;
157                 return;
158         }
159
160         for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); i++) {
161                 memset(&addr[i], 0, sizeof(addr[i]));
162                 addr[i].sa.sa_family = he->h_addrtype;
163                 switch (he->h_addrtype) {
164                 case AF_INET:
165                         memcpy(&addr[i].sin.sin_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
166                         break;
167                 case AF_INET6:
168                         memcpy(&addr[i].sin6.sin6_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
169                         break;
170                 }
171         }
172
173         debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, (int) i);
174         query->callback(query, i, &addr[0]);
175         query->callback = NULL;
176 }
177
178 void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*callback)(struct resolver_query *, int, union sockunion *))
179 {
180         if (query->callback != NULL) {
181                 zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname);
182                 return;
183         }
184
185         debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname);
186
187         query->callback = callback;
188         ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
189         resolver_update_timeouts(&state);
190 }