X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-resolve%2Fsd-resolve.c;h=df63f1de9e407c39d373a5f5829e23e55008d686;hb=502fe44ea44fdbe2282fbe0625bb2e01777425c0;hp=464967b36247ef796334683afe0de73b3a7e63e7;hpb=607553f9306286fdccf0b356bc3d1087adfe21c4;p=elogind.git diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 464967b36..df63f1de9 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -40,13 +40,19 @@ #include #include #include +#include -#include "sd-resolve.h" #include "util.h" +#include "list.h" +#include "socket-util.h" +#include "missing.h" +#include "resolve-util.h" +#include "sd-resolve.h" -#define MAX_WORKERS 16 -#define MAX_QUERIES 256 -#define BUFSIZE (10240) +#define WORKERS_MIN 1U +#define WORKERS_MAX 16U +#define QUERIES_MAX 256U +#define BUFSIZE 10240U typedef enum { REQUEST_ADDRINFO, @@ -61,40 +67,65 @@ typedef enum { } QueryType; enum { - REQUEST_RECV_FD = 0, - REQUEST_SEND_FD = 1, - RESPONSE_RECV_FD = 2, - RESPONSE_SEND_FD = 3, - MESSAGE_FD_MAX = 4 + REQUEST_RECV_FD, + REQUEST_SEND_FD, + RESPONSE_RECV_FD, + RESPONSE_SEND_FD, + _FD_MAX }; struct sd_resolve { - int fds[MESSAGE_FD_MAX]; + unsigned n_ref; + + bool dead:1; + pid_t original_pid; - pthread_t workers[MAX_WORKERS]; - unsigned valid_workers; + int fds[_FD_MAX]; + + pthread_t workers[WORKERS_MAX]; + unsigned n_valid_workers; unsigned current_id, current_index; - sd_resolve_query* queries[MAX_QUERIES]; + sd_resolve_query* query_array[QUERIES_MAX]; + unsigned n_queries, n_done; + + sd_event_source *event_source; + sd_event *event; + + sd_resolve_query *current; - sd_resolve_query *done_head, *done_tail; + sd_resolve **default_resolve_ptr; + pid_t tid; - int n_queries; - int dead; + LIST_HEAD(sd_resolve_query, queries); }; struct sd_resolve_query { + unsigned n_ref; + sd_resolve *resolve; - int done; + + QueryType type:4; + bool done:1; + bool floating:1; unsigned id; - QueryType type; - sd_resolve_query *done_next, *done_prev; + int ret; int _errno; int _h_errno; struct addrinfo *addrinfo; char *serv, *host; + unsigned char *answer; + + union { + sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; + sd_resolve_getnameinfo_handler_t getnameinfo_handler; + sd_resolve_res_handler_t res_handler; + }; + void *userdata; + + LIST_FIELDS(sd_resolve_query, queries); }; typedef struct RHeader { @@ -105,7 +136,7 @@ typedef struct RHeader { typedef struct AddrInfoRequest { struct RHeader header; - int hints_is_null; + bool hints_valid; int ai_flags; int ai_family; int ai_socktype; @@ -135,7 +166,7 @@ typedef struct NameInfoRequest { struct RHeader header; int flags; socklen_t sockaddr_len; - int gethost, getserv; + bool gethost:1, getserv:1; } NameInfoRequest; typedef struct NameInfoResponse { @@ -170,26 +201,40 @@ typedef union Packet { ResResponse res_response; } Packet; +static int getaddrinfo_done(sd_resolve_query* q); +static int getnameinfo_done(sd_resolve_query *q); +static int res_query_done(sd_resolve_query* q); + +static void resolve_query_disconnect(sd_resolve_query *q); + +#define RESOLVE_DONT_DESTROY(resolve) \ + _cleanup_resolve_unref_ _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) + static int send_died(int out_fd) { - RHeader rh = {}; - assert(out_fd > 0); - rh.type = RESPONSE_DIED; - rh.id = 0; - rh.length = sizeof(rh); + RHeader rh = { + .type = RESPONSE_DIED, + .length = sizeof(RHeader), + }; + + assert(out_fd >= 0); - return send(out_fd, &rh, rh.length, MSG_NOSIGNAL); + if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; } static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) { AddrInfoSerialization s; size_t cnl, l; + assert(p); assert(ai); assert(length); assert(*length <= maxlength); - cnl = (ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0); + cnl = ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0; l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl; if (*length + l > maxlength) @@ -206,33 +251,47 @@ static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *leng memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen); if (ai->ai_canonname) - strcpy((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname); + memcpy((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname, cnl); *length += l; return (uint8_t*) p + l; } -static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo *ai, int _errno, int _h_errno) { - AddrInfoResponse data[BUFSIZE/sizeof(AddrInfoResponse) + 1] = {}; - AddrInfoResponse *resp = data; - assert(out_fd >= 0); +static int send_addrinfo_reply( + int out_fd, + unsigned id, + int ret, + struct addrinfo *ai, + int _errno, + int _h_errno) { + + AddrInfoResponse resp = { + .header.type = RESPONSE_ADDRINFO, + .header.id = id, + .header.length = sizeof(AddrInfoResponse), + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[2]; + union { + AddrInfoSerialization ais; + uint8_t space[BUFSIZE]; + } buffer; - resp->header.type = RESPONSE_ADDRINFO; - resp->header.id = id; - resp->header.length = sizeof(AddrInfoResponse); - resp->ret = ret; - resp->_errno = _errno; - resp->_h_errno = _h_errno; + assert(out_fd >= 0); if (ret == 0 && ai) { - void *p = data + 1; + void *p = &buffer; struct addrinfo *k; for (k = ai; k; k = k->ai_next) { - p = serialize_addrinfo(p, k, &resp->header.length, (char*) data + BUFSIZE - (char*) p); + p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); if (!p) { - resp->ret = EAI_MEMORY; - break; + freeaddrinfo(ai); + return -ENOBUFS; } } } @@ -240,89 +299,126 @@ static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo if (ai) freeaddrinfo(ai); - return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL); + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) }; + iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) }; + + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); + + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; } -static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) { - NameInfoResponse data[BUFSIZE/sizeof(NameInfoResponse) + 1] = {}; +static int send_nameinfo_reply( + int out_fd, + unsigned id, + int ret, + const char *host, + const char *serv, + int _errno, + int _h_errno) { + + NameInfoResponse resp = { + .header.type = RESPONSE_NAMEINFO, + .header.id = id, + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[3]; size_t hl, sl; - NameInfoResponse *resp = data; assert(out_fd >= 0); sl = serv ? strlen(serv)+1 : 0; hl = host ? strlen(host)+1 : 0; - resp->header.type = RESPONSE_NAMEINFO; - resp->header.id = id; - resp->header.length = sizeof(NameInfoResponse) + hl + sl; - resp->ret = ret; - resp->_errno = _errno; - resp->_h_errno = _h_errno; - resp->hostlen = hl; - resp->servlen = sl; + resp.header.length = sizeof(NameInfoResponse) + hl + sl; + resp.hostlen = hl; + resp.servlen = sl; - assert(sizeof(data) >= resp->header.length); + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) }; + iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl }; + iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl }; - if (host) - memcpy((uint8_t *)data + sizeof(NameInfoResponse), host, hl); + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); - if (serv) - memcpy((uint8_t *)data + sizeof(NameInfoResponse) + hl, serv, sl); + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; - return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL); + return 0; } static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) { - ResResponse data[BUFSIZE/sizeof(ResResponse) + 1] = {}; - ResResponse *resp = data; + + ResResponse resp = { + .header.type = RESPONSE_RES, + .header.id = id, + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[2]; + size_t l; assert(out_fd >= 0); - resp->header.type = RESPONSE_RES; - resp->header.id = id; - resp->header.length = sizeof(ResResponse) + (ret < 0 ? 0 : ret); - resp->ret = ret; - resp->_errno = _errno; - resp->_h_errno = _h_errno; + l = ret > 0 ? (size_t) ret : 0; + + resp.header.length = sizeof(ResResponse) + l; + + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(ResResponse) }; + iov[1] = (struct iovec) { .iov_base = (void*) answer, .iov_len = l }; - assert(sizeof(data) >= resp->header.length); + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); - if (ret > 0) - memcpy((uint8_t *)data + sizeof(ResResponse), answer, ret); + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; - return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL); + return 0; } static int handle_request(int out_fd, const Packet *packet, size_t length) { const RHeader *req; + assert(out_fd >= 0); + assert(packet); req = &packet->rheader; - assert(req); + assert(length >= sizeof(RHeader)); assert(length == req->length); switch (req->type) { + case REQUEST_ADDRINFO: { - struct addrinfo ai = {}, *result = NULL; const AddrInfoRequest *ai_req = &packet->addrinfo_request; + struct addrinfo hints = {}, *result = NULL; const char *node, *service; int ret; assert(length >= sizeof(AddrInfoRequest)); assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len); - ai.ai_flags = ai_req->ai_flags; - ai.ai_family = ai_req->ai_family; - ai.ai_socktype = ai_req->ai_socktype; - ai.ai_protocol = ai_req->ai_protocol; + hints.ai_flags = ai_req->ai_flags; + hints.ai_family = ai_req->ai_family; + hints.ai_socktype = ai_req->ai_socktype; + hints.ai_protocol = ai_req->ai_protocol; node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL; service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL; - ret = getaddrinfo(node, service, - ai_req->hints_is_null ? NULL : &ai, + ret = getaddrinfo( + node, service, + ai_req->hints_valid ? &hints : NULL, &result); /* send_addrinfo_reply() frees result */ @@ -330,17 +426,18 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { } case REQUEST_NAMEINFO: { - int ret; const NameInfoRequest *ni_req = &packet->nameinfo_request; char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; - struct sockaddr_storage sa; + union sockaddr_union sa; + int ret; assert(length >= sizeof(NameInfoRequest)); assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len); + assert(sizeof(sa) >= ni_req->sockaddr_len); memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); - ret = getnameinfo((struct sockaddr *)&sa, ni_req->sockaddr_len, + ret = getnameinfo(&sa.sa, ni_req->sockaddr_len, ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0, ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0, ni_req->flags); @@ -353,10 +450,13 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { case REQUEST_RES_QUERY: case REQUEST_RES_SEARCH: { - int ret; - HEADER answer[BUFSIZE/sizeof(HEADER) + 1]; const ResRequest *res_req = &packet->res_request; + union { + HEADER header; + uint8_t space[BUFSIZE]; + } answer; const char *dname; + int ret; assert(length >= sizeof(ResRequest)); assert(length == sizeof(ResRequest) + res_req->dname_len); @@ -364,19 +464,19 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { dname = (const char *) req + sizeof(ResRequest); if (req->type == REQUEST_RES_QUERY) - ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE); + ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE); else - ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE); + ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE); - return send_res_reply(out_fd, req->id, (unsigned char *) answer, ret, errno, h_errno); + return send_res_reply(out_fd, req->id, (unsigned char *) &answer, ret, errno, h_errno); } case REQUEST_TERMINATE: /* Quit */ - return -1; + return -ECONNRESET; default: - ; + assert_not_reached("Unknown request"); } return 0; @@ -387,25 +487,33 @@ static void* thread_worker(void *p) { sigset_t fullset; /* No signals in this thread please */ - sigfillset(&fullset); - pthread_sigmask(SIG_BLOCK, &fullset, NULL); + assert_se(sigfillset(&fullset) == 0); + assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); + + /* Assign a pretty name to this thread */ + prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); while (!resolve->dead) { - Packet buf[BUFSIZE/sizeof(Packet) + 1]; + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; ssize_t length; - length = recv(resolve->fds[REQUEST_RECV_FD], buf, sizeof(buf), 0); - - if (length <= 0) { - if (length < 0 && (errno == EAGAIN || errno == EINTR)) + length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof(buf), 0); + if (length < 0) { + if (errno == EINTR) continue; + break; } + if (length == 0) + break; if (resolve->dead) break; - if (handle_request(resolve->fds[RESPONSE_SEND_FD], buf, (size_t) length) < 0) + if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) break; } @@ -414,114 +522,218 @@ static void* thread_worker(void *p) { return NULL; } -_public_ sd_resolve* sd_resolve_new(unsigned n_proc) { - sd_resolve *resolve = NULL; - int i, r; +static int start_threads(sd_resolve *resolve, unsigned extra) { + unsigned n; + int r; - assert(n_proc >= 1); + n = resolve->n_queries + extra - resolve->n_done; + n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); - if (n_proc > MAX_WORKERS) - n_proc = MAX_WORKERS; + while (resolve->n_valid_workers < n) { - resolve = new(sd_resolve, 1); - if (!resolve) { - errno = ENOMEM; - goto fail; + r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); + if (r != 0) + return -r; + + resolve->n_valid_workers ++; } - resolve->dead = 0; - resolve->valid_workers = 0; + return 0; +} - for (i = 0; i < MESSAGE_FD_MAX; i++) - resolve->fds[i] = -1; +static bool resolve_pid_changed(sd_resolve *r) { + assert(r); - memset(resolve->queries, 0, sizeof(resolve->queries)); + /* We don't support people creating a resolver and keeping it + * around after fork(). Let's complain. */ - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds); - if (r < 0) - goto fail; + return r->original_pid != getpid(); +} - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds+2); - if (r < 0) +_public_ int sd_resolve_new(sd_resolve **ret) { + sd_resolve *resolve = NULL; + int i, r; + + assert_return(ret, -EINVAL); + + resolve = new0(sd_resolve, 1); + if (!resolve) + return -ENOMEM; + + resolve->n_ref = 1; + resolve->original_pid = getpid(); + + for (i = 0; i < _FD_MAX; i++) + resolve->fds[i] = -1; + + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD); + if (r < 0) { + r = -errno; goto fail; + } - for (resolve->valid_workers = 0; resolve->valid_workers < n_proc; resolve->valid_workers++) { - r = pthread_create(&resolve->workers[resolve->valid_workers], NULL, thread_worker, resolve); - if (r) { - errno = r; - goto fail; - } + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD); + if (r < 0) { + r = -errno; + goto fail; } - resolve->current_index = resolve->current_id = 0; - resolve->done_head = resolve->done_tail = NULL; - resolve->n_queries = 0; + fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); + fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); + fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); + fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); - return resolve; + *ret = resolve; + return 0; fail: - if (resolve) - sd_resolve_free(resolve); + sd_resolve_unref(resolve); + return r; +} - return NULL; +_public_ int sd_resolve_default(sd_resolve **ret) { + + static thread_local sd_resolve *default_resolve = NULL; + sd_resolve *e = NULL; + int r; + + if (!ret) + return !!default_resolve; + + if (default_resolve) { + *ret = sd_resolve_ref(default_resolve); + return 0; + } + + r = sd_resolve_new(&e); + if (r < 0) + return r; + + e->default_resolve_ptr = &default_resolve; + e->tid = gettid(); + default_resolve = e; + + *ret = e; + return 1; } -_public_ void sd_resolve_free(sd_resolve *resolve) { - int i; - int saved_errno = errno; - unsigned p; +_public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { + assert_return(resolve, -EINVAL); + assert_return(tid, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + if (resolve->tid != 0) { + *tid = resolve->tid; + return 0; + } + + if (resolve->event) + return sd_event_get_tid(resolve->event, tid); + + return -ENXIO; +} + +static void resolve_free(sd_resolve *resolve) { + PROTECT_ERRNO; + sd_resolve_query *q; + unsigned i; assert(resolve); - resolve->dead = 1; + while ((q = resolve->queries)) { + assert(q->floating); + resolve_query_disconnect(q); + sd_resolve_query_unref(q); + } + + if (resolve->default_resolve_ptr) + *(resolve->default_resolve_ptr) = NULL; + + resolve->dead = true; + + sd_resolve_detach_event(resolve); if (resolve->fds[REQUEST_SEND_FD] >= 0) { - RHeader req = {}; - req.type = REQUEST_TERMINATE; - req.length = sizeof(req); - req.id = 0; + RHeader req = { + .type = REQUEST_TERMINATE, + .length = sizeof(req) + }; /* Send one termination packet for each worker */ - for (p = 0; p < resolve->valid_workers; p++) + for (i = 0; i < resolve->n_valid_workers; i++) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); } /* Now terminate them and wait until they are gone. */ - for (p = 0; p < resolve->valid_workers; p++) { + for (i = 0; i < resolve->n_valid_workers; i++) { for (;;) { - if (pthread_join(resolve->workers[p], NULL) != EINTR) + if (pthread_join(resolve->workers[i], NULL) != EINTR) break; } } /* Close all communication channels */ - for (i = 0; i < MESSAGE_FD_MAX; i++) - if (resolve->fds[i] >= 0) - close(resolve->fds[i]); - - for (p = 0; p < MAX_QUERIES; p++) - if (resolve->queries[p]) - sd_resolve_cancel(resolve, resolve->queries[p]); + for (i = 0; i < _FD_MAX; i++) + safe_close(resolve->fds[i]); free(resolve); +} + +_public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) { + assert_return(resolve, NULL); - errno = saved_errno; + assert(resolve->n_ref >= 1); + resolve->n_ref++; + + return resolve; } -_public_ int sd_resolve_fd(sd_resolve *resolve) { - assert(resolve); +_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { + + if (!resolve) + return NULL; + + assert(resolve->n_ref >= 1); + resolve->n_ref--; + + if (resolve->n_ref <= 0) + resolve_free(resolve); + + return NULL; +} + +_public_ int sd_resolve_get_fd(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); return resolve->fds[RESPONSE_RECV_FD]; } +_public_ int sd_resolve_get_events(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + return resolve->n_queries > resolve->n_done ? POLLIN : 0; +} + +_public_ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { + assert_return(resolve, -EINVAL); + assert_return(usec, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + *usec = (uint64_t) -1; + return 0; +} + static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { sd_resolve_query *q; + assert(resolve); - q = resolve->queries[id % MAX_QUERIES]; + q = resolve->query_array[id % QUERIES_MAX]; if (q) if (q->id == id) return q; @@ -529,52 +741,71 @@ static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { return NULL; } -static void complete_query(sd_resolve *resolve, sd_resolve_query *q) { - assert(resolve); +static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { + int r; + assert(q); assert(!q->done); + assert(q->resolve == resolve); + + q->done = true; + resolve->n_done ++; + + resolve->current = sd_resolve_query_ref(q); + + switch (q->type) { + + case REQUEST_ADDRINFO: + r = getaddrinfo_done(q); + break; + + case REQUEST_NAMEINFO: + r = getnameinfo_done(q); + break; + + case REQUEST_RES_QUERY: + case REQUEST_RES_SEARCH: + r = res_query_done(q); + break; - q->done = 1; + default: + assert_not_reached("Cannot complete unknown query type"); + } - if ((q->done_prev = resolve->done_tail)) - resolve->done_tail->done_next = q; - else - resolve->done_head = q; + resolve->current = NULL; - resolve->done_tail = q; - q->done_next = NULL; + if (q->floating) { + resolve_query_disconnect(q); + sd_resolve_query_unref(q); + } + + sd_resolve_query_unref(q); + + return r; } -static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, size_t *length) { +static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { AddrInfoSerialization s; size_t l; struct addrinfo *ai; + assert(p); + assert(*p); assert(ret_ai); assert(length); if (*length < sizeof(AddrInfoSerialization)) - return NULL; + return -EBADMSG; - memcpy(&s, p, sizeof(s)); + memcpy(&s, *p, sizeof(s)); l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; if (*length < l) - return NULL; + return -EBADMSG; - ai = new(struct addrinfo, 1); + ai = new0(struct addrinfo, 1); if (!ai) - goto fail; - - ai->ai_addr = NULL; - ai->ai_canonname = NULL; - ai->ai_next = NULL; - - if (s.ai_addrlen && !(ai->ai_addr = malloc(s.ai_addrlen))) - goto fail; - - if (s.canonname_len && !(ai->ai_canonname = malloc(s.canonname_len))) - goto fail; + return -ENOMEM; ai->ai_flags = s.ai_flags; ai->ai_family = s.ai_family; @@ -582,28 +813,34 @@ static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, ai->ai_protocol = s.ai_protocol; ai->ai_addrlen = s.ai_addrlen; - if (ai->ai_addr) - memcpy(ai->ai_addr, (const uint8_t*) p + sizeof(AddrInfoSerialization), s.ai_addrlen); + if (s.ai_addrlen > 0) { + ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); + if (!ai->ai_addr) { + free(ai); + return -ENOMEM; + } + } - if (ai->ai_canonname) - memcpy(ai->ai_canonname, (const uint8_t*) p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); + if (s.canonname_len > 0) { + ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); + if (!ai->ai_canonname) { + free(ai->ai_addr); + free(ai); + return -ENOMEM; + } + } *length -= l; *ret_ai = ai; + *p = ((const uint8_t*) *p) + l; - return (const uint8_t*) p + l; - - -fail: - if (ai) - sd_resolve_freeaddrinfo(ai); - - return NULL; + return 0; } static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { const RHeader *resp; sd_resolve_query *q; + int r; assert(resolve); @@ -613,7 +850,7 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len assert(length == resp->length); if (resp->type == RESPONSE_DIED) { - resolve->dead = 1; + resolve->dead = true; return 0; } @@ -622,6 +859,7 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len return 0; switch (resp->type) { + case RESPONSE_ADDRINFO: { const AddrInfoResponse *ai_resp = &packet->addrinfo_response; const void *p; @@ -634,15 +872,20 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len q->ret = ai_resp->ret; q->_errno = ai_resp->_errno; q->_h_errno = ai_resp->_h_errno; + l = length - sizeof(AddrInfoResponse); p = (const uint8_t*) resp + sizeof(AddrInfoResponse); while (l > 0 && p) { struct addrinfo *ai = NULL; - p = unserialize_addrinfo(p, &ai, &l); - if (!p || !ai) { - q->ret = EAI_MEMORY; + r = unserialize_addrinfo(&p, &l, &ai); + if (r < 0) { + q->ret = EAI_SYSTEM; + q->_errno = -r; + q->_h_errno = 0; + freeaddrinfo(q->addrinfo); + q->addrinfo = NULL; break; } @@ -654,8 +897,7 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len prev = ai; } - complete_query(resolve, q); - break; + return complete_query(resolve, q); } case RESPONSE_NAMEINFO: { @@ -668,16 +910,25 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len q->_errno = ni_resp->_errno; q->_h_errno = ni_resp->_h_errno; - if (ni_resp->hostlen) - if (!(q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1))) + if (ni_resp->hostlen > 0) { + q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1); + if (!q->host) { q->ret = EAI_MEMORY; + q->_errno = ENOMEM; + q->_h_errno = 0; + } + } - if (ni_resp->servlen) - if (!(q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1))) + if (ni_resp->servlen > 0) { + q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1); + if (!q->serv) { q->ret = EAI_MEMORY; + q->_errno = ENOMEM; + q->_h_errno = 0; + } + } - complete_query(resolve, q); - break; + return complete_query(resolve, q); } case RESPONSE_RES: { @@ -691,440 +942,491 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len q->_h_errno = res_resp->_h_errno; if (res_resp->ret >= 0) { - if (!(q->serv = malloc(res_resp->ret))) { + q->answer = memdup((const char *)resp + sizeof(ResResponse), res_resp->ret); + if (!q->answer) { q->ret = -1; q->_errno = ENOMEM; - } else - memcpy(q->serv, (const char *)resp + sizeof(ResResponse), res_resp->ret); + q->_h_errno = 0; + } } - complete_query(resolve, q); - break; + return complete_query(resolve, q); } default: - ; + return 0; } - - return 0; } -_public_ int sd_resolve_wait(sd_resolve *resolve, int block) { - int handled = 0; - assert(resolve); +_public_ int sd_resolve_process(sd_resolve *resolve) { + RESOLVE_DONT_DESTROY(resolve); - for (;;) { - Packet buf[BUFSIZE/sizeof(Packet) + 1]; - ssize_t l; + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; + ssize_t l; + int r; - if (resolve->dead) { - errno = ECHILD; - return -1; - } + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - l = recv(resolve->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0); - if (l < 0) { - fd_set fds; + /* We don't allow recursively invoking sd_resolve_process(). */ + assert_return(!resolve->current, -EBUSY); - if (errno != EAGAIN) - return -1; + l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof(buf), 0); + if (l < 0) { + if (errno == EAGAIN) + return 0; - if (!block || handled) - return 0; + return -errno; + } + if (l == 0) + return -ECONNREFUSED; - FD_ZERO(&fds); - FD_SET(resolve->fds[RESPONSE_RECV_FD], &fds); + r = handle_response(resolve, &buf.packet, (size_t) l); + if (r < 0) + return r; - if (select(resolve->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0) - return -1; + return 1; +} - continue; - } +_public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { + int r; - if (handle_response(resolve, buf, (size_t) l) < 0) - return -1; + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - handled = 1; - } + if (resolve->n_done >= resolve->n_queries) + return 0; + + do { + r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); + } while (r == -EINTR); + + if (r < 0) + return r; + + return sd_resolve_process(resolve); } -static sd_resolve_query *alloc_query(sd_resolve *resolve) { +static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { sd_resolve_query *q; + int r; + assert(resolve); + assert(_q); - if (resolve->n_queries >= MAX_QUERIES) { - errno = ENOMEM; - return NULL; - } + if (resolve->n_queries >= QUERIES_MAX) + return -ENOBUFS; - while (resolve->queries[resolve->current_index]) { + r = start_threads(resolve, 1); + if (r < 0) + return r; + + while (resolve->query_array[resolve->current_index]) { resolve->current_index++; resolve->current_id++; - while (resolve->current_index >= MAX_QUERIES) - resolve->current_index -= MAX_QUERIES; - } - - q = resolve->queries[resolve->current_index] = new(sd_resolve_query, 1); - if (!q) { - errno = ENOMEM; - return NULL; + resolve->current_index %= QUERIES_MAX; } - resolve->n_queries++; + q = resolve->query_array[resolve->current_index] = new0(sd_resolve_query, 1); + if (!q) + return -ENOMEM; + q->n_ref = 1; q->resolve = resolve; - q->done = 0; + q->floating = floating; q->id = resolve->current_id; - q->done_next = q->done_prev = NULL; - q->ret = 0; - q->_errno = 0; - q->_h_errno = 0; - q->addrinfo = NULL; - q->userdata = NULL; - q->host = q->serv = NULL; - return q; + if (!floating) + sd_resolve_ref(resolve); + + LIST_PREPEND(queries, resolve->queries, q); + resolve->n_queries++; + + *_q = q; + return 0; } -_public_ sd_resolve_query* sd_resolve_getaddrinfo(sd_resolve *resolve, const char *node, const char *service, const struct addrinfo *hints) { - AddrInfoRequest data[BUFSIZE/sizeof(AddrInfoRequest) + 1] = {}; - AddrInfoRequest *req = data; +_public_ int sd_resolve_getaddrinfo( + sd_resolve *resolve, + sd_resolve_query **_q, + const char *node, const char *service, + const struct addrinfo *hints, + sd_resolve_getaddrinfo_handler_t callback, void *userdata) { + + AddrInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[3]; sd_resolve_query *q; - assert(resolve); - assert(node || service); + int r; - if (resolve->dead) { - errno = ECHILD; - return NULL; - } + assert_return(resolve, -EINVAL); + assert_return(node || service, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - q = alloc_query(resolve); - if (!q) - return NULL; + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; - req->node_len = node ? strlen(node)+1 : 0; - req->service_len = service ? strlen(service)+1 : 0; + q->type = REQUEST_ADDRINFO; + q->getaddrinfo_handler = callback; + q->userdata = userdata; - req->header.id = q->id; - req->header.type = q->type = REQUEST_ADDRINFO; - req->header.length = sizeof(AddrInfoRequest) + req->node_len + req->service_len; + req.node_len = node ? strlen(node)+1 : 0; + req.service_len = service ? strlen(service)+1 : 0; - if (req->header.length > BUFSIZE) { - errno = ENOMEM; - goto fail; - } + req.header.id = q->id; + req.header.type = REQUEST_ADDRINFO; + req.header.length = sizeof(AddrInfoRequest) + req.node_len + req.service_len; - if (!(req->hints_is_null = !hints)) { - req->ai_flags = hints->ai_flags; - req->ai_family = hints->ai_family; - req->ai_socktype = hints->ai_socktype; - req->ai_protocol = hints->ai_protocol; + if (hints) { + req.hints_valid = true; + req.ai_flags = hints->ai_flags; + req.ai_family = hints->ai_family; + req.ai_socktype = hints->ai_socktype; + req.ai_protocol = hints->ai_protocol; } + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) }; if (node) - strcpy((char*) req + sizeof(AddrInfoRequest), node); - + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len }; if (service) - strcpy((char*) req + sizeof(AddrInfoRequest) + req->node_len, service); + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len }; + mh.msg_iov = iov; - if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0) - goto fail; - - return q; + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; + } -fail: - if (q) - sd_resolve_cancel(resolve, q); + if (_q) + *_q = q; - return NULL; + return 0; } -_public_ int sd_resolve_getaddrinfo_done(sd_resolve *resolve, sd_resolve_query* q, struct addrinfo **ret_res) { - int ret; - assert(resolve); +static int getaddrinfo_done(sd_resolve_query* q) { assert(q); - assert(q->resolve == resolve); - assert(q->type == REQUEST_ADDRINFO); + assert(q->done); + assert(q->getaddrinfo_handler); - if (resolve->dead) { - errno = ECHILD; - return EAI_SYSTEM; - } + errno = q->_errno; + h_errno = q->_h_errno; - if (!q->done) - return EAI_AGAIN; + return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); +} - *ret_res = q->addrinfo; - q->addrinfo = NULL; +_public_ int sd_resolve_getnameinfo( + sd_resolve *resolve, + sd_resolve_query**_q, + const struct sockaddr *sa, socklen_t salen, + int flags, + uint64_t get, + sd_resolve_getnameinfo_handler_t callback, + void *userdata) { + + NameInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[2]; + sd_resolve_query *q; + int r; - ret = q->ret; + assert_return(resolve, -EINVAL); + assert_return(sa, -EINVAL); + assert_return(salen >= sizeof(struct sockaddr), -EINVAL); + assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); + assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - if (ret == EAI_SYSTEM) - errno = q->_errno; + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; - if (ret != 0) - h_errno = q->_h_errno; + q->type = REQUEST_NAMEINFO; + q->getnameinfo_handler = callback; + q->userdata = userdata; - sd_resolve_cancel(resolve, q); + req.header.id = q->id; + req.header.type = REQUEST_NAMEINFO; + req.header.length = sizeof(NameInfoRequest) + salen; - return ret; -} + req.flags = flags; + req.sockaddr_len = salen; + req.gethost = !!(get & SD_RESOLVE_GET_HOST); + req.getserv = !!(get & SD_RESOLVE_GET_SERVICE); -_public_ sd_resolve_query* sd_resolve_getnameinfo(sd_resolve *resolve, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) { - NameInfoRequest data[BUFSIZE/sizeof(NameInfoRequest) + 1] = {}; - NameInfoRequest *req = data; - sd_resolve_query *q; + iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; + iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; - assert(resolve); - assert(sa); - assert(salen > 0); + mh.msg_iov = iov; + mh.msg_iovlen = 2; - if (resolve->dead) { - errno = ECHILD; - return NULL; + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; } - q = alloc_query(resolve); - if (!q) - return NULL; + if (_q) + *_q = q; - req->header.id = q->id; - req->header.type = q->type = REQUEST_NAMEINFO; - req->header.length = sizeof(NameInfoRequest) + salen; + return 0; +} - if (req->header.length > BUFSIZE) { - errno = ENOMEM; - goto fail; - } +static int getnameinfo_done(sd_resolve_query *q) { - req->flags = flags; - req->sockaddr_len = salen; - req->gethost = gethost; - req->getserv = getserv; + assert(q); + assert(q->done); + assert(q->getnameinfo_handler); - memcpy((uint8_t*) req + sizeof(NameInfoRequest), sa, salen); + errno = q->_errno; + h_errno= q->_h_errno; - if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0) - goto fail; + return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); +} - return q; +static int resolve_res( + sd_resolve *resolve, + sd_resolve_query **_q, + QueryType qtype, + const char *dname, + int class, int type, + sd_resolve_res_handler_t callback, void *userdata) { + + struct msghdr mh = {}; + struct iovec iov[2]; + ResRequest req = {}; + sd_resolve_query *q; + int r; -fail: - if (q) - sd_resolve_cancel(resolve, q); + assert_return(resolve, -EINVAL); + assert_return(dname, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - return NULL; -} + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; -_public_ int sd_resolve_getnameinfo_done(sd_resolve *resolve, sd_resolve_query* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) { - int ret; - assert(resolve); - assert(q); - assert(q->resolve == resolve); - assert(q->type == REQUEST_NAMEINFO); - assert(!ret_host || hostlen); - assert(!ret_serv || servlen); + q->type = qtype; + q->res_handler = callback; + q->userdata = userdata; - if (resolve->dead) { - errno = ECHILD; - return EAI_SYSTEM; - } + req.dname_len = strlen(dname) + 1; + req.class = class; + req.type = type; - if (!q->done) - return EAI_AGAIN; + req.header.id = q->id; + req.header.type = qtype; + req.header.length = sizeof(ResRequest) + req.dname_len; - if (ret_host && q->host) { - strncpy(ret_host, q->host, hostlen); - ret_host[hostlen-1] = 0; - } + iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(ResRequest) }; + iov[1] = (struct iovec) { .iov_base = (void*) dname, .iov_len = req.dname_len }; - if (ret_serv && q->serv) { - strncpy(ret_serv, q->serv, servlen); - ret_serv[servlen-1] = 0; - } + mh.msg_iov = iov; + mh.msg_iovlen = 2; - ret = q->ret; + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; + } - if (ret == EAI_SYSTEM) - errno = q->_errno; + if (_q) + *_q = q; - if (ret != 0) - h_errno = q->_h_errno; + return 0; +} - sd_resolve_cancel(resolve, q); +_public_ int sd_resolve_res_query(sd_resolve *resolve, sd_resolve_query** q, const char *dname, int class, int type, sd_resolve_res_handler_t callback, void *userdata) { + return resolve_res(resolve, q, REQUEST_RES_QUERY, dname, class, type, callback, userdata); +} - return ret; +_public_ int sd_resolve_res_search(sd_resolve *resolve, sd_resolve_query** q, const char *dname, int class, int type, sd_resolve_res_handler_t callback, void *userdata) { + return resolve_res(resolve, q, REQUEST_RES_SEARCH, dname, class, type, callback, userdata); } -static sd_resolve_query * resolve_res(sd_resolve *resolve, QueryType qtype, const char *dname, int class, int type) { - ResRequest data[BUFSIZE/sizeof(ResRequest) + 1]; - ResRequest *req = data; - sd_resolve_query *q; +static int res_query_done(sd_resolve_query* q) { + assert(q); + assert(q->done); + assert(q->res_handler); - assert(resolve); - assert(dname); + errno = q->_errno; + h_errno = q->_h_errno; - if (resolve->dead) { - errno = ECHILD; - return NULL; - } + return q->res_handler(q, q->ret, q->answer, q->userdata); +} - q = alloc_query(resolve); - if (!q) - return NULL; +_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { + assert_return(q, NULL); - req->dname_len = strlen(dname) + 1; + assert(q->n_ref >= 1); + q->n_ref++; - req->header.id = q->id; - req->header.type = q->type = qtype; - req->header.length = sizeof(ResRequest) + req->dname_len; + return q; +} - if (req->header.length > BUFSIZE) { - errno = ENOMEM; - goto fail; +static void resolve_freeaddrinfo(struct addrinfo *ai) { + while (ai) { + struct addrinfo *next = ai->ai_next; + + free(ai->ai_addr); + free(ai->ai_canonname); + free(ai); + ai = next; } +} - req->class = class; - req->type = type; +static void resolve_query_disconnect(sd_resolve_query *q) { + sd_resolve *resolve; + unsigned i; - strcpy((char*) req + sizeof(ResRequest), dname); + assert(q); - if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0) - goto fail; + if (!q->resolve) + return; - return q; - -fail: - if (q) - sd_resolve_cancel(resolve, q); + resolve = q->resolve; + assert(resolve->n_queries > 0); - return NULL; -} + if (q->done) { + assert(resolve->n_done > 0); + resolve->n_done--; + } -_public_ sd_resolve_query* sd_resolve_res_query(sd_resolve *resolve, const char *dname, int class, int type) { - return resolve_res(resolve, REQUEST_RES_QUERY, dname, class, type); -} + i = q->id % QUERIES_MAX; + assert(resolve->query_array[i] == q); + resolve->query_array[i] = NULL; + LIST_REMOVE(queries, resolve->queries, q); + resolve->n_queries--; -_public_ sd_resolve_query* sd_resolve_res_search(sd_resolve *resolve, const char *dname, int class, int type) { - return resolve_res(resolve, REQUEST_RES_SEARCH, dname, class, type); + q->resolve = NULL; + if (!q->floating) + sd_resolve_unref(resolve); } -_public_ int sd_resolve_res_done(sd_resolve *resolve, sd_resolve_query* q, unsigned char **answer) { - int ret; - assert(resolve); +static void resolve_query_free(sd_resolve_query *q) { assert(q); - assert(q->resolve == resolve); - assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH); - assert(answer); - - if (resolve->dead) { - errno = ECHILD; - return -ECHILD; - } - if (!q->done) { - errno = EAGAIN; - return -EAGAIN; - } + resolve_query_disconnect(q); - *answer = (unsigned char *)q->serv; - q->serv = NULL; + resolve_freeaddrinfo(q->addrinfo); + free(q->host); + free(q->serv); + free(q->answer); + free(q); +} - ret = q->ret; +_public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) { + if (!q) + return NULL; - if (ret < 0) { - errno = q->_errno; - h_errno = q->_h_errno; - } + assert(q->n_ref >= 1); + q->n_ref--; - sd_resolve_cancel(resolve, q); + if (q->n_ref <= 0) + resolve_query_free(q); - return ret < 0 ? -errno : ret; + return NULL; } -_public_ sd_resolve_query* sd_resolve_get_next(sd_resolve *resolve) { - assert(resolve); - return resolve->done_head; -} +_public_ int sd_resolve_query_is_done(sd_resolve_query *q) { + assert_return(q, -EINVAL); + assert_return(!resolve_pid_changed(q->resolve), -ECHILD); -_public_ int sd_resolve_get_n_queries(sd_resolve *resolve) { - assert(resolve); - return resolve->n_queries; + return q->done; } -_public_ void sd_resolve_cancel(sd_resolve *resolve, sd_resolve_query* q) { - int i; - int saved_errno = errno; +_public_ void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { + void *ret; - assert(resolve); - assert(q); - assert(q->resolve == resolve); - assert(resolve->n_queries > 0); + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); - if (q->done) { + ret = q->userdata; + q->userdata = userdata; - if (q->done_prev) - q->done_prev->done_next = q->done_next; - else - resolve->done_head = q->done_next; + return ret; +} - if (q->done_next) - q->done_next->done_prev = q->done_prev; - else - resolve->done_tail = q->done_prev; - } +_public_ void* sd_resolve_query_get_userdata(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); - i = q->id % MAX_QUERIES; - assert(resolve->queries[i] == q); - resolve->queries[i] = NULL; + return q->userdata; +} - sd_resolve_freeaddrinfo(q->addrinfo); - free(q->host); - free(q->serv); +_public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); - resolve->n_queries--; - free(q); + return q->resolve; +} - errno = saved_errno; +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_resolve *resolve = userdata; + int r; + + assert(resolve); + + r = sd_resolve_process(resolve); + if (r < 0) + return r; + + return 1; } -_public_ void sd_resolve_freeaddrinfo(struct addrinfo *ai) { - int saved_errno = errno; +_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int priority) { + int r; - while (ai) { - struct addrinfo *next = ai->ai_next; + assert_return(resolve, -EINVAL); + assert_return(!resolve->event, -EBUSY); - free(ai->ai_addr); - free(ai->ai_canonname); - free(ai); + assert(!resolve->event_source); - ai = next; + if (event) + resolve->event = sd_event_ref(event); + else { + r = sd_event_default(&resolve->event); + if (r < 0) + return r; } - errno = saved_errno; -} + r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); + if (r < 0) + goto fail; -_public_ int sd_resolve_isdone(sd_resolve *resolve, sd_resolve_query*q) { - assert(resolve); - assert(q); - assert(q->resolve == resolve); + r = sd_event_source_set_priority(resolve->event_source, priority); + if (r < 0) + goto fail; - return q->done; + return 0; + +fail: + sd_resolve_detach_event(resolve); + return r; } -_public_ void sd_resolve_setuserdata(sd_resolve *resolve, sd_resolve_query *q, void *userdata) { - assert(q); - assert(resolve); - assert(q->resolve = resolve); +_public_ int sd_resolve_detach_event(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); - q->userdata = userdata; + if (!resolve->event) + return 0; + + if (resolve->event_source) { + sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF); + resolve->event_source = sd_event_source_unref(resolve->event_source); + } + + resolve->event = sd_event_unref(resolve->event); + return 1; } -_public_ void* sd_resolve_getuserdata(sd_resolve *resolve, sd_resolve_query *q) { - assert(q); - assert(resolve); - assert(q->resolve = resolve); +_public_ sd_event *sd_resolve_get_event(sd_resolve *resolve) { + assert_return(resolve, NULL); - return q->userdata; + return resolve->event; }