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/>.
30 #include <sys/select.h>
36 #include <sys/types.h>
38 #include <netinet/in.h>
39 #include <arpa/nameser.h>
43 #include <sys/resource.h>
47 #ifdef HAVE_SYS_PRCTL_H
48 #include <sys/prctl.h>
54 #define MAX_WORKERS 16
55 #define MAX_QUERIES 256
56 #define BUFSIZE (10240)
79 int fds[MESSAGE_FD_MAX];
81 pthread_t workers[MAX_WORKERS];
82 unsigned valid_workers;
84 unsigned current_id, current_index;
85 asyncns_query_t* queries[MAX_QUERIES];
87 asyncns_query_t *done_head, *done_tail;
93 struct asyncns_query {
98 asyncns_query_t *done_next, *done_prev;
102 struct addrinfo *addrinfo;
107 typedef struct rheader {
113 typedef struct addrinfo_request {
114 struct rheader header;
120 size_t node_len, service_len;
121 } addrinfo_request_t;
123 typedef struct addrinfo_response {
124 struct rheader header;
128 /* followed by addrinfo_serialization[] */
129 } addrinfo_response_t;
131 typedef struct addrinfo_serialization {
137 size_t canonname_len;
138 /* Followed by ai_addr amd ai_canonname with variable lengths */
139 } addrinfo_serialization_t;
141 typedef struct nameinfo_request {
142 struct rheader header;
144 socklen_t sockaddr_len;
145 int gethost, getserv;
146 } nameinfo_request_t;
148 typedef struct nameinfo_response {
149 struct rheader header;
150 size_t hostlen, servlen;
154 } nameinfo_response_t;
156 typedef struct res_request {
157 struct rheader header;
163 typedef struct res_response {
164 struct rheader header;
170 typedef union packet {
172 addrinfo_request_t addrinfo_request;
173 addrinfo_response_t addrinfo_response;
174 nameinfo_request_t nameinfo_request;
175 nameinfo_response_t nameinfo_response;
176 res_request_t res_request;
177 res_response_t res_response;
180 static int send_died(int out_fd) {
184 rh.type = RESPONSE_DIED;
186 rh.length = sizeof(rh);
188 return send(out_fd, &rh, rh.length, MSG_NOSIGNAL);
191 static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) {
192 addrinfo_serialization_t s;
197 assert(*length <= maxlength);
199 cnl = (ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0);
200 l = sizeof(addrinfo_serialization_t) + ai->ai_addrlen + cnl;
202 if (*length + l > maxlength)
205 s.ai_flags = ai->ai_flags;
206 s.ai_family = ai->ai_family;
207 s.ai_socktype = ai->ai_socktype;
208 s.ai_protocol = ai->ai_protocol;
209 s.ai_addrlen = ai->ai_addrlen;
210 s.canonname_len = cnl;
212 memcpy((uint8_t*) p, &s, sizeof(addrinfo_serialization_t));
213 memcpy((uint8_t*) p + sizeof(addrinfo_serialization_t), ai->ai_addr, ai->ai_addrlen);
215 if (ai->ai_canonname)
216 strcpy((char*) p + sizeof(addrinfo_serialization_t) + ai->ai_addrlen, ai->ai_canonname);
219 return (uint8_t*) p + l;
222 static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo *ai, int _errno, int _h_errno) {
223 addrinfo_response_t data[BUFSIZE/sizeof(addrinfo_response_t) + 1] = {};
224 addrinfo_response_t *resp = data;
227 resp->header.type = RESPONSE_ADDRINFO;
228 resp->header.id = id;
229 resp->header.length = sizeof(addrinfo_response_t);
231 resp->_errno = _errno;
232 resp->_h_errno = _h_errno;
234 if (ret == 0 && ai) {
238 for (k = ai; k; k = k->ai_next) {
239 if (!(p = serialize_addrinfo(p, k, &resp->header.length, (char*) data + BUFSIZE - (char*) p))) {
240 resp->ret = EAI_MEMORY;
249 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
252 static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) {
253 nameinfo_response_t data[BUFSIZE/sizeof(nameinfo_response_t) + 1] = {};
255 nameinfo_response_t *resp = data;
259 sl = serv ? strlen(serv)+1 : 0;
260 hl = host ? strlen(host)+1 : 0;
262 resp->header.type = RESPONSE_NAMEINFO;
263 resp->header.id = id;
264 resp->header.length = sizeof(nameinfo_response_t) + hl + sl;
266 resp->_errno = _errno;
267 resp->_h_errno = _h_errno;
271 assert(sizeof(data) >= resp->header.length);
274 memcpy((uint8_t *)data + sizeof(nameinfo_response_t), host, hl);
277 memcpy((uint8_t *)data + sizeof(nameinfo_response_t) + hl, serv, sl);
279 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
282 static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) {
283 res_response_t data[BUFSIZE/sizeof(res_response_t) + 1] = {};
284 res_response_t *resp = data;
288 resp->header.type = RESPONSE_RES;
289 resp->header.id = id;
290 resp->header.length = sizeof(res_response_t) + (ret < 0 ? 0 : ret);
292 resp->_errno = _errno;
293 resp->_h_errno = _h_errno;
295 assert(sizeof(data) >= resp->header.length);
298 memcpy((uint8_t *)data + sizeof(res_response_t), answer, ret);
300 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
303 static int handle_request(int out_fd, const packet_t *packet, size_t length) {
304 const rheader_t *req;
307 req = &packet->rheader;
309 assert(length >= sizeof(rheader_t));
310 assert(length == req->length);
313 case REQUEST_ADDRINFO: {
314 struct addrinfo ai = {}, *result = NULL;
315 const addrinfo_request_t *ai_req = &packet->addrinfo_request;
316 const char *node, *service;
319 assert(length >= sizeof(addrinfo_request_t));
320 assert(length == sizeof(addrinfo_request_t) + ai_req->node_len + ai_req->service_len);
322 ai.ai_flags = ai_req->ai_flags;
323 ai.ai_family = ai_req->ai_family;
324 ai.ai_socktype = ai_req->ai_socktype;
325 ai.ai_protocol = ai_req->ai_protocol;
327 node = ai_req->node_len ? (const char*) ai_req + sizeof(addrinfo_request_t) : NULL;
328 service = ai_req->service_len ? (const char*) ai_req + sizeof(addrinfo_request_t) + ai_req->node_len : NULL;
330 ret = getaddrinfo(node, service,
331 ai_req->hints_is_null ? NULL : &ai,
334 /* send_addrinfo_reply() frees result */
335 return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno);
338 case REQUEST_NAMEINFO: {
340 const nameinfo_request_t *ni_req = &packet->nameinfo_request;
341 char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
342 struct sockaddr_storage sa;
344 assert(length >= sizeof(nameinfo_request_t));
345 assert(length == sizeof(nameinfo_request_t) + ni_req->sockaddr_len);
347 memcpy(&sa, (const uint8_t *) ni_req + sizeof(nameinfo_request_t), ni_req->sockaddr_len);
349 ret = getnameinfo((struct sockaddr *)&sa, ni_req->sockaddr_len,
350 ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0,
351 ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0,
354 return send_nameinfo_reply(out_fd, req->id, ret,
355 ret == 0 && ni_req->gethost ? hostbuf : NULL,
356 ret == 0 && ni_req->getserv ? servbuf : NULL,
360 case REQUEST_RES_QUERY:
361 case REQUEST_RES_SEARCH: {
363 HEADER answer[BUFSIZE/sizeof(HEADER) + 1];
364 const res_request_t *res_req = &packet->res_request;
367 assert(length >= sizeof(res_request_t));
368 assert(length == sizeof(res_request_t) + res_req->dname_len);
370 dname = (const char *) req + sizeof(res_request_t);
372 if (req->type == REQUEST_RES_QUERY)
373 ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
375 ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
377 return send_res_reply(out_fd, req->id, (unsigned char *) answer, ret, errno, h_errno);
380 case REQUEST_TERMINATE:
391 static void* thread_worker(void *p) {
392 asyncns_t *asyncns = p;
395 /* No signals in this thread please */
396 sigfillset(&fullset);
397 pthread_sigmask(SIG_BLOCK, &fullset, NULL);
399 while (!asyncns->dead) {
400 packet_t buf[BUFSIZE/sizeof(packet_t) + 1];
403 length = recv(asyncns->fds[REQUEST_RECV_FD], buf, sizeof(buf), 0);
406 if (length < 0 && (errno == EAGAIN || errno == EINTR))
414 if (handle_request(asyncns->fds[RESPONSE_SEND_FD], buf, (size_t) length) < 0)
418 send_died(asyncns->fds[RESPONSE_SEND_FD]);
423 asyncns_t* asyncns_new(unsigned n_proc) {
425 asyncns_t *asyncns = NULL;
429 if (n_proc > MAX_WORKERS)
430 n_proc = MAX_WORKERS;
432 asyncns = malloc(sizeof(asyncns_t));
439 asyncns->valid_workers = 0;
441 for (i = 0; i < MESSAGE_FD_MAX; i++)
442 asyncns->fds[i] = -1;
444 memset(asyncns->queries, 0, sizeof(asyncns->queries));
447 if (socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, asyncns->fds) < 0 ||
448 socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, asyncns->fds+2) < 0) {
450 /* Try again, without SOCK_CLOEXEC */
451 if (errno == EINVAL) {
453 if (socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds) < 0 ||
454 socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds+2) < 0)
462 for (i = 0; i < MESSAGE_FD_MAX; i++)
463 fd_cloexec(asyncns->fds[i], true);
465 for (asyncns->valid_workers = 0; asyncns->valid_workers < n_proc; asyncns->valid_workers++) {
468 if ((r = pthread_create(&asyncns->workers[asyncns->valid_workers], NULL, thread_worker, asyncns)) != 0) {
474 asyncns->current_index = asyncns->current_id = 0;
475 asyncns->done_head = asyncns->done_tail = NULL;
476 asyncns->n_queries = 0;
478 fd_nonblock(asyncns->fds[RESPONSE_RECV_FD], true);
484 asyncns_free(asyncns);
489 void asyncns_free(asyncns_t *asyncns) {
491 int saved_errno = errno;
498 if (asyncns->fds[REQUEST_SEND_FD] >= 0) {
501 req.type = REQUEST_TERMINATE;
502 req.length = sizeof(req);
505 /* Send one termination packet for each worker */
506 for (p = 0; p < asyncns->valid_workers; p++)
507 send(asyncns->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
510 /* Now terminate them and wait until they are gone. */
511 for (p = 0; p < asyncns->valid_workers; p++) {
513 if (pthread_join(asyncns->workers[p], NULL) != EINTR)
518 /* Close all communication channels */
519 for (i = 0; i < MESSAGE_FD_MAX; i++)
520 if (asyncns->fds[i] >= 0)
521 close(asyncns->fds[i]);
523 for (p = 0; p < MAX_QUERIES; p++)
524 if (asyncns->queries[p])
525 asyncns_cancel(asyncns, asyncns->queries[p]);
532 int asyncns_fd(asyncns_t *asyncns) {
535 return asyncns->fds[RESPONSE_RECV_FD];
538 static asyncns_query_t *lookup_query(asyncns_t *asyncns, unsigned id) {
542 if ((q = asyncns->queries[id % MAX_QUERIES]))
549 static void complete_query(asyncns_t *asyncns, asyncns_query_t *q) {
556 if ((q->done_prev = asyncns->done_tail))
557 asyncns->done_tail->done_next = q;
559 asyncns->done_head = q;
561 asyncns->done_tail = q;
565 static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, size_t *length) {
566 addrinfo_serialization_t s;
573 if (*length < sizeof(addrinfo_serialization_t))
576 memcpy(&s, p, sizeof(s));
578 l = sizeof(addrinfo_serialization_t) + s.ai_addrlen + s.canonname_len;
582 if (!(ai = malloc(sizeof(struct addrinfo))))
586 ai->ai_canonname = NULL;
589 if (s.ai_addrlen && !(ai->ai_addr = malloc(s.ai_addrlen)))
592 if (s.canonname_len && !(ai->ai_canonname = malloc(s.canonname_len)))
595 ai->ai_flags = s.ai_flags;
596 ai->ai_family = s.ai_family;
597 ai->ai_socktype = s.ai_socktype;
598 ai->ai_protocol = s.ai_protocol;
599 ai->ai_addrlen = s.ai_addrlen;
602 memcpy(ai->ai_addr, (const uint8_t*) p + sizeof(addrinfo_serialization_t), s.ai_addrlen);
604 if (ai->ai_canonname)
605 memcpy(ai->ai_canonname, (const uint8_t*) p + sizeof(addrinfo_serialization_t) + s.ai_addrlen, s.canonname_len);
610 return (const uint8_t*) p + l;
615 asyncns_freeaddrinfo(ai);
620 static int handle_response(asyncns_t *asyncns, const packet_t *packet, size_t length) {
621 const rheader_t *resp;
626 resp = &packet->rheader;
628 assert(length >= sizeof(rheader_t));
629 assert(length == resp->length);
631 if (resp->type == RESPONSE_DIED) {
636 if (!(q = lookup_query(asyncns, resp->id)))
639 switch (resp->type) {
640 case RESPONSE_ADDRINFO: {
641 const addrinfo_response_t *ai_resp = &packet->addrinfo_response;
644 struct addrinfo *prev = NULL;
646 assert(length >= sizeof(addrinfo_response_t));
647 assert(q->type == REQUEST_ADDRINFO);
649 q->ret = ai_resp->ret;
650 q->_errno = ai_resp->_errno;
651 q->_h_errno = ai_resp->_h_errno;
652 l = length - sizeof(addrinfo_response_t);
653 p = (const uint8_t*) resp + sizeof(addrinfo_response_t);
656 struct addrinfo *ai = NULL;
657 p = unserialize_addrinfo(p, &ai, &l);
672 complete_query(asyncns, q);
676 case RESPONSE_NAMEINFO: {
677 const nameinfo_response_t *ni_resp = &packet->nameinfo_response;
679 assert(length >= sizeof(nameinfo_response_t));
680 assert(q->type == REQUEST_NAMEINFO);
682 q->ret = ni_resp->ret;
683 q->_errno = ni_resp->_errno;
684 q->_h_errno = ni_resp->_h_errno;
686 if (ni_resp->hostlen)
687 if (!(q->host = strndup((const char*) ni_resp + sizeof(nameinfo_response_t), ni_resp->hostlen-1)))
690 if (ni_resp->servlen)
691 if (!(q->serv = strndup((const char*) ni_resp + sizeof(nameinfo_response_t) + ni_resp->hostlen, ni_resp->servlen-1)))
694 complete_query(asyncns, q);
699 const res_response_t *res_resp = &packet->res_response;
701 assert(length >= sizeof(res_response_t));
702 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
704 q->ret = res_resp->ret;
705 q->_errno = res_resp->_errno;
706 q->_h_errno = res_resp->_h_errno;
708 if (res_resp->ret >= 0) {
709 if (!(q->serv = malloc(res_resp->ret))) {
713 memcpy(q->serv, (const char *)resp + sizeof(res_response_t), res_resp->ret);
716 complete_query(asyncns, q);
727 int asyncns_wait(asyncns_t *asyncns, int block) {
732 packet_t buf[BUFSIZE/sizeof(packet_t) + 1];
740 if (((l = recv(asyncns->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0)) < 0)) {
746 if (!block || handled)
750 FD_SET(asyncns->fds[RESPONSE_RECV_FD], &fds);
752 if (select(asyncns->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0)
758 if (handle_response(asyncns, buf, (size_t) l) < 0)
765 static asyncns_query_t *alloc_query(asyncns_t *asyncns) {
769 if (asyncns->n_queries >= MAX_QUERIES) {
774 while (asyncns->queries[asyncns->current_index]) {
776 asyncns->current_index++;
777 asyncns->current_id++;
779 while (asyncns->current_index >= MAX_QUERIES)
780 asyncns->current_index -= MAX_QUERIES;
783 if (!(q = asyncns->queries[asyncns->current_index] = malloc(sizeof(asyncns_query_t)))) {
788 asyncns->n_queries++;
790 q->asyncns = asyncns;
792 q->id = asyncns->current_id;
793 q->done_next = q->done_prev = NULL;
799 q->host = q->serv = NULL;
804 asyncns_query_t* asyncns_getaddrinfo(asyncns_t *asyncns, const char *node, const char *service, const struct addrinfo *hints) {
805 addrinfo_request_t data[BUFSIZE/sizeof(addrinfo_request_t) + 1] = {};
806 addrinfo_request_t *req = data;
809 assert(node || service);
816 if (!(q = alloc_query(asyncns)))
820 req->node_len = node ? strlen(node)+1 : 0;
821 req->service_len = service ? strlen(service)+1 : 0;
823 req->header.id = q->id;
824 req->header.type = q->type = REQUEST_ADDRINFO;
825 req->header.length = sizeof(addrinfo_request_t) + req->node_len + req->service_len;
827 if (req->header.length > BUFSIZE) {
832 if (!(req->hints_is_null = !hints)) {
833 req->ai_flags = hints->ai_flags;
834 req->ai_family = hints->ai_family;
835 req->ai_socktype = hints->ai_socktype;
836 req->ai_protocol = hints->ai_protocol;
840 strcpy((char*) req + sizeof(addrinfo_request_t), node);
843 strcpy((char*) req + sizeof(addrinfo_request_t) + req->node_len, service);
845 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
852 asyncns_cancel(asyncns, q);
857 int asyncns_getaddrinfo_done(asyncns_t *asyncns, asyncns_query_t* q, struct addrinfo **ret_res) {
861 assert(q->asyncns == asyncns);
862 assert(q->type == REQUEST_ADDRINFO);
872 *ret_res = q->addrinfo;
877 if (ret == EAI_SYSTEM)
881 h_errno = q->_h_errno;
883 asyncns_cancel(asyncns, q);
888 asyncns_query_t* asyncns_getnameinfo(asyncns_t *asyncns, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) {
889 nameinfo_request_t data[BUFSIZE/sizeof(nameinfo_request_t) + 1] = {};
890 nameinfo_request_t *req = data;
902 if (!(q = alloc_query(asyncns)))
906 req->header.id = q->id;
907 req->header.type = q->type = REQUEST_NAMEINFO;
908 req->header.length = sizeof(nameinfo_request_t) + salen;
910 if (req->header.length > BUFSIZE) {
916 req->sockaddr_len = salen;
917 req->gethost = gethost;
918 req->getserv = getserv;
920 memcpy((uint8_t*) req + sizeof(nameinfo_request_t), sa, salen);
922 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
929 asyncns_cancel(asyncns, q);
934 int asyncns_getnameinfo_done(asyncns_t *asyncns, asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) {
938 assert(q->asyncns == asyncns);
939 assert(q->type == REQUEST_NAMEINFO);
940 assert(!ret_host || hostlen);
941 assert(!ret_serv || servlen);
951 if (ret_host && q->host) {
952 strncpy(ret_host, q->host, hostlen);
953 ret_host[hostlen-1] = 0;
956 if (ret_serv && q->serv) {
957 strncpy(ret_serv, q->serv, servlen);
958 ret_serv[servlen-1] = 0;
963 if (ret == EAI_SYSTEM)
967 h_errno = q->_h_errno;
969 asyncns_cancel(asyncns, q);
974 static asyncns_query_t * asyncns_res(asyncns_t *asyncns, query_type_t qtype, const char *dname, int class, int type) {
975 res_request_t data[BUFSIZE/sizeof(res_request_t) + 1];
976 res_request_t *req = data;
987 if (!(q = alloc_query(asyncns)))
990 req->dname_len = strlen(dname) + 1;
992 req->header.id = q->id;
993 req->header.type = q->type = qtype;
994 req->header.length = sizeof(res_request_t) + req->dname_len;
996 if (req->header.length > BUFSIZE) {
1004 strcpy((char*) req + sizeof(res_request_t), dname);
1006 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
1013 asyncns_cancel(asyncns, q);
1018 asyncns_query_t* asyncns_res_query(asyncns_t *asyncns, const char *dname, int class, int type) {
1019 return asyncns_res(asyncns, REQUEST_RES_QUERY, dname, class, type);
1022 asyncns_query_t* asyncns_res_search(asyncns_t *asyncns, const char *dname, int class, int type) {
1023 return asyncns_res(asyncns, REQUEST_RES_SEARCH, dname, class, type);
1026 int asyncns_res_done(asyncns_t *asyncns, asyncns_query_t* q, unsigned char **answer) {
1030 assert(q->asyncns == asyncns);
1031 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
1034 if (asyncns->dead) {
1044 *answer = (unsigned char *)q->serv;
1051 h_errno = q->_h_errno;
1054 asyncns_cancel(asyncns, q);
1056 return ret < 0 ? -errno : ret;
1059 asyncns_query_t* asyncns_getnext(asyncns_t *asyncns) {
1061 return asyncns->done_head;
1064 int asyncns_getnqueries(asyncns_t *asyncns) {
1066 return asyncns->n_queries;
1069 void asyncns_cancel(asyncns_t *asyncns, asyncns_query_t* q) {
1071 int saved_errno = errno;
1075 assert(q->asyncns == asyncns);
1076 assert(asyncns->n_queries > 0);
1081 q->done_prev->done_next = q->done_next;
1083 asyncns->done_head = q->done_next;
1086 q->done_next->done_prev = q->done_prev;
1088 asyncns->done_tail = q->done_prev;
1091 i = q->id % MAX_QUERIES;
1092 assert(asyncns->queries[i] == q);
1093 asyncns->queries[i] = NULL;
1095 asyncns_freeaddrinfo(q->addrinfo);
1099 asyncns->n_queries--;
1102 errno = saved_errno;
1105 void asyncns_freeaddrinfo(struct addrinfo *ai) {
1106 int saved_errno = errno;
1109 struct addrinfo *next = ai->ai_next;
1112 free(ai->ai_canonname);
1118 errno = saved_errno;
1121 void asyncns_freeanswer(unsigned char *answer) {
1122 int saved_errno = errno;
1127 /* Please note that this function is new in libasyncns 0.4. In
1128 * older versions you were supposed to free the answer directly
1129 * with free(). Hence, if this function is changed to do more than
1130 * just a simple free() this must be considered ABI/API breakage! */
1134 errno = saved_errno;
1137 int asyncns_isdone(asyncns_t *asyncns, asyncns_query_t*q) {
1140 assert(q->asyncns == asyncns);
1145 void asyncns_setuserdata(asyncns_t *asyncns, asyncns_query_t *q, void *userdata) {
1148 assert(q->asyncns = asyncns);
1150 q->userdata = userdata;
1153 void* asyncns_getuserdata(asyncns_t *asyncns, asyncns_query_t *q) {
1156 assert(q->asyncns = asyncns);