diff options
Diffstat (limited to 'src/libsystemd/sd-resolve/sd-resolve.c')
-rw-r--r-- | src/libsystemd/sd-resolve/sd-resolve.c | 1019 |
1 files changed, 575 insertions, 444 deletions
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 464967b362..803190f02b 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -40,13 +40,17 @@ #include <stdint.h> #include <pthread.h> #include <sys/prctl.h> +#include <sys/poll.h> -#include "sd-resolve.h" #include "util.h" +#include "list.h" +#include "socket-util.h" +#include "sd-resolve.h" -#define MAX_WORKERS 16 -#define MAX_QUERIES 256 -#define BUFSIZE (10240) +#define WORKERS_MIN 1 +#define WORKERS_MAX 16 +#define QUERIES_MAX 256 +#define BUFSIZE 10240 typedef enum { REQUEST_ADDRINFO, @@ -61,40 +65,46 @@ 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]; + bool dead:1; + pid_t original_pid; - pthread_t workers[MAX_WORKERS]; - unsigned valid_workers; + int fds[_FD_MAX]; - unsigned current_id, current_index; - sd_resolve_query* queries[MAX_QUERIES]; + pthread_t workers[WORKERS_MAX]; + unsigned n_valid_workers; - sd_resolve_query *done_head, *done_tail; + unsigned current_id, current_index; + sd_resolve_query* queries[QUERIES_MAX]; + unsigned n_queries; - int n_queries; - int dead; + LIST_HEAD(sd_resolve_query, done); + sd_resolve_query *done_tail; + unsigned n_done; }; struct sd_resolve_query { sd_resolve *resolve; - int done; + bool done: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; + void *userdata; + + LIST_FIELDS(sd_resolve_query, done); }; typedef struct RHeader { @@ -105,7 +115,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 +145,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 { @@ -172,24 +182,28 @@ typedef union Packet { static int send_died(int out_fd) { RHeader rh = {}; - assert(out_fd > 0); + + assert(out_fd >= 0); rh.type = RESPONSE_DIED; - rh.id = 0; rh.length = sizeof(rh); - 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 +220,46 @@ 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; +static int send_addrinfo_reply( + int out_fd, + unsigned id, + int ret, + struct addrinfo *ai, + int _errno, + int _h_errno) { + + AddrInfoResponse resp = {}; + struct msghdr mh = {}; + struct iovec iov[2]; + union { + AddrInfoSerialization ais; + uint8_t space[BUFSIZE]; + } buffer; + assert(out_fd >= 0); - 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; + 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; 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 +267,121 @@ 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 = {}; + 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.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; - 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; + struct msghdr mh = {}; + struct iovec iov[2]; + ResResponse resp = {}; + 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.type = RESPONSE_RES; + resp.header.id = id; + resp.header.length = sizeof(ResResponse) + l; + resp.ret = ret; + resp._errno = _errno; + resp._h_errno = _h_errno; - assert(sizeof(data) >= resp->header.length); + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(ResResponse) }; + iov[1] = (struct iovec) { .iov_base = (void*) answer, .iov_len = l }; - if (ret > 0) - memcpy((uint8_t *)data + sizeof(ResResponse), answer, ret); + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); - return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL); + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + 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 +389,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 +413,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 +427,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 +450,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 +485,154 @@ 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 - resolve->n_done + extra; - if (n_proc > MAX_WORKERS) - n_proc = MAX_WORKERS; + if (n < WORKERS_MIN) + n = WORKERS_MIN; + if (n > WORKERS_MAX) + n = WORKERS_MAX; - resolve = new(sd_resolve, 1); - if (!resolve) { - errno = ENOMEM; - goto fail; + while (resolve->n_valid_workers < n) { + + 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; +} + +static bool resolve_pid_changed(sd_resolve *r) { + assert(r); + + /* We don't support people creating a resolver and keeping it + * around after fork(). Let's complain. */ + + return r->original_pid != getpid(); +} - for (i = 0; i < MESSAGE_FD_MAX; i++) +_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; + + for (i = 0; i < _FD_MAX; i++) resolve->fds[i] = -1; - memset(resolve->queries, 0, sizeof(resolve->queries)); + resolve->original_pid = getpid(); - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds); - if (r < 0) + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD); + if (r < 0) { + r = -errno; goto fail; + } - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds+2); - if (r < 0) + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_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; - } } - 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 NULL; + return r; } -_public_ void sd_resolve_free(sd_resolve *resolve) { - int i; - int saved_errno = errno; - unsigned p; +_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { + PROTECT_ERRNO; - assert(resolve); + unsigned i; - resolve->dead = 1; + assert_return(resolve, NULL); + assert_return(!resolve_pid_changed(resolve), NULL); + + resolve->dead = true; 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++) + for (i = 0; i < _FD_MAX; i++) if (resolve->fds[i] >= 0) - close(resolve->fds[i]); + close_nointr_nofail(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 < QUERIES_MAX && resolve->n_queries > 0; i++) + if (resolve->queries[i]) + sd_resolve_cancel(resolve->queries[i]); free(resolve); - - errno = saved_errno; + return NULL; } -_public_ int sd_resolve_fd(sd_resolve *resolve) { - assert(resolve); +_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->queries[id % QUERIES_MAX]; if (q) if (q->id == id) return q; @@ -529,52 +640,37 @@ 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 void complete_query(sd_resolve_query *q) { assert(q); assert(!q->done); - q->done = 1; - - if ((q->done_prev = resolve->done_tail)) - resolve->done_tail->done_next = q; - else - resolve->done_head = q; - - resolve->done_tail = q; - q->done_next = NULL; + q->done = true; + LIST_PREPEND(done, q->resolve->done, q); + q->resolve->n_done ++; } -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 +678,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 +715,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 +724,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 +737,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,7 +762,7 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len prev = ai; } - complete_query(resolve, q); + complete_query(q); break; } @@ -668,15 +776,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); + complete_query(q); break; } @@ -691,14 +809,15 @@ 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->serv = memdup((const char *)resp + sizeof(ResResponse), res_resp->ret); + if (!q->serv) { 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); + complete_query(q); break; } @@ -709,224 +828,245 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len 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) { + int n_processed = 0, r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); for (;;) { - Packet buf[BUFSIZE/sizeof(Packet) + 1]; ssize_t l; + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; - if (resolve->dead) { - errno = ECHILD; - return -1; + l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof(buf), 0); + if (l < 0) { + if (errno == EAGAIN) + return n_processed; + + return -errno; } + if (l == 0) + return -ECONNREFUSED; - l = recv(resolve->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0); - if (l < 0) { - fd_set fds; + r = handle_response(resolve, &buf.packet, (size_t) l); + if (r < 0) + return r; - if (errno != EAGAIN) - return -1; + n_processed++; + } +} - if (!block || handled) - return 0; +_public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { + int r; - FD_ZERO(&fds); - FD_SET(resolve->fds[RESPONSE_RECV_FD], &fds); + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - if (select(resolve->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0) - return -1; + if (resolve->n_queries <= 0) + return 0; - continue; - } + do { + r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); + } while (r == -EINTR); - if (handle_response(resolve, buf, (size_t) l) < 0) - return -1; + if (r < 0) + return r; - handled = 1; - } + return sd_resolve_process(resolve); } -static sd_resolve_query *alloc_query(sd_resolve *resolve) { +static int alloc_query(sd_resolve *resolve, 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; + + r = start_threads(resolve, 1); + if (r < 0) + return r; while (resolve->queries[resolve->current_index]) { resolve->current_index++; resolve->current_id++; - while (resolve->current_index >= MAX_QUERIES) - resolve->current_index -= MAX_QUERIES; + resolve->current_index %= QUERIES_MAX; } - q = resolve->queries[resolve->current_index] = new(sd_resolve_query, 1); - if (!q) { - errno = ENOMEM; - return NULL; - } + q = resolve->queries[resolve->current_index] = new0(sd_resolve_query, 1); + if (!q) + return -ENOMEM; resolve->n_queries++; q->resolve = resolve; - q->done = 0; 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; + + *_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, + const char *node, + const char *service, + const struct addrinfo *hints, + sd_resolve_query **_q) { + + 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(_q, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - q = alloc_query(resolve); - if (!q) - return NULL; + r = alloc_query(resolve, &q); + if (r < 0) + return r; - req->node_len = node ? strlen(node)+1 : 0; - req->service_len = service ? strlen(service)+1 : 0; + req.node_len = node ? strlen(node)+1 : 0; + req.service_len = service ? strlen(service)+1 : 0; - req->header.id = q->id; - req->header.type = q->type = REQUEST_ADDRINFO; - req->header.length = sizeof(AddrInfoRequest) + req->node_len + req->service_len; + req.header.id = q->id; + req.header.type = q->type = REQUEST_ADDRINFO; + req.header.length = sizeof(AddrInfoRequest) + req.node_len + req.service_len; - if (req->header.length > BUFSIZE) { - errno = ENOMEM; - goto fail; + 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; } - 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; - } + 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 }; - if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0) - goto fail; - - return q; + mh.msg_iov = iov; -fail: - if (q) - sd_resolve_cancel(resolve, q); + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_cancel(q); + return -errno; + } - return NULL; + *_q = q; + return 0; } -_public_ int sd_resolve_getaddrinfo_done(sd_resolve *resolve, sd_resolve_query* q, struct addrinfo **ret_res) { +_public_ int sd_resolve_getaddrinfo_done(sd_resolve_query* q, struct addrinfo **ret_res) { int ret; - assert(resolve); - assert(q); - assert(q->resolve == resolve); - assert(q->type == REQUEST_ADDRINFO); - if (resolve->dead) { - errno = ECHILD; + if (!q) { + errno = EINVAL; return EAI_SYSTEM; } + if (q->type != REQUEST_ADDRINFO) { + errno = ENOTTY; + return EAI_SYSTEM; + } + + if (resolve_pid_changed(q->resolve)) { + errno = ECHILD; + return EAI_SYSTEM; + } if (!q->done) return EAI_AGAIN; - *ret_res = q->addrinfo; - q->addrinfo = NULL; + if (ret_res) { + *ret_res = q->addrinfo; + q->addrinfo = NULL; + } ret = q->ret; - if (ret == EAI_SYSTEM) + if (ret != 0) { errno = q->_errno; - - if (ret != 0) h_errno = q->_h_errno; + } - sd_resolve_cancel(resolve, q); + sd_resolve_cancel(q); return ret; } -_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; - - assert(resolve); - assert(sa); - assert(salen > 0); +_public_ int sd_resolve_getnameinfo( + sd_resolve *resolve, + const struct sockaddr *sa, socklen_t salen, + int flags, + int gethost, int getserv, + sd_resolve_query**_q) { - if (resolve->dead) { - errno = ECHILD; - return NULL; - } - - q = alloc_query(resolve); - if (!q) - return NULL; + NameInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[2]; + sd_resolve_query *q; + int r; - req->header.id = q->id; - req->header.type = q->type = REQUEST_NAMEINFO; - req->header.length = sizeof(NameInfoRequest) + salen; + 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(_q, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - if (req->header.length > BUFSIZE) { - errno = ENOMEM; - goto fail; - } + r = alloc_query(resolve, &q); + if (r < 0) + return r; - req->flags = flags; - req->sockaddr_len = salen; - req->gethost = gethost; - req->getserv = getserv; + req.header.id = q->id; + req.header.type = q->type = REQUEST_NAMEINFO; + req.header.length = sizeof(NameInfoRequest) + salen; - memcpy((uint8_t*) req + sizeof(NameInfoRequest), sa, salen); + req.flags = flags; + req.sockaddr_len = salen; + req.gethost = !!gethost; + req.getserv = !!getserv; - if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0) - goto fail; + iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; + iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; - return q; + mh.msg_iov = iov; + mh.msg_iovlen = 2; -fail: - if (q) - sd_resolve_cancel(resolve, q); + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_cancel(q); + return -errno; + } - return NULL; + *_q = q; + return 0; } -_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) { +_public_ int sd_resolve_getnameinfo_done(sd_resolve_query* q, char **ret_host, char **ret_serv) { int ret; - assert(resolve); - assert(q); - assert(q->resolve == resolve); - assert(q->type == REQUEST_NAMEINFO); - assert(!ret_host || hostlen); - assert(!ret_serv || servlen); - if (resolve->dead) { + if (!q) { + errno = EINVAL; + return EAI_SYSTEM; + } + + if (q->type != REQUEST_NAMEINFO) { + errno = ENOTTY; + return EAI_SYSTEM; + } + + if (resolve_pid_changed(q->resolve)) { errno = ECHILD; return EAI_SYSTEM; } @@ -934,163 +1074,154 @@ _public_ int sd_resolve_getnameinfo_done(sd_resolve *resolve, sd_resolve_query* if (!q->done) return EAI_AGAIN; - if (ret_host && q->host) { - strncpy(ret_host, q->host, hostlen); - ret_host[hostlen-1] = 0; + if (ret_host) { + *ret_host = q->host; + q->host = NULL; } - if (ret_serv && q->serv) { - strncpy(ret_serv, q->serv, servlen); - ret_serv[servlen-1] = 0; + if (ret_serv) { + *ret_serv = q->serv; + q->serv = NULL; } ret = q->ret; - if (ret == EAI_SYSTEM) + if (ret != 0) { errno = q->_errno; - - if (ret != 0) h_errno = q->_h_errno; + } - sd_resolve_cancel(resolve, q); + sd_resolve_cancel(q); return ret; } -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 resolve_res( + sd_resolve *resolve, + QueryType qtype, + const char *dname, + int class, int type, + sd_resolve_query **_q) { - assert(resolve); - assert(dname); - - if (resolve->dead) { - errno = ECHILD; - return NULL; - } - - q = alloc_query(resolve); - if (!q) - return NULL; - - req->dname_len = strlen(dname) + 1; + struct msghdr mh = {}; + struct iovec iov[2]; + ResRequest req = {}; + sd_resolve_query *q; + int r; - req->header.id = q->id; - req->header.type = q->type = qtype; - req->header.length = sizeof(ResRequest) + req->dname_len; + assert_return(resolve, -EINVAL); + assert_return(dname, -EINVAL); + assert_return(_q, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); - if (req->header.length > BUFSIZE) { - errno = ENOMEM; - goto fail; - } + r = alloc_query(resolve, &q); + if (r < 0) + return r; - req->class = class; - req->type = type; + req.dname_len = strlen(dname) + 1; + req.class = class; + req.type = type; - strcpy((char*) req + sizeof(ResRequest), dname); + req.header.id = q->id; + req.header.type = q->type = qtype; + req.header.length = sizeof(ResRequest) + req.dname_len; - if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0) - goto fail; + iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(ResRequest) }; + iov[1] = (struct iovec) { .iov_base = (void*) dname, .iov_len = req.dname_len }; - return q; + mh.msg_iov = iov; + mh.msg_iovlen = 2; -fail: - if (q) - sd_resolve_cancel(resolve, q); + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_cancel(q); + return -errno; + } - return NULL; + *_q = q; + return 0; } -_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); +_public_ int sd_resolve_res_query(sd_resolve *resolve, const char *dname, int class, int type, sd_resolve_query** q) { + return resolve_res(resolve, REQUEST_RES_QUERY, dname, class, type, q); } -_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); +_public_ int sd_resolve_res_search(sd_resolve *resolve, const char *dname, int class, int type, sd_resolve_query** q) { + return resolve_res(resolve, REQUEST_RES_SEARCH, dname, class, type, q); } -_public_ int sd_resolve_res_done(sd_resolve *resolve, sd_resolve_query* q, unsigned char **answer) { +_public_ int sd_resolve_res_done(sd_resolve_query* q, unsigned char **answer) { int ret; - assert(resolve); - 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; - } + assert_return(q, -EINVAL); + assert_return(answer, -EINVAL); + assert_return(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH, -ENOTTY); + assert_return(!resolve_pid_changed(q->resolve), -ECHILD); - if (!q->done) { - errno = EAGAIN; + if (!q->done) return -EAGAIN; - } - *answer = (unsigned char *)q->serv; + *answer = (unsigned char *) q->serv; q->serv = NULL; ret = q->ret; - if (ret < 0) { + if (ret != 0) { errno = q->_errno; h_errno = q->_h_errno; } - sd_resolve_cancel(resolve, q); + sd_resolve_cancel(q); return ret < 0 ? -errno : ret; } -_public_ sd_resolve_query* sd_resolve_get_next(sd_resolve *resolve) { - assert(resolve); - return resolve->done_head; +_public_ int sd_resolve_get_next(sd_resolve *resolve, sd_resolve_query **q) { + assert_return(resolve, -EINVAL); + assert_return(q, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + *q = resolve->done; + return !!resolve->done; } _public_ int sd_resolve_get_n_queries(sd_resolve *resolve) { - assert(resolve); + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + return resolve->n_queries; } -_public_ void sd_resolve_cancel(sd_resolve *resolve, sd_resolve_query* q) { +_public_ int sd_resolve_cancel(sd_resolve_query* q) { + PROTECT_ERRNO; int i; - int saved_errno = errno; - assert(resolve); - assert(q); - assert(q->resolve == resolve); - assert(resolve->n_queries > 0); + if (!q) + return 0; - if (q->done) { + assert_return(!resolve_pid_changed(q->resolve), -ECHILD); - if (q->done_prev) - q->done_prev->done_next = q->done_next; - else - resolve->done_head = q->done_next; + assert(q->resolve); + assert(q->resolve->n_queries > 0); - if (q->done_next) - q->done_next->done_prev = q->done_prev; - else - resolve->done_tail = q->done_prev; + if (q->done) { + LIST_REMOVE(done, q->resolve->done, q); + q->resolve->n_done--; } - i = q->id % MAX_QUERIES; - assert(resolve->queries[i] == q); - resolve->queries[i] = NULL; + i = q->id % QUERIES_MAX; + assert(q->resolve->queries[i] == q); + q->resolve->queries[i] = NULL; + q->resolve->n_queries--; sd_resolve_freeaddrinfo(q->addrinfo); free(q->host); free(q->serv); - - resolve->n_queries--; free(q); - errno = saved_errno; + return 0; } _public_ void sd_resolve_freeaddrinfo(struct addrinfo *ai) { - int saved_errno = errno; while (ai) { struct addrinfo *next = ai->ai_next; @@ -1101,30 +1232,30 @@ _public_ void sd_resolve_freeaddrinfo(struct addrinfo *ai) { ai = next; } - - errno = saved_errno; } -_public_ int sd_resolve_isdone(sd_resolve *resolve, sd_resolve_query*q) { - assert(resolve); - assert(q); - assert(q->resolve == resolve); +_public_ int sd_resolve_is_done(sd_resolve_query *q) { + assert_return(q, -EINVAL); + assert_return(!resolve_pid_changed(q->resolve), -ECHILD); return q->done; } -_public_ void sd_resolve_setuserdata(sd_resolve *resolve, sd_resolve_query *q, void *userdata) { - assert(q); - assert(resolve); - assert(q->resolve = resolve); +_public_ void* sd_resolve_set_userdata(sd_resolve_query *q, void *userdata) { + void *ret; + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + ret = q->userdata; q->userdata = userdata; + + return ret; } -_public_ void* sd_resolve_getuserdata(sd_resolve *resolve, sd_resolve_query *q) { - assert(q); - assert(resolve); - assert(q->resolve = resolve); +_public_ void* sd_resolve_get_userdata(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); return q->userdata; } |