1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2005-2008 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/select.h>
32 #include <sys/types.h>
34 #include <netinet/in.h>
35 #include <arpa/nameser.h>
39 #include <sys/resource.h>
42 #include <sys/prctl.h>
47 #define MAX_WORKERS 16
48 #define MAX_QUERIES 256
49 #define BUFSIZE (10240)
72 int fds[MESSAGE_FD_MAX];
74 pthread_t workers[MAX_WORKERS];
75 unsigned valid_workers;
77 unsigned current_id, current_index;
78 asyncns_query_t* queries[MAX_QUERIES];
80 asyncns_query_t *done_head, *done_tail;
86 struct asyncns_query {
91 asyncns_query_t *done_next, *done_prev;
95 struct addrinfo *addrinfo;
100 typedef struct rheader {
106 typedef struct addrinfo_request {
107 struct rheader header;
113 size_t node_len, service_len;
114 } addrinfo_request_t;
116 typedef struct addrinfo_response {
117 struct rheader header;
121 /* followed by addrinfo_serialization[] */
122 } addrinfo_response_t;
124 typedef struct addrinfo_serialization {
130 size_t canonname_len;
131 /* Followed by ai_addr amd ai_canonname with variable lengths */
132 } addrinfo_serialization_t;
134 typedef struct nameinfo_request {
135 struct rheader header;
137 socklen_t sockaddr_len;
138 int gethost, getserv;
139 } nameinfo_request_t;
141 typedef struct nameinfo_response {
142 struct rheader header;
143 size_t hostlen, servlen;
147 } nameinfo_response_t;
149 typedef struct res_request {
150 struct rheader header;
156 typedef struct res_response {
157 struct rheader header;
163 typedef union packet {
165 addrinfo_request_t addrinfo_request;
166 addrinfo_response_t addrinfo_response;
167 nameinfo_request_t nameinfo_request;
168 nameinfo_response_t nameinfo_response;
169 res_request_t res_request;
170 res_response_t res_response;
173 static int send_died(int out_fd) {
177 rh.type = RESPONSE_DIED;
179 rh.length = sizeof(rh);
181 return send(out_fd, &rh, rh.length, MSG_NOSIGNAL);
184 static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) {
185 addrinfo_serialization_t s;
190 assert(*length <= maxlength);
192 cnl = (ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0);
193 l = sizeof(addrinfo_serialization_t) + ai->ai_addrlen + cnl;
195 if (*length + l > maxlength)
198 s.ai_flags = ai->ai_flags;
199 s.ai_family = ai->ai_family;
200 s.ai_socktype = ai->ai_socktype;
201 s.ai_protocol = ai->ai_protocol;
202 s.ai_addrlen = ai->ai_addrlen;
203 s.canonname_len = cnl;
205 memcpy((uint8_t*) p, &s, sizeof(addrinfo_serialization_t));
206 memcpy((uint8_t*) p + sizeof(addrinfo_serialization_t), ai->ai_addr, ai->ai_addrlen);
208 if (ai->ai_canonname)
209 strcpy((char*) p + sizeof(addrinfo_serialization_t) + ai->ai_addrlen, ai->ai_canonname);
212 return (uint8_t*) p + l;
215 static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo *ai, int _errno, int _h_errno) {
216 addrinfo_response_t data[BUFSIZE/sizeof(addrinfo_response_t) + 1] = {};
217 addrinfo_response_t *resp = data;
220 resp->header.type = RESPONSE_ADDRINFO;
221 resp->header.id = id;
222 resp->header.length = sizeof(addrinfo_response_t);
224 resp->_errno = _errno;
225 resp->_h_errno = _h_errno;
227 if (ret == 0 && ai) {
231 for (k = ai; k; k = k->ai_next) {
232 if (!(p = serialize_addrinfo(p, k, &resp->header.length, (char*) data + BUFSIZE - (char*) p))) {
233 resp->ret = EAI_MEMORY;
242 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
245 static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) {
246 nameinfo_response_t data[BUFSIZE/sizeof(nameinfo_response_t) + 1] = {};
248 nameinfo_response_t *resp = data;
252 sl = serv ? strlen(serv)+1 : 0;
253 hl = host ? strlen(host)+1 : 0;
255 resp->header.type = RESPONSE_NAMEINFO;
256 resp->header.id = id;
257 resp->header.length = sizeof(nameinfo_response_t) + hl + sl;
259 resp->_errno = _errno;
260 resp->_h_errno = _h_errno;
264 assert(sizeof(data) >= resp->header.length);
267 memcpy((uint8_t *)data + sizeof(nameinfo_response_t), host, hl);
270 memcpy((uint8_t *)data + sizeof(nameinfo_response_t) + hl, serv, sl);
272 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
275 static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) {
276 res_response_t data[BUFSIZE/sizeof(res_response_t) + 1] = {};
277 res_response_t *resp = data;
281 resp->header.type = RESPONSE_RES;
282 resp->header.id = id;
283 resp->header.length = sizeof(res_response_t) + (ret < 0 ? 0 : ret);
285 resp->_errno = _errno;
286 resp->_h_errno = _h_errno;
288 assert(sizeof(data) >= resp->header.length);
291 memcpy((uint8_t *)data + sizeof(res_response_t), answer, ret);
293 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
296 static int handle_request(int out_fd, const packet_t *packet, size_t length) {
297 const rheader_t *req;
300 req = &packet->rheader;
302 assert(length >= sizeof(rheader_t));
303 assert(length == req->length);
306 case REQUEST_ADDRINFO: {
307 struct addrinfo ai = {}, *result = NULL;
308 const addrinfo_request_t *ai_req = &packet->addrinfo_request;
309 const char *node, *service;
312 assert(length >= sizeof(addrinfo_request_t));
313 assert(length == sizeof(addrinfo_request_t) + ai_req->node_len + ai_req->service_len);
315 ai.ai_flags = ai_req->ai_flags;
316 ai.ai_family = ai_req->ai_family;
317 ai.ai_socktype = ai_req->ai_socktype;
318 ai.ai_protocol = ai_req->ai_protocol;
320 node = ai_req->node_len ? (const char*) ai_req + sizeof(addrinfo_request_t) : NULL;
321 service = ai_req->service_len ? (const char*) ai_req + sizeof(addrinfo_request_t) + ai_req->node_len : NULL;
323 ret = getaddrinfo(node, service,
324 ai_req->hints_is_null ? NULL : &ai,
327 /* send_addrinfo_reply() frees result */
328 return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno);
331 case REQUEST_NAMEINFO: {
333 const nameinfo_request_t *ni_req = &packet->nameinfo_request;
334 char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
335 struct sockaddr_storage sa;
337 assert(length >= sizeof(nameinfo_request_t));
338 assert(length == sizeof(nameinfo_request_t) + ni_req->sockaddr_len);
340 memcpy(&sa, (const uint8_t *) ni_req + sizeof(nameinfo_request_t), ni_req->sockaddr_len);
342 ret = getnameinfo((struct sockaddr *)&sa, ni_req->sockaddr_len,
343 ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0,
344 ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0,
347 return send_nameinfo_reply(out_fd, req->id, ret,
348 ret == 0 && ni_req->gethost ? hostbuf : NULL,
349 ret == 0 && ni_req->getserv ? servbuf : NULL,
353 case REQUEST_RES_QUERY:
354 case REQUEST_RES_SEARCH: {
356 HEADER answer[BUFSIZE/sizeof(HEADER) + 1];
357 const res_request_t *res_req = &packet->res_request;
360 assert(length >= sizeof(res_request_t));
361 assert(length == sizeof(res_request_t) + res_req->dname_len);
363 dname = (const char *) req + sizeof(res_request_t);
365 if (req->type == REQUEST_RES_QUERY)
366 ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
368 ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
370 return send_res_reply(out_fd, req->id, (unsigned char *) answer, ret, errno, h_errno);
373 case REQUEST_TERMINATE:
384 static void* thread_worker(void *p) {
385 asyncns_t *asyncns = p;
388 /* No signals in this thread please */
389 sigfillset(&fullset);
390 pthread_sigmask(SIG_BLOCK, &fullset, NULL);
392 while (!asyncns->dead) {
393 packet_t buf[BUFSIZE/sizeof(packet_t) + 1];
396 length = recv(asyncns->fds[REQUEST_RECV_FD], buf, sizeof(buf), 0);
399 if (length < 0 && (errno == EAGAIN || errno == EINTR))
407 if (handle_request(asyncns->fds[RESPONSE_SEND_FD], buf, (size_t) length) < 0)
411 send_died(asyncns->fds[RESPONSE_SEND_FD]);
416 asyncns_t* asyncns_new(unsigned n_proc) {
418 asyncns_t *asyncns = NULL;
422 if (n_proc > MAX_WORKERS)
423 n_proc = MAX_WORKERS;
425 asyncns = malloc(sizeof(asyncns_t));
432 asyncns->valid_workers = 0;
434 for (i = 0; i < MESSAGE_FD_MAX; i++)
435 asyncns->fds[i] = -1;
437 memset(asyncns->queries, 0, sizeof(asyncns->queries));
440 if (socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, asyncns->fds) < 0 ||
441 socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, asyncns->fds+2) < 0) {
443 /* Try again, without SOCK_CLOEXEC */
444 if (errno == EINVAL) {
446 if (socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds) < 0 ||
447 socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds+2) < 0)
455 for (i = 0; i < MESSAGE_FD_MAX; i++)
456 fd_cloexec(asyncns->fds[i], true);
458 for (asyncns->valid_workers = 0; asyncns->valid_workers < n_proc; asyncns->valid_workers++) {
461 if ((r = pthread_create(&asyncns->workers[asyncns->valid_workers], NULL, thread_worker, asyncns)) != 0) {
467 asyncns->current_index = asyncns->current_id = 0;
468 asyncns->done_head = asyncns->done_tail = NULL;
469 asyncns->n_queries = 0;
471 fd_nonblock(asyncns->fds[RESPONSE_RECV_FD], true);
477 asyncns_free(asyncns);
482 void asyncns_free(asyncns_t *asyncns) {
484 int saved_errno = errno;
491 if (asyncns->fds[REQUEST_SEND_FD] >= 0) {
494 req.type = REQUEST_TERMINATE;
495 req.length = sizeof(req);
498 /* Send one termination packet for each worker */
499 for (p = 0; p < asyncns->valid_workers; p++)
500 send(asyncns->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
503 /* Now terminate them and wait until they are gone. */
504 for (p = 0; p < asyncns->valid_workers; p++) {
506 if (pthread_join(asyncns->workers[p], NULL) != EINTR)
511 /* Close all communication channels */
512 for (i = 0; i < MESSAGE_FD_MAX; i++)
513 if (asyncns->fds[i] >= 0)
514 close(asyncns->fds[i]);
516 for (p = 0; p < MAX_QUERIES; p++)
517 if (asyncns->queries[p])
518 asyncns_cancel(asyncns, asyncns->queries[p]);
525 int asyncns_fd(asyncns_t *asyncns) {
528 return asyncns->fds[RESPONSE_RECV_FD];
531 static asyncns_query_t *lookup_query(asyncns_t *asyncns, unsigned id) {
535 if ((q = asyncns->queries[id % MAX_QUERIES]))
542 static void complete_query(asyncns_t *asyncns, asyncns_query_t *q) {
549 if ((q->done_prev = asyncns->done_tail))
550 asyncns->done_tail->done_next = q;
552 asyncns->done_head = q;
554 asyncns->done_tail = q;
558 static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, size_t *length) {
559 addrinfo_serialization_t s;
566 if (*length < sizeof(addrinfo_serialization_t))
569 memcpy(&s, p, sizeof(s));
571 l = sizeof(addrinfo_serialization_t) + s.ai_addrlen + s.canonname_len;
575 if (!(ai = malloc(sizeof(struct addrinfo))))
579 ai->ai_canonname = NULL;
582 if (s.ai_addrlen && !(ai->ai_addr = malloc(s.ai_addrlen)))
585 if (s.canonname_len && !(ai->ai_canonname = malloc(s.canonname_len)))
588 ai->ai_flags = s.ai_flags;
589 ai->ai_family = s.ai_family;
590 ai->ai_socktype = s.ai_socktype;
591 ai->ai_protocol = s.ai_protocol;
592 ai->ai_addrlen = s.ai_addrlen;
595 memcpy(ai->ai_addr, (const uint8_t*) p + sizeof(addrinfo_serialization_t), s.ai_addrlen);
597 if (ai->ai_canonname)
598 memcpy(ai->ai_canonname, (const uint8_t*) p + sizeof(addrinfo_serialization_t) + s.ai_addrlen, s.canonname_len);
603 return (const uint8_t*) p + l;
608 asyncns_freeaddrinfo(ai);
613 static int handle_response(asyncns_t *asyncns, const packet_t *packet, size_t length) {
614 const rheader_t *resp;
619 resp = &packet->rheader;
621 assert(length >= sizeof(rheader_t));
622 assert(length == resp->length);
624 if (resp->type == RESPONSE_DIED) {
629 if (!(q = lookup_query(asyncns, resp->id)))
632 switch (resp->type) {
633 case RESPONSE_ADDRINFO: {
634 const addrinfo_response_t *ai_resp = &packet->addrinfo_response;
637 struct addrinfo *prev = NULL;
639 assert(length >= sizeof(addrinfo_response_t));
640 assert(q->type == REQUEST_ADDRINFO);
642 q->ret = ai_resp->ret;
643 q->_errno = ai_resp->_errno;
644 q->_h_errno = ai_resp->_h_errno;
645 l = length - sizeof(addrinfo_response_t);
646 p = (const uint8_t*) resp + sizeof(addrinfo_response_t);
649 struct addrinfo *ai = NULL;
650 p = unserialize_addrinfo(p, &ai, &l);
665 complete_query(asyncns, q);
669 case RESPONSE_NAMEINFO: {
670 const nameinfo_response_t *ni_resp = &packet->nameinfo_response;
672 assert(length >= sizeof(nameinfo_response_t));
673 assert(q->type == REQUEST_NAMEINFO);
675 q->ret = ni_resp->ret;
676 q->_errno = ni_resp->_errno;
677 q->_h_errno = ni_resp->_h_errno;
679 if (ni_resp->hostlen)
680 if (!(q->host = strndup((const char*) ni_resp + sizeof(nameinfo_response_t), ni_resp->hostlen-1)))
683 if (ni_resp->servlen)
684 if (!(q->serv = strndup((const char*) ni_resp + sizeof(nameinfo_response_t) + ni_resp->hostlen, ni_resp->servlen-1)))
687 complete_query(asyncns, q);
692 const res_response_t *res_resp = &packet->res_response;
694 assert(length >= sizeof(res_response_t));
695 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
697 q->ret = res_resp->ret;
698 q->_errno = res_resp->_errno;
699 q->_h_errno = res_resp->_h_errno;
701 if (res_resp->ret >= 0) {
702 if (!(q->serv = malloc(res_resp->ret))) {
706 memcpy(q->serv, (const char *)resp + sizeof(res_response_t), res_resp->ret);
709 complete_query(asyncns, q);
720 int asyncns_wait(asyncns_t *asyncns, int block) {
725 packet_t buf[BUFSIZE/sizeof(packet_t) + 1];
733 if (((l = recv(asyncns->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0)) < 0)) {
739 if (!block || handled)
743 FD_SET(asyncns->fds[RESPONSE_RECV_FD], &fds);
745 if (select(asyncns->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0)
751 if (handle_response(asyncns, buf, (size_t) l) < 0)
758 static asyncns_query_t *alloc_query(asyncns_t *asyncns) {
762 if (asyncns->n_queries >= MAX_QUERIES) {
767 while (asyncns->queries[asyncns->current_index]) {
769 asyncns->current_index++;
770 asyncns->current_id++;
772 while (asyncns->current_index >= MAX_QUERIES)
773 asyncns->current_index -= MAX_QUERIES;
776 if (!(q = asyncns->queries[asyncns->current_index] = malloc(sizeof(asyncns_query_t)))) {
781 asyncns->n_queries++;
783 q->asyncns = asyncns;
785 q->id = asyncns->current_id;
786 q->done_next = q->done_prev = NULL;
792 q->host = q->serv = NULL;
797 asyncns_query_t* asyncns_getaddrinfo(asyncns_t *asyncns, const char *node, const char *service, const struct addrinfo *hints) {
798 addrinfo_request_t data[BUFSIZE/sizeof(addrinfo_request_t) + 1] = {};
799 addrinfo_request_t *req = data;
802 assert(node || service);
809 if (!(q = alloc_query(asyncns)))
813 req->node_len = node ? strlen(node)+1 : 0;
814 req->service_len = service ? strlen(service)+1 : 0;
816 req->header.id = q->id;
817 req->header.type = q->type = REQUEST_ADDRINFO;
818 req->header.length = sizeof(addrinfo_request_t) + req->node_len + req->service_len;
820 if (req->header.length > BUFSIZE) {
825 if (!(req->hints_is_null = !hints)) {
826 req->ai_flags = hints->ai_flags;
827 req->ai_family = hints->ai_family;
828 req->ai_socktype = hints->ai_socktype;
829 req->ai_protocol = hints->ai_protocol;
833 strcpy((char*) req + sizeof(addrinfo_request_t), node);
836 strcpy((char*) req + sizeof(addrinfo_request_t) + req->node_len, service);
838 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
845 asyncns_cancel(asyncns, q);
850 int asyncns_getaddrinfo_done(asyncns_t *asyncns, asyncns_query_t* q, struct addrinfo **ret_res) {
854 assert(q->asyncns == asyncns);
855 assert(q->type == REQUEST_ADDRINFO);
865 *ret_res = q->addrinfo;
870 if (ret == EAI_SYSTEM)
874 h_errno = q->_h_errno;
876 asyncns_cancel(asyncns, q);
881 asyncns_query_t* asyncns_getnameinfo(asyncns_t *asyncns, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) {
882 nameinfo_request_t data[BUFSIZE/sizeof(nameinfo_request_t) + 1] = {};
883 nameinfo_request_t *req = data;
895 if (!(q = alloc_query(asyncns)))
899 req->header.id = q->id;
900 req->header.type = q->type = REQUEST_NAMEINFO;
901 req->header.length = sizeof(nameinfo_request_t) + salen;
903 if (req->header.length > BUFSIZE) {
909 req->sockaddr_len = salen;
910 req->gethost = gethost;
911 req->getserv = getserv;
913 memcpy((uint8_t*) req + sizeof(nameinfo_request_t), sa, salen);
915 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
922 asyncns_cancel(asyncns, q);
927 int asyncns_getnameinfo_done(asyncns_t *asyncns, asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) {
931 assert(q->asyncns == asyncns);
932 assert(q->type == REQUEST_NAMEINFO);
933 assert(!ret_host || hostlen);
934 assert(!ret_serv || servlen);
944 if (ret_host && q->host) {
945 strncpy(ret_host, q->host, hostlen);
946 ret_host[hostlen-1] = 0;
949 if (ret_serv && q->serv) {
950 strncpy(ret_serv, q->serv, servlen);
951 ret_serv[servlen-1] = 0;
956 if (ret == EAI_SYSTEM)
960 h_errno = q->_h_errno;
962 asyncns_cancel(asyncns, q);
967 static asyncns_query_t * asyncns_res(asyncns_t *asyncns, query_type_t qtype, const char *dname, int class, int type) {
968 res_request_t data[BUFSIZE/sizeof(res_request_t) + 1];
969 res_request_t *req = data;
980 if (!(q = alloc_query(asyncns)))
983 req->dname_len = strlen(dname) + 1;
985 req->header.id = q->id;
986 req->header.type = q->type = qtype;
987 req->header.length = sizeof(res_request_t) + req->dname_len;
989 if (req->header.length > BUFSIZE) {
997 strcpy((char*) req + sizeof(res_request_t), dname);
999 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
1006 asyncns_cancel(asyncns, q);
1011 asyncns_query_t* asyncns_res_query(asyncns_t *asyncns, const char *dname, int class, int type) {
1012 return asyncns_res(asyncns, REQUEST_RES_QUERY, dname, class, type);
1015 asyncns_query_t* asyncns_res_search(asyncns_t *asyncns, const char *dname, int class, int type) {
1016 return asyncns_res(asyncns, REQUEST_RES_SEARCH, dname, class, type);
1019 int asyncns_res_done(asyncns_t *asyncns, asyncns_query_t* q, unsigned char **answer) {
1023 assert(q->asyncns == asyncns);
1024 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
1027 if (asyncns->dead) {
1037 *answer = (unsigned char *)q->serv;
1044 h_errno = q->_h_errno;
1047 asyncns_cancel(asyncns, q);
1049 return ret < 0 ? -errno : ret;
1052 asyncns_query_t* asyncns_getnext(asyncns_t *asyncns) {
1054 return asyncns->done_head;
1057 int asyncns_getnqueries(asyncns_t *asyncns) {
1059 return asyncns->n_queries;
1062 void asyncns_cancel(asyncns_t *asyncns, asyncns_query_t* q) {
1064 int saved_errno = errno;
1068 assert(q->asyncns == asyncns);
1069 assert(asyncns->n_queries > 0);
1074 q->done_prev->done_next = q->done_next;
1076 asyncns->done_head = q->done_next;
1079 q->done_next->done_prev = q->done_prev;
1081 asyncns->done_tail = q->done_prev;
1084 i = q->id % MAX_QUERIES;
1085 assert(asyncns->queries[i] == q);
1086 asyncns->queries[i] = NULL;
1088 asyncns_freeaddrinfo(q->addrinfo);
1092 asyncns->n_queries--;
1095 errno = saved_errno;
1098 void asyncns_freeaddrinfo(struct addrinfo *ai) {
1099 int saved_errno = errno;
1102 struct addrinfo *next = ai->ai_next;
1105 free(ai->ai_canonname);
1111 errno = saved_errno;
1114 void asyncns_freeanswer(unsigned char *answer) {
1115 int saved_errno = errno;
1120 /* Please note that this function is new in libasyncns 0.4. In
1121 * older versions you were supposed to free the answer directly
1122 * with free(). Hence, if this function is changed to do more than
1123 * just a simple free() this must be considered ABI/API breakage! */
1127 errno = saved_errno;
1130 int asyncns_isdone(asyncns_t *asyncns, asyncns_query_t*q) {
1133 assert(q->asyncns == asyncns);
1138 void asyncns_setuserdata(asyncns_t *asyncns, asyncns_query_t *q, void *userdata) {
1141 assert(q->asyncns = asyncns);
1143 q->userdata = userdata;
1146 void* asyncns_getuserdata(asyncns_t *asyncns, asyncns_query_t *q) {
1149 assert(q->asyncns = asyncns);