1 /*
2 * Copyright (c) 2006, Stefan Walter
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above
10 * copyright notice, this list of conditions and the
11 * following disclaimer.
12 * * Redistributions in binary form must reproduce the
13 * above copyright notice, this list of conditions and
14 * the following disclaimer in the documentation and/or
15 * other materials provided with the distribution.
16 * * The names of contributors to this software may not be
17 * used to endorse or promote products derived from this
18 * software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 * DAMAGE.
32 */
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
37 #include <netdb.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <pthread.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <stdio.h>
44 #include <assert.h>
45 #include <errno.h>
46
47 #include "async-resolver.h"
48 #include "server-mainloop.h"
49
50 /* -----------------------------------------------------------------------------
51 * THREAD COMMUNICATION
52 */
53
54 #define TSIGNAL_UNITITIALIZED { -1, -1 }
55
56 static int
tsignal_init(int * sig)57 tsignal_init(int* sig)
58 {
59 if(pipe(sig) == -1)
60 return -1;
61 fcntl(sig[0], F_SETFL, fcntl(sig[0], F_GETFL, 0) | O_NONBLOCK);
62 return 0;
63 }
64
65 static int
tsignal_get_fd(int * sig)66 tsignal_get_fd(int* sig)
67 {
68 return sig[0];
69 }
70
71 static void
tsignal_wake(int * sig)72 tsignal_wake(int* sig)
73 {
74 write(sig[1], "1", 1);
75 }
76
77 static void
tsignal_clear(int * sig)78 tsignal_clear(int* sig)
79 {
80 char buf[16];
81 if(sig[0] == -1)
82 return;
83 while(read(sig[0], buf, sizeof(buf)) > 0);
84 }
85
86 static void
tsignal_wait(int * sig,struct timeval * tv)87 tsignal_wait(int* sig, struct timeval* tv)
88 {
89 fd_set watch;
90 FD_ZERO(&watch);
91 FD_SET(sig[0], &watch);
92 select(sig[0], &watch, NULL, NULL, tv);
93 }
94
95 static void
tsignal_uninit(int * sig)96 tsignal_uninit(int* sig)
97 {
98 if(sig[1] != -1)
99 close(sig[1]);
100 sig[1] = -1;
101 if(sig[0] != -1)
102 close(sig[0]);
103 sig[0] = -1;
104 }
105
106 /* -----------------------------------------------------------------------------
107 * RESOLVER
108 */
109
110 typedef struct _resolve_request
111 {
112 char hostname[256];
113 char servname[256];
114 struct addrinfo hints;
115 async_resolve_callback cb;
116 void *arg;
117
118 int gaierr;
119 int errn;
120 struct addrinfo *ai;
121
122 struct _resolve_request *next;
123 }
124 resolve_request;
125
126 /* The queues */
127 static int res_quit = 0;
128 static resolve_request* res_requests = NULL;
129 static resolve_request* res_done = NULL;
130
131 /* Thread communication */
132 static pthread_t res_thread = 0;
133 static pthread_mutex_t res_mutex = PTHREAD_MUTEX_INITIALIZER;
134 static int res_request_signal[2] = TSIGNAL_UNITITIALIZED;
135 static int res_done_signal[2] = TSIGNAL_UNITITIALIZED;
136
137 static void*
resolver_thread(void * arg)138 resolver_thread(void* arg)
139 {
140 resolve_request* req;
141 resolve_request* r;
142 struct timeval tv;
143
144 while(!res_quit)
145 {
146 pthread_mutex_lock(&res_mutex);
147
148 /* Dig out any requests */
149 req = res_requests;
150 if(req)
151 {
152 res_requests = req->next;
153 req->next = NULL;
154 }
155
156 pthread_mutex_unlock(&res_mutex);
157
158 /* No requests, wait for a request */
159 if(!req)
160 {
161 tv.tv_sec = 0;
162 tv.tv_usec = 500000;
163 tsignal_wait(res_request_signal, &tv);
164 tsignal_clear(res_request_signal);
165 continue;
166 }
167
168 /* The actual resolve */
169 req->gaierr = getaddrinfo(req->hostname, req->servname[0] ? req->servname : NULL,
170 &req->hints, &req->ai);
171 req->errn = errno;
172
173 /* A timeout */
174 if(!req->gaierr && !req->ai)
175 {
176 req->gaierr = EAI_SYSTEM;
177 req->errn = ETIMEDOUT;
178 }
179
180 /* Append the result to done */
181 pthread_mutex_lock(&res_mutex);
182
183 if(!res_done)
184 {
185 res_done = req;
186 }
187 else
188 {
189 r = res_done;
190 while(r->next)
191 r = r->next;
192 r->next = req;
193 }
194
195 pthread_mutex_unlock(&res_mutex);
196
197 /* Tell the main thread to check outbound */
198 tsignal_wake(res_done_signal);
199 }
200
201 return NULL;
202 }
203
204 static void
resolver_done(int fd,int type,void * arg)205 resolver_done(int fd, int type, void* arg)
206 {
207 resolve_request* req;
208 resolve_request* r;
209
210 tsignal_clear(res_done_signal);
211
212 pthread_mutex_lock(&res_mutex);
213
214 req = res_done;
215 res_done = NULL;
216
217 pthread_mutex_unlock(&res_mutex);
218
219 while(req)
220 {
221 /* Send off the result */
222 errno = req->errn;
223 (req->cb)(req->gaierr, req->ai, req->arg);
224
225 /* And free it all */
226 r = req->next;
227 if(req->ai)
228 freeaddrinfo(req->ai);
229 free(req);
230
231 req = r;
232 }
233 }
234
235 int
async_resolver_init()236 async_resolver_init()
237 {
238 int r;
239
240 /* The signal pipes */
241 if(tsignal_init(res_request_signal) < 0)
242 return -1;
243 if(tsignal_init(res_done_signal) < 0)
244 return -1;
245
246 if(server_watch(tsignal_get_fd(res_done_signal), SERVER_READ, resolver_done, NULL) == -1)
247 return -1;
248
249 r = pthread_create(&res_thread, NULL, resolver_thread, NULL);
250 if(r != 0)
251 {
252 res_thread = 0;
253 return -1;
254 }
255
256 return 0;
257 }
258
259 void
async_resolver_queue(const char * hostname,const char * servname,struct addrinfo * hints,async_resolve_callback cb,void * arg)260 async_resolver_queue(const char* hostname, const char* servname,
261 struct addrinfo* hints, async_resolve_callback cb, void* arg)
262 {
263 resolve_request* req;
264 resolve_request* r;
265 char* t;
266
267 if(!res_thread)
268 {
269 /* All errors go to callback */
270 errno = ESRCH;
271 (cb)(EAI_SYSTEM, NULL, arg);
272 return;
273 }
274
275 req = calloc(1, sizeof(resolve_request));
276 if(!req)
277 {
278 /* All errors go to callback */
279 (cb)(EAI_MEMORY, NULL, arg);
280 return;
281 }
282
283 req->cb = cb;
284 req->arg = arg;
285
286 strncpy(req->hostname, hostname, sizeof(req->hostname));
287 req->hostname[sizeof(req->hostname) - 1] = 0;
288
289 /* A colon and we try to split */
290 t = strchr(req->hostname, ':');
291 if(t)
292 {
293 *t = 0;
294 strncpy(req->servname, t + 1, sizeof(req->servname));
295 }
296
297 if(servname && !req->servname[0])
298 strncpy(req->servname, servname, sizeof(req->servname));
299 req->servname[sizeof(req->servname) - 1] = 0;
300
301 if(hints)
302 memcpy(&(req->hints), hints, sizeof(req->hints));
303
304 /* Append the result to requests */
305 pthread_mutex_lock(&res_mutex);
306
307 if(!res_requests)
308 {
309 res_requests = req;
310 }
311 else
312 {
313 for(r = res_requests; r->next; r = r->next);
314 r->next = req;
315 }
316
317 pthread_mutex_unlock(&res_mutex);
318
319 tsignal_wake(res_request_signal);
320 }
321
322 void
async_resolver_uninit()323 async_resolver_uninit()
324 {
325 resolve_request* req;
326
327 /* No more responses from this point on */
328 if(tsignal_get_fd(res_done_signal) != -1)
329 server_unwatch(tsignal_get_fd(res_done_signal));
330
331 pthread_mutex_lock(&res_mutex);
332
333 while(res_requests)
334 {
335 req = res_requests->next;
336 if(res_requests->ai)
337 freeaddrinfo(res_requests->ai);
338 free(res_requests);
339 res_requests = req;
340 }
341
342 while(res_done)
343 {
344 req = res_done->next;
345 if(res_done->ai)
346 freeaddrinfo(res_done->ai);
347 free(res_done);
348 res_done = req;
349 }
350
351 pthread_mutex_unlock(&res_mutex);
352
353 /* Wake up the resolver thread */
354 res_quit = 1;
355 tsignal_uninit(res_request_signal);
356
357 /* Wait for it to finish */
358 if(res_thread)
359 {
360 pthread_join(res_thread, NULL);
361 res_thread = 0;
362 }
363
364 /* And close up the signals in the other direction */
365 tsignal_uninit(res_done_signal);
366 }
367