1 /* $Id: asyncns.c 27 2007-02-16 13:51:03Z lennart $ */ |
|
2 |
|
3 /*** |
|
4 This file is part of libasyncns. |
|
5 Copyright (C) 2006 Collabora Ltd. |
|
6 |
|
7 libasyncns is free software; you can redistribute it and/or modify |
|
8 it under the terms of the GNU Lesser General Public License as |
|
9 published by the Free Software Foundation; either version 2 of the |
|
10 License, or (at your option) any later version. |
|
11 |
|
12 libasyncns is distributed in the hope that it will be useful, but |
|
13 WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 General Public License for more details. |
|
16 |
|
17 You should have received a copy of the GNU Lesser General Public |
|
18 License along with libasyncns; if not, write to the Free Software |
|
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
|
20 USA. |
|
21 ***/ |
|
22 |
|
23 /*#undef HAVE_PTHREAD */ |
|
24 |
|
25 #include <assert.h> |
|
26 #include <fcntl.h> |
|
27 #include <signal.h> |
|
28 #include <unistd.h> |
|
29 #include <sys/select.h> |
|
30 #include <stdio.h> |
|
31 #include <string.h> |
|
32 #include <stdlib.h> |
|
33 #include <errno.h> |
|
34 #include <sys/wait.h> |
|
35 #include <sys/types.h> |
|
36 #include <pwd.h> |
|
37 #include <netinet/in.h> |
|
38 #include <arpa/nameser.h> |
|
39 #include <resolv.h> |
|
40 |
|
41 #ifdef HAVE_SYS_PRCTL_H |
|
42 #include <sys/prctl.h> |
|
43 #endif |
|
44 |
|
45 #if HAVE_PTHREAD |
|
46 #include <pthread.h> |
|
47 #endif |
|
48 |
|
49 #include "asyncns.h" |
|
50 |
|
51 #define MAX_WORKERS 16 |
|
52 #define MAX_QUERIES 256 |
|
53 #define BUFSIZE (10240) |
|
54 |
|
55 typedef enum { |
|
56 REQUEST_ADDRINFO, |
|
57 RESPONSE_ADDRINFO, |
|
58 REQUEST_NAMEINFO, |
|
59 RESPONSE_NAMEINFO, |
|
60 REQUEST_RES_QUERY, |
|
61 REQUEST_RES_SEARCH, |
|
62 RESPONSE_RES, |
|
63 REQUEST_TERMINATE |
|
64 } query_type_t; |
|
65 |
|
66 enum { |
|
67 REQUEST_RECV_FD = 0, |
|
68 REQUEST_SEND_FD = 1, |
|
69 RESPONSE_RECV_FD = 2, |
|
70 RESPONSE_SEND_FD = 3 |
|
71 }; |
|
72 |
|
73 struct asyncns { |
|
74 int fds[4]; |
|
75 |
|
76 #ifndef HAVE_PTHREAD |
|
77 pid_t workers[MAX_WORKERS]; |
|
78 #else |
|
79 pthread_t workers[MAX_WORKERS]; |
|
80 #endif |
|
81 unsigned valid_workers; |
|
82 |
|
83 unsigned current_id, current_index; |
|
84 asyncns_query_t* queries[MAX_QUERIES]; |
|
85 |
|
86 asyncns_query_t *done_head, *done_tail; |
|
87 |
|
88 int n_queries; |
|
89 }; |
|
90 |
|
91 struct asyncns_query { |
|
92 asyncns_t *asyncns; |
|
93 int done; |
|
94 unsigned id; |
|
95 query_type_t type; |
|
96 asyncns_query_t *done_next, *done_prev; |
|
97 int ret; |
|
98 struct addrinfo *addrinfo; |
|
99 char *serv, *host; |
|
100 void *userdata; |
|
101 }; |
|
102 |
|
103 typedef struct rheader { |
|
104 query_type_t type; |
|
105 unsigned id; |
|
106 size_t length; |
|
107 } rheader_t; |
|
108 |
|
109 typedef struct addrinfo_request { |
|
110 struct rheader header; |
|
111 int hints_is_null; |
|
112 int ai_flags; |
|
113 int ai_family; |
|
114 int ai_socktype; |
|
115 int ai_protocol; |
|
116 size_t node_len, service_len; |
|
117 } addrinfo_request_t; |
|
118 |
|
119 typedef struct addrinfo_response { |
|
120 struct rheader header; |
|
121 int ret; |
|
122 } addrinfo_response_t; |
|
123 |
|
124 typedef struct addrinfo_serialization { |
|
125 int ai_flags; |
|
126 int ai_family; |
|
127 int ai_socktype; |
|
128 int ai_protocol; |
|
129 size_t ai_addrlen; |
|
130 size_t canonname_len; |
|
131 } addrinfo_serialization_t; |
|
132 |
|
133 typedef struct nameinfo_request { |
|
134 struct rheader header; |
|
135 int flags; |
|
136 socklen_t sockaddr_len; |
|
137 int gethost, getserv; |
|
138 } nameinfo_request_t; |
|
139 |
|
140 typedef struct nameinfo_response { |
|
141 struct rheader header; |
|
142 size_t hostlen, servlen; |
|
143 int ret; |
|
144 } nameinfo_response_t; |
|
145 |
|
146 typedef struct res_query_request { |
|
147 struct rheader header; |
|
148 int class; |
|
149 int type; |
|
150 size_t dlen; |
|
151 } res_request_t; |
|
152 |
|
153 typedef struct res_query_response { |
|
154 struct rheader header; |
|
155 int ret; |
|
156 } res_response_t; |
|
157 |
|
158 #ifndef HAVE_STRNDUP |
|
159 |
|
160 static char *strndup(const char *s, size_t l) { |
|
161 size_t a; |
|
162 char *n; |
|
163 |
|
164 a = strlen(s); |
|
165 if (a > l) |
|
166 a = l; |
|
167 |
|
168 if (!(n = malloc(a+1))) |
|
169 return NULL; |
|
170 |
|
171 strncpy(n, s, a); |
|
172 n[a] = 0; |
|
173 |
|
174 return n; |
|
175 } |
|
176 |
|
177 #endif |
|
178 |
|
179 static int fd_nonblock(int fd) { |
|
180 int i; |
|
181 assert(fd >= 0); |
|
182 |
|
183 if ((i = fcntl(fd, F_GETFL, 0)) < 0) |
|
184 return -1; |
|
185 |
|
186 if (i & O_NONBLOCK) |
|
187 return 0; |
|
188 |
|
189 return fcntl(fd, F_SETFL, i | O_NONBLOCK); |
|
190 } |
|
191 |
|
192 static int fd_cloexec(int fd) { |
|
193 int v; |
|
194 assert(fd >= 0); |
|
195 |
|
196 if ((v = fcntl(fd, F_GETFD, 0)) < 0) |
|
197 return -1; |
|
198 |
|
199 if (v & FD_CLOEXEC) |
|
200 return 0; |
|
201 |
|
202 return fcntl(fd, F_SETFD, v | FD_CLOEXEC); |
|
203 } |
|
204 |
|
205 |
|
206 static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) { |
|
207 addrinfo_serialization_t *s = p; |
|
208 size_t cnl, l; |
|
209 assert(p); |
|
210 assert(ai); |
|
211 assert(length); |
|
212 assert(*length <= maxlength); |
|
213 |
|
214 cnl = (ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0); |
|
215 l = sizeof(addrinfo_serialization_t) + ai->ai_addrlen + cnl; |
|
216 |
|
217 if (*length + l > maxlength) |
|
218 return NULL; |
|
219 |
|
220 s->ai_flags = ai->ai_flags; |
|
221 s->ai_family = ai->ai_family; |
|
222 s->ai_socktype = ai->ai_socktype; |
|
223 s->ai_protocol = ai->ai_protocol; |
|
224 s->ai_addrlen = ai->ai_addrlen; |
|
225 s->canonname_len = cnl; |
|
226 |
|
227 memcpy((uint8_t*) p + sizeof(addrinfo_serialization_t), ai->ai_addr, ai->ai_addrlen); |
|
228 |
|
229 if (ai->ai_canonname) |
|
230 strcpy((char*) p + sizeof(addrinfo_serialization_t) + ai->ai_addrlen, ai->ai_canonname); |
|
231 |
|
232 *length += l; |
|
233 return (uint8_t*) p + l; |
|
234 } |
|
235 |
|
236 static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo *ai) { |
|
237 uint8_t data[BUFSIZE]; |
|
238 addrinfo_response_t *resp = (addrinfo_response_t*) data; |
|
239 assert(out_fd >= 0); |
|
240 |
|
241 resp->header.type = RESPONSE_ADDRINFO; |
|
242 resp->header.id = id; |
|
243 resp->header.length = sizeof(addrinfo_response_t); |
|
244 resp->ret = ret; |
|
245 |
|
246 if (ret == 0 && ai) { |
|
247 void *p = data + sizeof(addrinfo_response_t); |
|
248 |
|
249 while (ai && p) { |
|
250 p = serialize_addrinfo(p, ai, &resp->header.length, BUFSIZE); |
|
251 ai = ai->ai_next; |
|
252 } |
|
253 } |
|
254 |
|
255 if (ai) |
|
256 freeaddrinfo(ai); |
|
257 |
|
258 return send(out_fd, resp, resp->header.length, 0); |
|
259 } |
|
260 |
|
261 static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv) { |
|
262 uint8_t data[BUFSIZE]; |
|
263 size_t hl, sl; |
|
264 nameinfo_response_t *resp = (nameinfo_response_t*) data; |
|
265 |
|
266 assert(out_fd >= 0); |
|
267 |
|
268 sl = serv ? strlen(serv)+1 : 0; |
|
269 hl = host ? strlen(host)+1 : 0; |
|
270 |
|
271 resp->header.type = RESPONSE_NAMEINFO; |
|
272 resp->header.id = id; |
|
273 resp->header.length = sizeof(nameinfo_response_t) + hl + sl; |
|
274 resp->ret = ret; |
|
275 resp->hostlen = hl; |
|
276 resp->servlen = sl; |
|
277 |
|
278 assert(sizeof(data) >= resp->header.length); |
|
279 |
|
280 if (host) |
|
281 memcpy(data + sizeof(nameinfo_response_t), host, hl); |
|
282 |
|
283 if (serv) |
|
284 memcpy(data + sizeof(nameinfo_response_t) + hl, serv, sl); |
|
285 |
|
286 return send(out_fd, resp, resp->header.length, 0); |
|
287 } |
|
288 |
|
289 static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret) { |
|
290 uint8_t data[BUFSIZE]; |
|
291 res_response_t *resp = (res_response_t *) data; |
|
292 |
|
293 assert(out_fd >= 0); |
|
294 |
|
295 resp->header.type = RESPONSE_RES; |
|
296 resp->header.id = id; |
|
297 resp->header.length = sizeof(res_response_t) + (ret < 0 ? 0 : ret); |
|
298 resp->ret = (ret < 0) ? -errno : ret; |
|
299 |
|
300 assert(sizeof(data) >= resp->header.length); |
|
301 |
|
302 if (ret > 0) |
|
303 memcpy(data + sizeof(res_response_t), answer, ret); |
|
304 |
|
305 return send(out_fd, resp, resp->header.length, 0); |
|
306 } |
|
307 |
|
308 static int handle_request(int out_fd, const rheader_t *req, size_t length) { |
|
309 assert(out_fd >= 0); |
|
310 assert(req); |
|
311 assert(length >= sizeof(rheader_t)); |
|
312 assert(length == req->length); |
|
313 |
|
314 switch (req->type) { |
|
315 case REQUEST_ADDRINFO: { |
|
316 struct addrinfo ai, *result = NULL; |
|
317 const addrinfo_request_t *ai_req = (const addrinfo_request_t*) req; |
|
318 const char *node, *service; |
|
319 int ret; |
|
320 |
|
321 assert(length >= sizeof(addrinfo_request_t)); |
|
322 assert(length == sizeof(addrinfo_request_t) + ai_req->node_len + ai_req->service_len); |
|
323 |
|
324 memset(&ai, 0, sizeof(ai)); |
|
325 ai.ai_flags = ai_req->ai_flags; |
|
326 ai.ai_family = ai_req->ai_family; |
|
327 ai.ai_socktype = ai_req->ai_socktype; |
|
328 ai.ai_protocol = ai_req->ai_protocol; |
|
329 |
|
330 node = ai_req->node_len ? (const char*) req + sizeof(addrinfo_request_t) : NULL; |
|
331 service = ai_req->service_len ? (const char*) req + sizeof(addrinfo_request_t) + ai_req->node_len : NULL; |
|
332 |
|
333 /*printf("[getaddrinfo for '%s']\n", node);*/ |
|
334 ret = getaddrinfo(node, service, |
|
335 ai_req->hints_is_null ? NULL : &ai, |
|
336 &result); |
|
337 /*printf("[return value: %d: '%s']\n", ret, gai_strerror(ret));*/ |
|
338 |
|
339 /* send_addrinfo_reply() frees result */ |
|
340 return send_addrinfo_reply(out_fd, req->id, ret, result); |
|
341 } |
|
342 |
|
343 case REQUEST_NAMEINFO: { |
|
344 int ret; |
|
345 const nameinfo_request_t *ni_req = (const nameinfo_request_t*) req; |
|
346 char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; |
|
347 const struct sockaddr *sa; |
|
348 |
|
349 assert(length >= sizeof(nameinfo_request_t)); |
|
350 assert(length == sizeof(nameinfo_request_t) + ni_req->sockaddr_len); |
|
351 |
|
352 sa = (const struct sockaddr*) ((const char*) req + sizeof(nameinfo_request_t)); |
|
353 |
|
354 ret = getnameinfo(sa, ni_req->sockaddr_len, |
|
355 ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0, |
|
356 ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0, |
|
357 ni_req->flags); |
|
358 |
|
359 return send_nameinfo_reply(out_fd, req->id, ret, |
|
360 ret == 0 && ni_req->gethost ? hostbuf : NULL, |
|
361 ret == 0 && ni_req->getserv ? servbuf : NULL); |
|
362 } |
|
363 |
|
364 case REQUEST_RES_QUERY: |
|
365 case REQUEST_RES_SEARCH: { |
|
366 int ret; |
|
367 unsigned char answer[BUFSIZE]; |
|
368 const res_request_t *res_req = (const res_request_t *)req; |
|
369 const char *dname; |
|
370 |
|
371 assert(length >= sizeof(res_request_t)); |
|
372 assert(length == sizeof(res_request_t) + res_req->dlen + 1); |
|
373 |
|
374 dname = (const char *) req + sizeof(res_request_t); |
|
375 |
|
376 if (req->type == REQUEST_RES_QUERY) { |
|
377 /*printf("[res query for '%s']\n", dname);*/ |
|
378 ret = res_query(dname, res_req->class, res_req->type, |
|
379 answer, BUFSIZE); |
|
380 /*printf("[return value: %d]\n", ret);*/ |
|
381 } else { |
|
382 ret = res_search(dname, res_req->class, res_req->type, |
|
383 answer, BUFSIZE); |
|
384 } |
|
385 return send_res_reply(out_fd, req->id, answer, ret); |
|
386 } |
|
387 |
|
388 case REQUEST_TERMINATE: { |
|
389 /* Quit */ |
|
390 return -1; |
|
391 } |
|
392 |
|
393 default: |
|
394 ; |
|
395 } |
|
396 |
|
397 return 0; |
|
398 } |
|
399 |
|
400 #ifndef HAVE_PTHREAD |
|
401 |
|
402 static int process_worker(int in_fd, int out_fd) { |
|
403 int have_death_sig = 0; |
|
404 assert(in_fd > 2); |
|
405 assert(out_fd > 2); |
|
406 |
|
407 close(0); |
|
408 close(1); |
|
409 close(2); |
|
410 |
|
411 open("/dev/null", O_RDONLY); |
|
412 open("/dev/null", O_WRONLY); |
|
413 open("/dev/null", O_WRONLY); |
|
414 |
|
415 chdir("/"); |
|
416 |
|
417 if (geteuid() == 0) { |
|
418 struct passwd *pw; |
|
419 |
|
420 if ((pw = getpwnam("nobody"))) { |
|
421 #ifdef HAVE_SETRESUID |
|
422 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); |
|
423 #elif HAVE_SETREUID |
|
424 setreuid(pw->pw_uid, pw->pw_uid); |
|
425 #else |
|
426 setuid(pw->pw_uid); |
|
427 seteuid(pw->pw_uid); |
|
428 #endif |
|
429 } |
|
430 } |
|
431 |
|
432 signal(SIGTERM, SIG_DFL); |
|
433 |
|
434 signal(SIGINT, SIG_IGN); |
|
435 signal(SIGHUP, SIG_IGN); |
|
436 signal(SIGPIPE, SIG_IGN); |
|
437 signal(SIGUSR1, SIG_IGN); |
|
438 signal(SIGUSR2, SIG_IGN); |
|
439 |
|
440 #ifdef PR_SET_PDEATHSIG |
|
441 if (prctl(PR_SET_PDEATHSIG, SIGTERM) >= 0) |
|
442 have_death_sig = 1; |
|
443 #endif |
|
444 |
|
445 if (!have_death_sig) |
|
446 fd_nonblock(in_fd); |
|
447 |
|
448 while (getppid() > 1) { /* if the parent PID is 1 our parent process died. */ |
|
449 char buf[BUFSIZE]; |
|
450 ssize_t length; |
|
451 |
|
452 if (!have_death_sig) { |
|
453 fd_set fds; |
|
454 struct timeval tv = { 0, 500000 } ; |
|
455 |
|
456 FD_ZERO(&fds); |
|
457 FD_SET(in_fd, &fds); |
|
458 |
|
459 if (select(in_fd+1, &fds, NULL, NULL, &tv) < 0) |
|
460 break; |
|
461 |
|
462 if (getppid() == 1) |
|
463 break; |
|
464 } |
|
465 |
|
466 if ((length = recv(in_fd, buf, sizeof(buf), 0)) <= 0) { |
|
467 |
|
468 if (length < 0 && errno == EAGAIN) |
|
469 continue; |
|
470 |
|
471 break; |
|
472 } |
|
473 |
|
474 if (handle_request(out_fd, (rheader_t*) buf, (size_t) length) < 0) |
|
475 break; |
|
476 } |
|
477 |
|
478 close(in_fd); |
|
479 close(out_fd); |
|
480 |
|
481 return 0; |
|
482 } |
|
483 |
|
484 #else |
|
485 |
|
486 static void* thread_worker(void *p) { |
|
487 sigset_t fullset; |
|
488 int *fds = p; |
|
489 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); |
|
490 |
|
491 /* No signals in this thread please */ |
|
492 sigfillset(&fullset); |
|
493 pthread_sigmask(SIG_BLOCK, &fullset, NULL); |
|
494 |
|
495 for (;;) { |
|
496 char buf[BUFSIZE]; |
|
497 ssize_t length; |
|
498 |
|
499 if ((length = recv(fds[REQUEST_RECV_FD], buf, sizeof(buf), 0)) <= 0) |
|
500 break; |
|
501 |
|
502 if (handle_request(fds[RESPONSE_SEND_FD], (rheader_t*) buf, (size_t) length) < 0) |
|
503 break; |
|
504 |
|
505 } |
|
506 |
|
507 return NULL; |
|
508 } |
|
509 |
|
510 #endif |
|
511 |
|
512 asyncns_t* asyncns_new(unsigned n_proc) { |
|
513 asyncns_t *asyncns = NULL; |
|
514 int i; |
|
515 unsigned p; |
|
516 assert(n_proc >= 1); |
|
517 |
|
518 if (n_proc > MAX_WORKERS) |
|
519 n_proc = MAX_WORKERS; |
|
520 |
|
521 if (!(asyncns = malloc(sizeof(asyncns_t)))) |
|
522 goto fail; |
|
523 |
|
524 asyncns->valid_workers = 0; |
|
525 |
|
526 for (i = 0; i < 4; i++) |
|
527 asyncns->fds[i] = -1; |
|
528 |
|
529 for (p = 0; p < MAX_QUERIES; p++) |
|
530 asyncns->queries[p] = NULL; |
|
531 |
|
532 if (socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds) < 0 || |
|
533 socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds+2) < 0) |
|
534 goto fail; |
|
535 |
|
536 for (i = 0; i < 4; i++) |
|
537 fd_cloexec(asyncns->fds[i]); |
|
538 |
|
539 for (asyncns->valid_workers = 0; asyncns->valid_workers < n_proc; asyncns->valid_workers++) { |
|
540 |
|
541 #ifndef HAVE_PTHREAD |
|
542 if ((asyncns->workers[asyncns->valid_workers] = fork()) < 0) |
|
543 goto fail; |
|
544 else if (asyncns->workers[asyncns->valid_workers] == 0) { |
|
545 close(asyncns->fds[REQUEST_SEND_FD]); |
|
546 close(asyncns->fds[RESPONSE_RECV_FD]); |
|
547 _exit(process_worker(asyncns->fds[REQUEST_RECV_FD], asyncns->fds[RESPONSE_SEND_FD])); |
|
548 } |
|
549 #else |
|
550 if (pthread_create(&asyncns->workers[asyncns->valid_workers], NULL, thread_worker, asyncns->fds) != 0) |
|
551 goto fail; |
|
552 #endif |
|
553 } |
|
554 |
|
555 #ifndef HAVE_PTHREAD |
|
556 close(asyncns->fds[REQUEST_RECV_FD]); |
|
557 close(asyncns->fds[RESPONSE_SEND_FD]); |
|
558 asyncns->fds[REQUEST_RECV_FD] = asyncns->fds[RESPONSE_SEND_FD] = -1; |
|
559 #endif |
|
560 |
|
561 asyncns->current_index = asyncns->current_id = 0; |
|
562 asyncns->done_head = asyncns->done_tail = NULL; |
|
563 asyncns->n_queries = 0; |
|
564 |
|
565 fd_nonblock(asyncns->fds[RESPONSE_RECV_FD]); |
|
566 |
|
567 return asyncns; |
|
568 |
|
569 fail: |
|
570 if (asyncns) |
|
571 asyncns_free(asyncns); |
|
572 |
|
573 return NULL; |
|
574 } |
|
575 |
|
576 void asyncns_free(asyncns_t *asyncns) { |
|
577 unsigned p; |
|
578 int i; |
|
579 rheader_t req; |
|
580 assert(asyncns); |
|
581 |
|
582 req.type = REQUEST_TERMINATE; |
|
583 req.length = sizeof(req); |
|
584 req.id = 0; |
|
585 |
|
586 /* Send one termiantion packet for each worker */ |
|
587 for (p = 0; p < asyncns->valid_workers; p++) |
|
588 send(asyncns->fds[REQUEST_SEND_FD], &req, req.length, 0); |
|
589 |
|
590 /* No terminate them forcibly*/ |
|
591 for (p = 0; p < asyncns->valid_workers; p++) { |
|
592 #ifndef HAVE_PTHREAD |
|
593 kill(asyncns->workers[p], SIGTERM); |
|
594 waitpid(asyncns->workers[p], NULL, 0); |
|
595 #else |
|
596 pthread_cancel(asyncns->workers[p]); |
|
597 pthread_join(asyncns->workers[p], NULL); |
|
598 #endif |
|
599 } |
|
600 |
|
601 /* Due to Solaris' broken thread cancelation we first send an |
|
602 * termination request and then cancel th thread. */ |
|
603 |
|
604 |
|
605 for (i = 0; i < 4; i++) |
|
606 if (asyncns->fds[i] >= 0) |
|
607 close(asyncns->fds[i]); |
|
608 |
|
609 for (p = 0; p < MAX_QUERIES; p++) |
|
610 if (asyncns->queries[p]) |
|
611 asyncns_cancel(asyncns, asyncns->queries[p]); |
|
612 |
|
613 free(asyncns); |
|
614 } |
|
615 |
|
616 int asyncns_fd(asyncns_t *asyncns) { |
|
617 assert(asyncns); |
|
618 |
|
619 return asyncns->fds[RESPONSE_RECV_FD]; |
|
620 } |
|
621 |
|
622 static asyncns_query_t *lookup_query(asyncns_t *asyncns, unsigned id) { |
|
623 asyncns_query_t *q; |
|
624 assert(asyncns); |
|
625 |
|
626 if ((q = asyncns->queries[id % MAX_QUERIES])) |
|
627 if (q->id == id) |
|
628 return q; |
|
629 |
|
630 return NULL; |
|
631 } |
|
632 |
|
633 static void complete_query(asyncns_t *asyncns, asyncns_query_t *q) { |
|
634 assert(asyncns); |
|
635 assert(q); |
|
636 assert(!q->done); |
|
637 |
|
638 q->done = 1; |
|
639 |
|
640 if ((q->done_prev = asyncns->done_tail)) |
|
641 asyncns->done_tail->done_next = q; |
|
642 else |
|
643 asyncns->done_head = q; |
|
644 |
|
645 asyncns->done_tail = q; |
|
646 q->done_next = NULL; |
|
647 } |
|
648 |
|
649 static void *unserialize_addrinfo(void *p, struct addrinfo **ret_ai, size_t *length) { |
|
650 addrinfo_serialization_t *s = p; |
|
651 size_t l; |
|
652 struct addrinfo *ai; |
|
653 assert(p); |
|
654 assert(ret_ai); |
|
655 assert(length); |
|
656 |
|
657 if (*length < sizeof(addrinfo_serialization_t)) |
|
658 return NULL; |
|
659 |
|
660 l = sizeof(addrinfo_serialization_t) + s->ai_addrlen + s->canonname_len; |
|
661 if (*length < l) |
|
662 return NULL; |
|
663 |
|
664 if (!(ai = malloc(sizeof(struct addrinfo)))) |
|
665 goto fail; |
|
666 |
|
667 ai->ai_addr = NULL; |
|
668 ai->ai_canonname = NULL; |
|
669 ai->ai_next = NULL; |
|
670 |
|
671 if (s->ai_addrlen && !(ai->ai_addr = malloc(s->ai_addrlen))) |
|
672 goto fail; |
|
673 |
|
674 if (s->canonname_len && !(ai->ai_canonname = malloc(s->canonname_len))) |
|
675 goto fail; |
|
676 |
|
677 ai->ai_flags = s->ai_flags; |
|
678 ai->ai_family = s->ai_family; |
|
679 ai->ai_socktype = s->ai_socktype; |
|
680 ai->ai_protocol = s->ai_protocol; |
|
681 ai->ai_addrlen = s->ai_addrlen; |
|
682 |
|
683 if (ai->ai_addr) |
|
684 memcpy(ai->ai_addr, (uint8_t*) p + sizeof(addrinfo_serialization_t), s->ai_addrlen); |
|
685 |
|
686 if (ai->ai_canonname) |
|
687 memcpy(ai->ai_canonname, (uint8_t*) p + sizeof(addrinfo_serialization_t) + s->ai_addrlen, s->canonname_len); |
|
688 |
|
689 *length -= l; |
|
690 *ret_ai = ai; |
|
691 |
|
692 return (uint8_t*) p + l; |
|
693 |
|
694 |
|
695 fail: |
|
696 if (ai) |
|
697 asyncns_freeaddrinfo(ai); |
|
698 |
|
699 return NULL; |
|
700 } |
|
701 |
|
702 static int handle_response(asyncns_t *asyncns, rheader_t *resp, size_t length) { |
|
703 asyncns_query_t *q; |
|
704 assert(asyncns); |
|
705 assert(resp); |
|
706 assert(length >= sizeof(rheader_t)); |
|
707 assert(length == resp->length); |
|
708 |
|
709 if (!(q = lookup_query(asyncns, resp->id))) |
|
710 return 0; |
|
711 |
|
712 switch (resp->type) { |
|
713 case RESPONSE_ADDRINFO: { |
|
714 const addrinfo_response_t *ai_resp = (addrinfo_response_t*) resp; |
|
715 void *p; |
|
716 size_t l; |
|
717 struct addrinfo *prev = NULL; |
|
718 |
|
719 assert(length >= sizeof(addrinfo_response_t)); |
|
720 assert(q->type == REQUEST_ADDRINFO); |
|
721 |
|
722 q->ret = ai_resp->ret; |
|
723 l = length - sizeof(addrinfo_response_t); |
|
724 p = (uint8_t*) resp + sizeof(addrinfo_response_t); |
|
725 |
|
726 while (l > 0 && p) { |
|
727 struct addrinfo *ai = NULL; |
|
728 p = unserialize_addrinfo(p, &ai, &l); |
|
729 |
|
730 if (!ai) |
|
731 break; |
|
732 |
|
733 if (prev) |
|
734 prev->ai_next = ai; |
|
735 else |
|
736 q->addrinfo = ai; |
|
737 |
|
738 prev = ai; |
|
739 } |
|
740 |
|
741 complete_query(asyncns, q); |
|
742 break; |
|
743 } |
|
744 |
|
745 case RESPONSE_NAMEINFO: { |
|
746 const nameinfo_response_t *ni_resp = (nameinfo_response_t*) resp; |
|
747 |
|
748 assert(length >= sizeof(nameinfo_response_t)); |
|
749 assert(q->type == REQUEST_NAMEINFO); |
|
750 |
|
751 q->ret = ni_resp->ret; |
|
752 |
|
753 if (ni_resp->hostlen) |
|
754 q->host = strndup((const char*) ni_resp + sizeof(nameinfo_response_t), ni_resp->hostlen-1); |
|
755 |
|
756 if (ni_resp->servlen) |
|
757 q->serv = strndup((const char*) ni_resp + sizeof(nameinfo_response_t) + ni_resp->hostlen, ni_resp->servlen-1); |
|
758 |
|
759 |
|
760 complete_query(asyncns, q); |
|
761 break; |
|
762 } |
|
763 |
|
764 case RESPONSE_RES: { |
|
765 const res_response_t *res_resp = (res_response_t *)resp; |
|
766 |
|
767 assert(length >= sizeof(res_response_t)); |
|
768 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH); |
|
769 |
|
770 q->ret = res_resp->ret; |
|
771 |
|
772 if (res_resp->ret >= 0) { |
|
773 q->serv = malloc(res_resp->ret); |
|
774 memcpy(q->serv, (char *)resp + sizeof(res_response_t), res_resp->ret); |
|
775 } |
|
776 |
|
777 complete_query(asyncns, q); |
|
778 break; |
|
779 } |
|
780 |
|
781 default: |
|
782 ; |
|
783 } |
|
784 |
|
785 return 0; |
|
786 } |
|
787 |
|
788 int asyncns_wait(asyncns_t *asyncns, int block) { |
|
789 int handled = 0; |
|
790 assert(asyncns); |
|
791 |
|
792 for (;;) { |
|
793 char buf[BUFSIZE]; |
|
794 ssize_t l; |
|
795 |
|
796 if (((l = recv(asyncns->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0)) < 0)) { |
|
797 fd_set fds; |
|
798 |
|
799 if (errno != EAGAIN) |
|
800 return -1; |
|
801 |
|
802 if (!block || handled) |
|
803 return 0; |
|
804 |
|
805 FD_ZERO(&fds); |
|
806 FD_SET(asyncns->fds[RESPONSE_RECV_FD], &fds); |
|
807 |
|
808 if (select(asyncns->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0) |
|
809 return -1; |
|
810 |
|
811 continue; |
|
812 } |
|
813 |
|
814 |
|
815 if (handle_response(asyncns, (rheader_t*) buf, (size_t) l) < 0) |
|
816 return -1; |
|
817 |
|
818 handled = 1; |
|
819 } |
|
820 } |
|
821 |
|
822 static asyncns_query_t *alloc_query(asyncns_t *asyncns) { |
|
823 asyncns_query_t *q; |
|
824 assert(asyncns); |
|
825 |
|
826 if (asyncns->n_queries >= MAX_QUERIES) |
|
827 return NULL; |
|
828 |
|
829 while (asyncns->queries[asyncns->current_index]) { |
|
830 |
|
831 asyncns->current_index++; |
|
832 asyncns->current_id++; |
|
833 |
|
834 while (asyncns->current_index >= MAX_QUERIES) |
|
835 asyncns->current_index -= MAX_QUERIES; |
|
836 } |
|
837 |
|
838 if (!(q = asyncns->queries[asyncns->current_index] = malloc(sizeof(asyncns_query_t)))) |
|
839 return NULL; |
|
840 |
|
841 asyncns->n_queries++; |
|
842 |
|
843 q->asyncns = asyncns; |
|
844 q->done = 0; |
|
845 q->id = asyncns->current_id; |
|
846 q->done_next = q->done_prev = NULL; |
|
847 q->ret = 0; |
|
848 q->addrinfo = NULL; |
|
849 q->userdata = NULL; |
|
850 q->host = q->serv = NULL; |
|
851 |
|
852 return q; |
|
853 } |
|
854 |
|
855 asyncns_query_t* asyncns_getaddrinfo(asyncns_t *asyncns, const char *node, const char *service, const struct addrinfo *hints) { |
|
856 uint8_t data[BUFSIZE]; |
|
857 addrinfo_request_t *req = (addrinfo_request_t*) data; |
|
858 asyncns_query_t *q; |
|
859 assert(asyncns); |
|
860 assert(node || service); |
|
861 |
|
862 if (!(q = alloc_query(asyncns))) |
|
863 return NULL; |
|
864 |
|
865 memset(req, 0, sizeof(addrinfo_request_t)); |
|
866 |
|
867 req->node_len = node ? strlen(node)+1 : 0; |
|
868 req->service_len = service ? strlen(service)+1 : 0; |
|
869 |
|
870 req->header.id = q->id; |
|
871 req->header.type = q->type = REQUEST_ADDRINFO; |
|
872 req->header.length = sizeof(addrinfo_request_t) + req->node_len + req->service_len; |
|
873 |
|
874 if (req->header.length > BUFSIZE) |
|
875 goto fail; |
|
876 |
|
877 if (!(req->hints_is_null = !hints)) { |
|
878 req->ai_flags = hints->ai_flags; |
|
879 req->ai_family = hints->ai_family; |
|
880 req->ai_socktype = hints->ai_socktype; |
|
881 req->ai_protocol = hints->ai_protocol; |
|
882 } |
|
883 |
|
884 if (node) |
|
885 strcpy((char*) req + sizeof(addrinfo_request_t), node); |
|
886 |
|
887 if (service) |
|
888 strcpy((char*) req + sizeof(addrinfo_request_t) + req->node_len, service); |
|
889 |
|
890 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, 0) < 0) |
|
891 goto fail; |
|
892 |
|
893 return q; |
|
894 |
|
895 fail: |
|
896 if (q) |
|
897 asyncns_cancel(asyncns, q); |
|
898 |
|
899 return NULL; |
|
900 } |
|
901 |
|
902 int asyncns_getaddrinfo_done(asyncns_t *asyncns, asyncns_query_t* q, struct addrinfo **ret_res) { |
|
903 int ret; |
|
904 assert(asyncns); |
|
905 assert(q); |
|
906 assert(q->asyncns == asyncns); |
|
907 assert(q->type == REQUEST_ADDRINFO); |
|
908 |
|
909 if (!q->done) |
|
910 return EAI_AGAIN; |
|
911 |
|
912 *ret_res = q->addrinfo; |
|
913 q->addrinfo = NULL; |
|
914 ret = q->ret; |
|
915 asyncns_cancel(asyncns, q); |
|
916 |
|
917 return ret; |
|
918 } |
|
919 |
|
920 asyncns_query_t* asyncns_getnameinfo(asyncns_t *asyncns, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) { |
|
921 uint8_t data[BUFSIZE]; |
|
922 nameinfo_request_t *req = (nameinfo_request_t*) data; |
|
923 asyncns_query_t *q; |
|
924 |
|
925 assert(asyncns); |
|
926 assert(sa); |
|
927 assert(salen > 0); |
|
928 |
|
929 if (!(q = alloc_query(asyncns))) |
|
930 return NULL; |
|
931 |
|
932 memset(req, 0, sizeof(nameinfo_request_t)); |
|
933 |
|
934 req->header.id = q->id; |
|
935 req->header.type = q->type = REQUEST_NAMEINFO; |
|
936 req->header.length = sizeof(nameinfo_request_t) + salen; |
|
937 |
|
938 if (req->header.length > BUFSIZE) |
|
939 goto fail; |
|
940 |
|
941 req->flags = flags; |
|
942 req->sockaddr_len = salen; |
|
943 req->gethost = gethost; |
|
944 req->getserv = getserv; |
|
945 |
|
946 memcpy((uint8_t*) req + sizeof(nameinfo_request_t), sa, salen); |
|
947 |
|
948 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, 0) < 0) |
|
949 goto fail; |
|
950 |
|
951 return q; |
|
952 |
|
953 fail: |
|
954 if (q) |
|
955 asyncns_cancel(asyncns, q); |
|
956 |
|
957 return NULL; |
|
958 } |
|
959 |
|
960 int asyncns_getnameinfo_done(asyncns_t *asyncns, asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) { |
|
961 int ret; |
|
962 assert(asyncns); |
|
963 assert(q); |
|
964 assert(q->asyncns == asyncns); |
|
965 assert(q->type == REQUEST_NAMEINFO); |
|
966 assert(!ret_host || hostlen); |
|
967 assert(!ret_serv || servlen); |
|
968 |
|
969 if (!q->done) |
|
970 return EAI_AGAIN; |
|
971 |
|
972 if (ret_host && q->host) { |
|
973 strncpy(ret_host, q->host, hostlen); |
|
974 ret_host[hostlen-1] = 0; |
|
975 } |
|
976 |
|
977 if (ret_serv && q->serv) { |
|
978 strncpy(ret_serv, q->serv, servlen); |
|
979 ret_serv[servlen-1] = 0; |
|
980 } |
|
981 |
|
982 ret = q->ret; |
|
983 asyncns_cancel(asyncns, q); |
|
984 |
|
985 return ret; |
|
986 } |
|
987 |
|
988 static asyncns_query_t * |
|
989 asyncns_res(asyncns_t *asyncns, query_type_t qtype, |
|
990 const char *dname, int class, int type) { |
|
991 uint8_t data[BUFSIZE]; |
|
992 res_request_t *req = (res_request_t*) data; |
|
993 asyncns_query_t *q; |
|
994 size_t dlen; |
|
995 |
|
996 assert(asyncns); |
|
997 assert(dname); |
|
998 |
|
999 dlen = strlen(dname); |
|
1000 |
|
1001 if (!(q = alloc_query(asyncns))) |
|
1002 return NULL; |
|
1003 |
|
1004 memset(req, 0, sizeof(res_request_t)); |
|
1005 |
|
1006 req->header.id = q->id; |
|
1007 req->header.type = q->type = qtype; |
|
1008 req->header.length = sizeof(res_request_t) + dlen + 1; |
|
1009 |
|
1010 if (req->header.length > BUFSIZE) |
|
1011 goto fail; |
|
1012 |
|
1013 req->class = class; |
|
1014 req->type = type; |
|
1015 req->dlen = dlen; |
|
1016 |
|
1017 memcpy((uint8_t*) req + sizeof(res_request_t), dname, dlen + 1); |
|
1018 |
|
1019 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, 0) < 0) |
|
1020 goto fail; |
|
1021 |
|
1022 return q; |
|
1023 |
|
1024 fail: |
|
1025 if (q) |
|
1026 asyncns_cancel(asyncns, q); |
|
1027 |
|
1028 return NULL; |
|
1029 } |
|
1030 |
|
1031 asyncns_query_t* asyncns_res_query(asyncns_t *asyncns, const char *dname, int class, int type) { |
|
1032 return asyncns_res(asyncns, REQUEST_RES_QUERY, dname, class, type); |
|
1033 } |
|
1034 |
|
1035 asyncns_query_t* asyncns_res_search(asyncns_t *asyncns, const char *dname, int class, int type) { |
|
1036 return asyncns_res(asyncns, REQUEST_RES_SEARCH, dname, class, type); |
|
1037 } |
|
1038 |
|
1039 int asyncns_res_done(asyncns_t *asyncns, asyncns_query_t* q, unsigned char **answer) { |
|
1040 int ret; |
|
1041 assert(asyncns); |
|
1042 assert(q); |
|
1043 assert(q->asyncns == asyncns); |
|
1044 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH); |
|
1045 assert(answer); |
|
1046 |
|
1047 if (!q->done) |
|
1048 return -EAGAIN; |
|
1049 |
|
1050 *answer = (unsigned char *)q->serv; |
|
1051 q->serv = NULL; |
|
1052 |
|
1053 ret = q->ret; |
|
1054 |
|
1055 asyncns_cancel(asyncns, q); |
|
1056 |
|
1057 return ret; |
|
1058 } |
|
1059 |
|
1060 asyncns_query_t* asyncns_getnext(asyncns_t *asyncns) { |
|
1061 assert(asyncns); |
|
1062 return asyncns->done_head; |
|
1063 } |
|
1064 |
|
1065 int asyncns_getnqueries(asyncns_t *asyncns) { |
|
1066 assert(asyncns); |
|
1067 return asyncns->n_queries; |
|
1068 } |
|
1069 |
|
1070 void asyncns_cancel(asyncns_t *asyncns, asyncns_query_t* q) { |
|
1071 int i; |
|
1072 assert(asyncns); |
|
1073 assert(q); |
|
1074 assert(q->asyncns == asyncns); |
|
1075 assert(asyncns->n_queries > 0); |
|
1076 |
|
1077 if (q->done) { |
|
1078 |
|
1079 if (q->done_prev) |
|
1080 q->done_prev->done_next = q->done_next; |
|
1081 else |
|
1082 asyncns->done_head = q->done_next; |
|
1083 |
|
1084 if (q->done_next) |
|
1085 q->done_next->done_prev = q->done_prev; |
|
1086 else |
|
1087 asyncns->done_tail = q->done_prev; |
|
1088 } |
|
1089 |
|
1090 |
|
1091 i = q->id % MAX_QUERIES; |
|
1092 assert(asyncns->queries[i] == q); |
|
1093 asyncns->queries[i] = NULL; |
|
1094 |
|
1095 asyncns_freeaddrinfo(q->addrinfo); |
|
1096 free(q->addrinfo); |
|
1097 free(q->host); |
|
1098 free(q->serv); |
|
1099 |
|
1100 asyncns->n_queries--; |
|
1101 free(q); |
|
1102 } |
|
1103 |
|
1104 void asyncns_freeaddrinfo(struct addrinfo *ai) { |
|
1105 if (!ai) |
|
1106 return; |
|
1107 |
|
1108 while (ai) { |
|
1109 struct addrinfo *next = ai->ai_next; |
|
1110 |
|
1111 free(ai->ai_addr); |
|
1112 free(ai->ai_canonname); |
|
1113 free(ai); |
|
1114 |
|
1115 ai = next; |
|
1116 } |
|
1117 } |
|
1118 |
|
1119 int asyncns_isdone(asyncns_t *asyncns, asyncns_query_t*q) { |
|
1120 assert(asyncns); |
|
1121 assert(q); |
|
1122 assert(q->asyncns == asyncns); |
|
1123 |
|
1124 return q->done; |
|
1125 } |
|
1126 |
|
1127 void asyncns_setuserdata(asyncns_t *asyncns, asyncns_query_t *q, void *userdata) { |
|
1128 assert(q); |
|
1129 assert(asyncns); |
|
1130 assert(q->asyncns = asyncns); |
|
1131 |
|
1132 q->userdata = userdata; |
|
1133 } |
|
1134 |
|
1135 void* asyncns_getuserdata(asyncns_t *asyncns, asyncns_query_t *q) { |
|
1136 assert(q); |
|
1137 assert(asyncns); |
|
1138 assert(q->asyncns = asyncns); |
|
1139 |
|
1140 return q->userdata; |
|
1141 } |
|
1142 |
|
1143 |
|