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 p = serialize_addrinfo(p, k, &resp->header.length, (char*) data + BUFSIZE - (char*) p);
234 resp->ret = EAI_MEMORY;
243 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
246 static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) {
247 nameinfo_response_t data[BUFSIZE/sizeof(nameinfo_response_t) + 1] = {};
249 nameinfo_response_t *resp = data;
253 sl = serv ? strlen(serv)+1 : 0;
254 hl = host ? strlen(host)+1 : 0;
256 resp->header.type = RESPONSE_NAMEINFO;
257 resp->header.id = id;
258 resp->header.length = sizeof(nameinfo_response_t) + hl + sl;
260 resp->_errno = _errno;
261 resp->_h_errno = _h_errno;
265 assert(sizeof(data) >= resp->header.length);
268 memcpy((uint8_t *)data + sizeof(nameinfo_response_t), host, hl);
271 memcpy((uint8_t *)data + sizeof(nameinfo_response_t) + hl, serv, sl);
273 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
276 static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) {
277 res_response_t data[BUFSIZE/sizeof(res_response_t) + 1] = {};
278 res_response_t *resp = data;
282 resp->header.type = RESPONSE_RES;
283 resp->header.id = id;
284 resp->header.length = sizeof(res_response_t) + (ret < 0 ? 0 : ret);
286 resp->_errno = _errno;
287 resp->_h_errno = _h_errno;
289 assert(sizeof(data) >= resp->header.length);
292 memcpy((uint8_t *)data + sizeof(res_response_t), answer, ret);
294 return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
297 static int handle_request(int out_fd, const packet_t *packet, size_t length) {
298 const rheader_t *req;
301 req = &packet->rheader;
303 assert(length >= sizeof(rheader_t));
304 assert(length == req->length);
307 case REQUEST_ADDRINFO: {
308 struct addrinfo ai = {}, *result = NULL;
309 const addrinfo_request_t *ai_req = &packet->addrinfo_request;
310 const char *node, *service;
313 assert(length >= sizeof(addrinfo_request_t));
314 assert(length == sizeof(addrinfo_request_t) + ai_req->node_len + ai_req->service_len);
316 ai.ai_flags = ai_req->ai_flags;
317 ai.ai_family = ai_req->ai_family;
318 ai.ai_socktype = ai_req->ai_socktype;
319 ai.ai_protocol = ai_req->ai_protocol;
321 node = ai_req->node_len ? (const char*) ai_req + sizeof(addrinfo_request_t) : NULL;
322 service = ai_req->service_len ? (const char*) ai_req + sizeof(addrinfo_request_t) + ai_req->node_len : NULL;
324 ret = getaddrinfo(node, service,
325 ai_req->hints_is_null ? NULL : &ai,
328 /* send_addrinfo_reply() frees result */
329 return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno);
332 case REQUEST_NAMEINFO: {
334 const nameinfo_request_t *ni_req = &packet->nameinfo_request;
335 char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
336 struct sockaddr_storage sa;
338 assert(length >= sizeof(nameinfo_request_t));
339 assert(length == sizeof(nameinfo_request_t) + ni_req->sockaddr_len);
341 memcpy(&sa, (const uint8_t *) ni_req + sizeof(nameinfo_request_t), ni_req->sockaddr_len);
343 ret = getnameinfo((struct sockaddr *)&sa, ni_req->sockaddr_len,
344 ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0,
345 ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0,
348 return send_nameinfo_reply(out_fd, req->id, ret,
349 ret == 0 && ni_req->gethost ? hostbuf : NULL,
350 ret == 0 && ni_req->getserv ? servbuf : NULL,
354 case REQUEST_RES_QUERY:
355 case REQUEST_RES_SEARCH: {
357 HEADER answer[BUFSIZE/sizeof(HEADER) + 1];
358 const res_request_t *res_req = &packet->res_request;
361 assert(length >= sizeof(res_request_t));
362 assert(length == sizeof(res_request_t) + res_req->dname_len);
364 dname = (const char *) req + sizeof(res_request_t);
366 if (req->type == REQUEST_RES_QUERY)
367 ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
369 ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
371 return send_res_reply(out_fd, req->id, (unsigned char *) answer, ret, errno, h_errno);
374 case REQUEST_TERMINATE:
385 static void* thread_worker(void *p) {
386 asyncns_t *asyncns = p;
389 /* No signals in this thread please */
390 sigfillset(&fullset);
391 pthread_sigmask(SIG_BLOCK, &fullset, NULL);
393 while (!asyncns->dead) {
394 packet_t buf[BUFSIZE/sizeof(packet_t) + 1];
397 length = recv(asyncns->fds[REQUEST_RECV_FD], buf, sizeof(buf), 0);
400 if (length < 0 && (errno == EAGAIN || errno == EINTR))
408 if (handle_request(asyncns->fds[RESPONSE_SEND_FD], buf, (size_t) length) < 0)
412 send_died(asyncns->fds[RESPONSE_SEND_FD]);
417 asyncns_t* asyncns_new(unsigned n_proc) {
419 asyncns_t *asyncns = NULL;
423 if (n_proc > MAX_WORKERS)
424 n_proc = MAX_WORKERS;
426 asyncns = malloc(sizeof(asyncns_t));
433 asyncns->valid_workers = 0;
435 for (i = 0; i < MESSAGE_FD_MAX; i++)
436 asyncns->fds[i] = -1;
438 memset(asyncns->queries, 0, sizeof(asyncns->queries));
441 if (socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, asyncns->fds) < 0 ||
442 socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, asyncns->fds+2) < 0) {
444 /* Try again, without SOCK_CLOEXEC */
445 if (errno == EINVAL) {
447 if (socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds) < 0 ||
448 socketpair(PF_UNIX, SOCK_DGRAM, 0, asyncns->fds+2) < 0)
456 for (i = 0; i < MESSAGE_FD_MAX; i++)
457 fd_cloexec(asyncns->fds[i], true);
459 for (asyncns->valid_workers = 0; asyncns->valid_workers < n_proc; asyncns->valid_workers++) {
461 r = pthread_create(&asyncns->workers[asyncns->valid_workers], NULL, thread_worker, asyncns);
468 asyncns->current_index = asyncns->current_id = 0;
469 asyncns->done_head = asyncns->done_tail = NULL;
470 asyncns->n_queries = 0;
472 fd_nonblock(asyncns->fds[RESPONSE_RECV_FD], true);
478 asyncns_free(asyncns);
483 void asyncns_free(asyncns_t *asyncns) {
485 int saved_errno = errno;
492 if (asyncns->fds[REQUEST_SEND_FD] >= 0) {
495 req.type = REQUEST_TERMINATE;
496 req.length = sizeof(req);
499 /* Send one termination packet for each worker */
500 for (p = 0; p < asyncns->valid_workers; p++)
501 send(asyncns->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
504 /* Now terminate them and wait until they are gone. */
505 for (p = 0; p < asyncns->valid_workers; p++) {
507 if (pthread_join(asyncns->workers[p], NULL) != EINTR)
512 /* Close all communication channels */
513 for (i = 0; i < MESSAGE_FD_MAX; i++)
514 if (asyncns->fds[i] >= 0)
515 close(asyncns->fds[i]);
517 for (p = 0; p < MAX_QUERIES; p++)
518 if (asyncns->queries[p])
519 asyncns_cancel(asyncns, asyncns->queries[p]);
526 int asyncns_fd(asyncns_t *asyncns) {
529 return asyncns->fds[RESPONSE_RECV_FD];
532 static asyncns_query_t *lookup_query(asyncns_t *asyncns, unsigned id) {
536 q = asyncns->queries[id % MAX_QUERIES];
544 static void complete_query(asyncns_t *asyncns, asyncns_query_t *q) {
551 if ((q->done_prev = asyncns->done_tail))
552 asyncns->done_tail->done_next = q;
554 asyncns->done_head = q;
556 asyncns->done_tail = q;
560 static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, size_t *length) {
561 addrinfo_serialization_t s;
568 if (*length < sizeof(addrinfo_serialization_t))
571 memcpy(&s, p, sizeof(s));
573 l = sizeof(addrinfo_serialization_t) + s.ai_addrlen + s.canonname_len;
577 ai = malloc(sizeof(struct addrinfo));
582 ai->ai_canonname = NULL;
585 if (s.ai_addrlen && !(ai->ai_addr = malloc(s.ai_addrlen)))
588 if (s.canonname_len && !(ai->ai_canonname = malloc(s.canonname_len)))
591 ai->ai_flags = s.ai_flags;
592 ai->ai_family = s.ai_family;
593 ai->ai_socktype = s.ai_socktype;
594 ai->ai_protocol = s.ai_protocol;
595 ai->ai_addrlen = s.ai_addrlen;
598 memcpy(ai->ai_addr, (const uint8_t*) p + sizeof(addrinfo_serialization_t), s.ai_addrlen);
600 if (ai->ai_canonname)
601 memcpy(ai->ai_canonname, (const uint8_t*) p + sizeof(addrinfo_serialization_t) + s.ai_addrlen, s.canonname_len);
606 return (const uint8_t*) p + l;
611 asyncns_freeaddrinfo(ai);
616 static int handle_response(asyncns_t *asyncns, const packet_t *packet, size_t length) {
617 const rheader_t *resp;
622 resp = &packet->rheader;
624 assert(length >= sizeof(rheader_t));
625 assert(length == resp->length);
627 if (resp->type == RESPONSE_DIED) {
632 q = lookup_query(asyncns, resp->id);
636 switch (resp->type) {
637 case RESPONSE_ADDRINFO: {
638 const addrinfo_response_t *ai_resp = &packet->addrinfo_response;
641 struct addrinfo *prev = NULL;
643 assert(length >= sizeof(addrinfo_response_t));
644 assert(q->type == REQUEST_ADDRINFO);
646 q->ret = ai_resp->ret;
647 q->_errno = ai_resp->_errno;
648 q->_h_errno = ai_resp->_h_errno;
649 l = length - sizeof(addrinfo_response_t);
650 p = (const uint8_t*) resp + sizeof(addrinfo_response_t);
653 struct addrinfo *ai = NULL;
654 p = unserialize_addrinfo(p, &ai, &l);
669 complete_query(asyncns, q);
673 case RESPONSE_NAMEINFO: {
674 const nameinfo_response_t *ni_resp = &packet->nameinfo_response;
676 assert(length >= sizeof(nameinfo_response_t));
677 assert(q->type == REQUEST_NAMEINFO);
679 q->ret = ni_resp->ret;
680 q->_errno = ni_resp->_errno;
681 q->_h_errno = ni_resp->_h_errno;
683 if (ni_resp->hostlen)
684 if (!(q->host = strndup((const char*) ni_resp + sizeof(nameinfo_response_t), ni_resp->hostlen-1)))
687 if (ni_resp->servlen)
688 if (!(q->serv = strndup((const char*) ni_resp + sizeof(nameinfo_response_t) + ni_resp->hostlen, ni_resp->servlen-1)))
691 complete_query(asyncns, q);
696 const res_response_t *res_resp = &packet->res_response;
698 assert(length >= sizeof(res_response_t));
699 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
701 q->ret = res_resp->ret;
702 q->_errno = res_resp->_errno;
703 q->_h_errno = res_resp->_h_errno;
705 if (res_resp->ret >= 0) {
706 if (!(q->serv = malloc(res_resp->ret))) {
710 memcpy(q->serv, (const char *)resp + sizeof(res_response_t), res_resp->ret);
713 complete_query(asyncns, q);
724 int asyncns_wait(asyncns_t *asyncns, int block) {
729 packet_t buf[BUFSIZE/sizeof(packet_t) + 1];
737 l = recv(asyncns->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0);
744 if (!block || handled)
748 FD_SET(asyncns->fds[RESPONSE_RECV_FD], &fds);
750 if (select(asyncns->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0)
756 if (handle_response(asyncns, buf, (size_t) l) < 0)
763 static asyncns_query_t *alloc_query(asyncns_t *asyncns) {
767 if (asyncns->n_queries >= MAX_QUERIES) {
772 while (asyncns->queries[asyncns->current_index]) {
773 asyncns->current_index++;
774 asyncns->current_id++;
776 while (asyncns->current_index >= MAX_QUERIES)
777 asyncns->current_index -= MAX_QUERIES;
780 q = asyncns->queries[asyncns->current_index] = malloc(sizeof(asyncns_query_t));
786 asyncns->n_queries++;
788 q->asyncns = asyncns;
790 q->id = asyncns->current_id;
791 q->done_next = q->done_prev = NULL;
797 q->host = q->serv = NULL;
802 asyncns_query_t* asyncns_getaddrinfo(asyncns_t *asyncns, const char *node, const char *service, const struct addrinfo *hints) {
803 addrinfo_request_t data[BUFSIZE/sizeof(addrinfo_request_t) + 1] = {};
804 addrinfo_request_t *req = data;
807 assert(node || service);
814 q = alloc_query(asyncns);
818 req->node_len = node ? strlen(node)+1 : 0;
819 req->service_len = service ? strlen(service)+1 : 0;
821 req->header.id = q->id;
822 req->header.type = q->type = REQUEST_ADDRINFO;
823 req->header.length = sizeof(addrinfo_request_t) + req->node_len + req->service_len;
825 if (req->header.length > BUFSIZE) {
830 if (!(req->hints_is_null = !hints)) {
831 req->ai_flags = hints->ai_flags;
832 req->ai_family = hints->ai_family;
833 req->ai_socktype = hints->ai_socktype;
834 req->ai_protocol = hints->ai_protocol;
838 strcpy((char*) req + sizeof(addrinfo_request_t), node);
841 strcpy((char*) req + sizeof(addrinfo_request_t) + req->node_len, service);
843 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
850 asyncns_cancel(asyncns, q);
855 int asyncns_getaddrinfo_done(asyncns_t *asyncns, asyncns_query_t* q, struct addrinfo **ret_res) {
859 assert(q->asyncns == asyncns);
860 assert(q->type == REQUEST_ADDRINFO);
870 *ret_res = q->addrinfo;
875 if (ret == EAI_SYSTEM)
879 h_errno = q->_h_errno;
881 asyncns_cancel(asyncns, q);
886 asyncns_query_t* asyncns_getnameinfo(asyncns_t *asyncns, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) {
887 nameinfo_request_t data[BUFSIZE/sizeof(nameinfo_request_t) + 1] = {};
888 nameinfo_request_t *req = data;
900 q = alloc_query(asyncns);
904 req->header.id = q->id;
905 req->header.type = q->type = REQUEST_NAMEINFO;
906 req->header.length = sizeof(nameinfo_request_t) + salen;
908 if (req->header.length > BUFSIZE) {
914 req->sockaddr_len = salen;
915 req->gethost = gethost;
916 req->getserv = getserv;
918 memcpy((uint8_t*) req + sizeof(nameinfo_request_t), sa, salen);
920 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
927 asyncns_cancel(asyncns, q);
932 int asyncns_getnameinfo_done(asyncns_t *asyncns, asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) {
936 assert(q->asyncns == asyncns);
937 assert(q->type == REQUEST_NAMEINFO);
938 assert(!ret_host || hostlen);
939 assert(!ret_serv || servlen);
949 if (ret_host && q->host) {
950 strncpy(ret_host, q->host, hostlen);
951 ret_host[hostlen-1] = 0;
954 if (ret_serv && q->serv) {
955 strncpy(ret_serv, q->serv, servlen);
956 ret_serv[servlen-1] = 0;
961 if (ret == EAI_SYSTEM)
965 h_errno = q->_h_errno;
967 asyncns_cancel(asyncns, q);
972 static asyncns_query_t * asyncns_res(asyncns_t *asyncns, query_type_t qtype, const char *dname, int class, int type) {
973 res_request_t data[BUFSIZE/sizeof(res_request_t) + 1];
974 res_request_t *req = data;
985 q = alloc_query(asyncns);
989 req->dname_len = strlen(dname) + 1;
991 req->header.id = q->id;
992 req->header.type = q->type = qtype;
993 req->header.length = sizeof(res_request_t) + req->dname_len;
995 if (req->header.length > BUFSIZE) {
1003 strcpy((char*) req + sizeof(res_request_t), dname);
1005 if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
1012 asyncns_cancel(asyncns, q);
1017 asyncns_query_t* asyncns_res_query(asyncns_t *asyncns, const char *dname, int class, int type) {
1018 return asyncns_res(asyncns, REQUEST_RES_QUERY, dname, class, type);
1021 asyncns_query_t* asyncns_res_search(asyncns_t *asyncns, const char *dname, int class, int type) {
1022 return asyncns_res(asyncns, REQUEST_RES_SEARCH, dname, class, type);
1025 int asyncns_res_done(asyncns_t *asyncns, asyncns_query_t* q, unsigned char **answer) {
1029 assert(q->asyncns == asyncns);
1030 assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
1033 if (asyncns->dead) {
1043 *answer = (unsigned char *)q->serv;
1050 h_errno = q->_h_errno;
1053 asyncns_cancel(asyncns, q);
1055 return ret < 0 ? -errno : ret;
1058 asyncns_query_t* asyncns_getnext(asyncns_t *asyncns) {
1060 return asyncns->done_head;
1063 int asyncns_getnqueries(asyncns_t *asyncns) {
1065 return asyncns->n_queries;
1068 void asyncns_cancel(asyncns_t *asyncns, asyncns_query_t* q) {
1070 int saved_errno = errno;
1074 assert(q->asyncns == asyncns);
1075 assert(asyncns->n_queries > 0);
1080 q->done_prev->done_next = q->done_next;
1082 asyncns->done_head = q->done_next;
1085 q->done_next->done_prev = q->done_prev;
1087 asyncns->done_tail = q->done_prev;
1090 i = q->id % MAX_QUERIES;
1091 assert(asyncns->queries[i] == q);
1092 asyncns->queries[i] = NULL;
1094 asyncns_freeaddrinfo(q->addrinfo);
1098 asyncns->n_queries--;
1101 errno = saved_errno;
1104 void asyncns_freeaddrinfo(struct addrinfo *ai) {
1105 int saved_errno = errno;
1108 struct addrinfo *next = ai->ai_next;
1111 free(ai->ai_canonname);
1117 errno = saved_errno;
1120 void asyncns_freeanswer(unsigned char *answer) {
1121 int saved_errno = errno;
1126 /* Please note that this function is new in libasyncns 0.4. In
1127 * older versions you were supposed to free the answer directly
1128 * with free(). Hence, if this function is changed to do more than
1129 * just a simple free() this must be considered ABI/API breakage! */
1133 errno = saved_errno;
1136 int asyncns_isdone(asyncns_t *asyncns, asyncns_query_t*q) {
1139 assert(q->asyncns == asyncns);
1144 void asyncns_setuserdata(asyncns_t *asyncns, asyncns_query_t *q, void *userdata) {
1147 assert(q->asyncns = asyncns);
1149 q->userdata = userdata;
1152 void* asyncns_getuserdata(asyncns_t *asyncns, asyncns_query_t *q) {
1155 assert(q->asyncns = asyncns);