The recent fix for handling large (about 16k) answers in unwind has
the downside that we are now always copying at least 16k per answer
between the resolver and frontend process.
That seems wasteful.

This re-arranges things to only copy what its needed.

Tests, OKs?

diff --git frontend.c frontend.c
index 8adbb07219b..74715087c5f 100644
--- frontend.c
+++ frontend.c
@@ -86,7 +86,8 @@ struct pending_query {
        int                              fd;
        int                              bogus;
        int                              rcode_override;
-       ssize_t                          answer_len;
+       int                              answer_len;
+       int                              received;
        uint8_t                         *answer;
 };
 
@@ -424,10 +425,7 @@ frontend_dispatch_resolver(int fd, short event, void *bula)
        struct imsgev                   *iev = bula;
        struct imsgbuf                  *ibuf = &iev->ibuf;
        struct imsg                      imsg;
-       struct query_imsg               *query_imsg;
-       struct answer_imsg              *answer_imsg;
        int                              n, shut = 0, chg;
-       uint8_t                         *p;
 
        if (event & EV_READ) {
                if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
@@ -449,45 +447,30 @@ frontend_dispatch_resolver(int fd, short event, void 
*bula)
                        break;
 
                switch (imsg.hdr.type) {
-               case IMSG_ANSWER_HEADER:
-                       if (IMSG_DATA_SIZE(imsg) != sizeof(*query_imsg))
-                               fatalx("%s: IMSG_ANSWER_HEADER wrong length: "
-                                   "%lu", __func__, IMSG_DATA_SIZE(imsg));
-                       query_imsg = (struct query_imsg *)imsg.data;
-                       if ((pq = find_pending_query(query_imsg->id)) ==
-                           NULL) {
-                               log_warnx("cannot find pending query %llu",
-                                   query_imsg->id);
-                               break;
-                       }
-                       if (query_imsg->err) {
-                               send_answer(pq);
-                               pq = NULL;
-                               break;
-                       }
-                       pq->bogus = query_imsg->bogus;
-                       break;
-               case IMSG_ANSWER:
-                       if (IMSG_DATA_SIZE(imsg) != sizeof(*answer_imsg))
+               case IMSG_ANSWER: {
+                       struct answer_header    *answer_header;
+                       int                      data_len;
+                       uint8_t                 *data;
+
+                       if (IMSG_DATA_SIZE(imsg) < sizeof(*answer_header))
                                fatalx("%s: IMSG_ANSWER wrong length: "
                                    "%lu", __func__, IMSG_DATA_SIZE(imsg));
-                       answer_imsg = (struct answer_imsg *)imsg.data;
-                       if ((pq = find_pending_query(answer_imsg->id)) ==
+                       answer_header = (struct answer_header *)imsg.data;
+                       data = (uint8_t *)imsg.data + sizeof(*answer_header);
+                       if (answer_header->answer_len > 65535)
+                               fatalx("%s: IMSG_ANSWER answer too big: %d",
+                                   __func__, answer_header->answer_len);
+                       data_len = IMSG_DATA_SIZE(imsg) -
+                           sizeof(*answer_header);
+
+                       if ((pq = find_pending_query(answer_header->id)) ==
                            NULL) {
-                               log_warnx("cannot find pending query %llu",
-                                   answer_imsg->id);
+                               log_warnx("%s: cannot find pending query %llu",
+                                   __func__, answer_header->id);
                                break;
                        }
 
-                       p = realloc(pq->answer, pq->answer_len +
-                           answer_imsg->len);
-
-                       if (p != NULL) {
-                               pq->answer = p;
-                               memcpy(pq->answer + pq->answer_len,
-                                   answer_imsg->answer, answer_imsg->len);
-                               pq->answer_len += answer_imsg->len;
-                       } else {
+                       if (answer_header->srvfail) {
                                free(pq->answer);
                                pq->answer_len = 0;
                                pq->answer = NULL;
@@ -495,9 +478,32 @@ frontend_dispatch_resolver(int fd, short event, void *bula)
                                send_answer(pq);
                                break;
                        }
-                       if (!answer_imsg->truncated)
+
+                       if (pq->answer == NULL) {
+                               pq->answer = malloc(answer_header->answer_len);
+                               if (pq->answer == NULL) {
+                                       pq->answer_len = 0;
+                                       pq->rcode_override =
+                                           LDNS_RCODE_SERVFAIL;
+                                       send_answer(pq);
+                                       break;
+                               }
+                               pq->answer_len = answer_header->answer_len;
+                               pq->received = 0;
+                               pq->bogus = answer_header->bogus;
+                       }
+
+                       if (pq->received + data_len > pq->answer_len)
+                               fatalx("%s: IMSG_ANSWER answer too big: %d",
+                                   __func__, data_len);
+
+                       memcpy(pq->answer + pq->received, data, data_len);
+                       pq->received += data_len;
+
+                       if (pq->received == pq->answer_len)
                                send_answer(pq);
                        break;
+               }
                case IMSG_CTL_RESOLVER_INFO:
                case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
                case IMSG_CTL_MEM_INFO:
diff --git resolver.c resolver.c
index fb34dee87d6..c3faba08faf 100644
--- resolver.c
+++ resolver.c
@@ -884,7 +884,7 @@ resolve_done(struct uw_resolver *res, void *arg, int rcode,
        sldns_buffer            *buf = NULL;
        struct regional         *region = NULL;
        struct query_imsg       *query_imsg;
-       struct answer_imsg       answer_imsg;
+       struct answer_header    *answer_header;
        struct running_query    *rq;
        struct timespec          tp, elapsed;
        int64_t                  ms;
@@ -894,12 +894,19 @@ resolve_done(struct uw_resolver *res, void *arg, int 
rcode,
        char                     rcode_buf[16];
        char                     qclass_buf[16];
        char                     qtype_buf[16];
-       uint8_t                 *p;
+       uint8_t                 *p, *data;
+       uint8_t                  answer_imsg[MAX_IMSGSIZE - IMSG_HEADER_SIZE];
 
        clock_gettime(CLOCK_MONOTONIC, &tp);
 
        query_imsg = (struct query_imsg *)arg;
 
+       answer_header = (struct answer_header *)answer_imsg;
+       data = answer_imsg + sizeof(*answer_header);
+       answer_header->id = query_imsg->id;
+       answer_header->srvfail = 0;
+       answer_header->answer_len = answer_len;
+
        timespecsub(&tp, &query_imsg->tp, &elapsed);
 
        ms = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
@@ -1004,43 +1011,33 @@ resolve_done(struct uw_resolver *res, void *arg, int 
rcode,
        if (result->rcode == LDNS_RCODE_SERVFAIL)
                goto servfail;
 
-       query_imsg->err = 0;
-
        if (sec == SECURE)
                res->state = VALIDATING;
 
        if (res->state == VALIDATING && sec == BOGUS) {
-               query_imsg->bogus = !force_acceptbogus;
-               if (query_imsg->bogus && why_bogus != NULL)
+               answer_header->bogus = !force_acceptbogus;
+               if (answer_header->bogus && why_bogus != NULL)
                        log_warnx("%s", why_bogus);
        } else
-               query_imsg->bogus = 0;
+               answer_header->bogus = 0;
 
-       if (resolver_imsg_compose_frontend(IMSG_ANSWER_HEADER, 0, query_imsg,
-           sizeof(*query_imsg)) == -1)
-               fatalx("IMSG_ANSWER_HEADER failed for \"%s %s %s\"",
-                   query_imsg->qname, qclass_buf, qtype_buf);
-
-       answer_imsg.id = query_imsg->id;
        p = answer_packet;
-       while ((size_t)answer_len > MAX_ANSWER_SIZE) {
-               answer_imsg.truncated = 1;
-               answer_imsg.len = MAX_ANSWER_SIZE;
-               memcpy(&answer_imsg.answer, p, MAX_ANSWER_SIZE);
-               if (resolver_imsg_compose_frontend(IMSG_ANSWER, 0, &answer_imsg,
-                   sizeof(answer_imsg)) == -1)
+       do {
+               int len;
+
+               if ((size_t)answer_len > sizeof(answer_imsg) -
+                   sizeof(*answer_header))
+                       len = sizeof(answer_imsg) - sizeof(*answer_header);
+               else
+                       len = answer_len;
+               memcpy(data, p, len);
+               if (resolver_imsg_compose_frontend(IMSG_ANSWER, 0,
+                   &answer_imsg, sizeof(*answer_header) + len) == -1)
                        fatalx("IMSG_ANSWER failed for \"%s %s %s\"",
                            query_imsg->qname, qclass_buf, qtype_buf);
-               p += MAX_ANSWER_SIZE;
-               answer_len -= MAX_ANSWER_SIZE;
-       }
-       answer_imsg.truncated = 0;
-       answer_imsg.len = answer_len;
-       memcpy(&answer_imsg.answer, p, answer_len);
-       if (resolver_imsg_compose_frontend(IMSG_ANSWER, 0, &answer_imsg,
-           sizeof(answer_imsg)) == -1)
-               fatalx("IMSG_ANSWER failed for \"%s %s %s\"",
-                   query_imsg->qname, qclass_buf, qtype_buf);
+               answer_len -= len;
+               p += len;
+       } while (answer_len > 0);
 
        TAILQ_REMOVE(&running_queries, rq, entry);
        evtimer_del(&rq->timer_ev);
@@ -1052,9 +1049,9 @@ resolve_done(struct uw_resolver *res, void *arg, int 
rcode,
        /* try_next_resolver() might free rq */
        if (try_next_resolver(rq) != 0 && running_res == 0) {
                /* we are the last one, send SERVFAIL */
-               query_imsg->err = -4; /* UB_SERVFAIL */
-               resolver_imsg_compose_frontend(IMSG_ANSWER_HEADER, 0,
-                   query_imsg, sizeof(*query_imsg));
+               answer_header->srvfail = 1;
+               resolver_imsg_compose_frontend(IMSG_ANSWER, 0,
+                   answer_imsg, sizeof(*answer_header));
        }
  out:
        free(query_imsg);
diff --git unwind.h unwind.h
index 9ebd8f47f87..161619cd34c 100644
--- unwind.h
+++ unwind.h
@@ -116,7 +116,6 @@ enum imsg_type {
        IMSG_SOCKET_IPC_FRONTEND,
        IMSG_SOCKET_IPC_RESOLVER,
        IMSG_QUERY,
-       IMSG_ANSWER_HEADER,
        IMSG_ANSWER,
        IMSG_CTL_RESOLVER_INFO,
        IMSG_CTL_AUTOCONF_RESOLVER_INFO,
@@ -170,18 +169,14 @@ struct query_imsg {
        char             qname[NI_MAXHOST];
        int              t;
        int              c;
-       int              err;
-       int              bogus;
        struct timespec  tp;
 };
 
-struct answer_imsg {
-#define        MAX_ANSWER_SIZE MAX_IMSGSIZE - IMSG_HEADER_SIZE - 
sizeof(uint64_t) - \
-                           2 * sizeof(int)
-       uint64_t         id;
-       int              truncated;
-       int              len;
-       uint8_t          answer[MAX_ANSWER_SIZE];
+struct answer_header {
+       uint64_t id;
+       int      srvfail;
+       int      bogus;
+       int      answer_len;
 };
 
 extern uint32_t         cmd_opts;


-- 
I'm not entirely sure you are real.

Reply via email to